fix(api): resolve merge conflicts, add type safety for endpoint_url in S3 connection test

- Resolve merge conflicts with main (PR #834 also fixed S3 SSRF)
- Add isinstance(endpoint_url, str) type check before urlparse to prevent TypeError on non-string values
- Reject endpoint_url with empty/missing hostname after parsing (malformed URLs like 'https://')
- Keep scheme validation (http/https only) and private IP blocking via is_private_ip()
- Add logger.warning for SSRF block events
- Add regression tests: non-string endpoint_url and empty hostname cases
- Update sentinel.md with consolidated SSRF entry

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-27 14:48:52 +00:00
5 changed files with 106 additions and 17 deletions
+44 -1
View File
@@ -998,6 +998,23 @@ class TestConnectionTestEndpoint:
assert data["success"] is False
assert "Missing" in data["message"]
def test_test_imap_blocks_private_ip(self, int_client):
"""IMAP test with private IP returns failure (SSRF protection)."""
payload = {
"integration_type": "IMAP",
"config": {
"host": "127.0.0.1",
"port": 993,
"username": "user",
},
"credentials": {"password": "pass"},
}
resp = int_client.post("/api/integrations/test", json=payload)
assert resp.status_code == 200
data = resp.json()
assert data["success"] is False
assert "Invalid hostname or IP address" in data["message"]
def test_test_s3_missing_bucket(self, int_client):
"""S3 test with missing bucket returns failure."""
payload = {
@@ -1015,7 +1032,7 @@ class TestConnectionTestEndpoint:
"""S3 test blocks requests to private/internal IPs via endpoint_url (SSRF protection)."""
payload = {
"integration_type": "S3",
"config": {"bucket": "my-bucket", "endpoint_url": "http://127.0.0.1/s3"},
"config": {"bucket": "my-bucket", "endpoint_url": "http://127.0.0.1:9000"},
"credentials": {"access_key_id": "AKIA", "secret_access_key": "secret"},
}
resp = int_client.post("/api/integrations/test", json=payload)
@@ -1050,6 +1067,32 @@ class TestConnectionTestEndpoint:
assert data["success"] is False
assert "scheme" in data["message"].lower()
def test_test_s3_blocks_non_string_endpoint_url(self, int_client):
"""S3 test rejects non-string endpoint_url values gracefully (no 500)."""
payload = {
"integration_type": "S3",
"config": {"bucket": "my-bucket", "endpoint_url": 12345},
"credentials": {"access_key_id": "AKIA", "secret_access_key": "secret"},
}
resp = int_client.post("/api/integrations/test", json=payload)
assert resp.status_code == 200
data = resp.json()
assert data["success"] is False
assert "string" in data["message"].lower()
def test_test_s3_blocks_empty_hostname_endpoint_url(self, int_client):
"""S3 test rejects endpoint_url with no hostname (e.g. malformed URL)."""
payload = {
"integration_type": "S3",
"config": {"bucket": "my-bucket", "endpoint_url": "https://"},
"credentials": {"access_key_id": "AKIA", "secret_access_key": "secret"},
}
resp = int_client.post("/api/integrations/test", json=payload)
assert resp.status_code == 200
data = resp.json()
assert data["success"] is False
assert "hostname" in data["message"].lower()
def test_test_webdav_missing_url(self, int_client):
"""WebDAV test with missing URL returns failure."""
payload = {