diff --git a/CHANGELOG.md b/CHANGELOG.md index f27abbb..6b2a449 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Ignore loopback development URLs in data-leakage findings while retaining detection for non-loopback internal hosts (#194). - **ELICIT_PHISH matching** — client-capability `TRIGGERS` edges use `trust_boundary` layer; path enumeration terminates at dead-end capability nodes - **Validator hardening** — removed standalone `evidence.hop_count` and `evidence.path` proven-path bypasses; chain level matches associated graph path - **Auxiliary trust explicit parity** — vet/fuzz/inventory/readiness/pentest set `findings_trust_mode_explicit` when `--findings-trust-mode` is passed; API readiness adds `ignore_policy` + explicit flag diff --git a/src/mcts/analyzers/data_leakage.py b/src/mcts/analyzers/data_leakage.py index 6d72182..222b26e 100644 --- a/src/mcts/analyzers/data_leakage.py +++ b/src/mcts/analyzers/data_leakage.py @@ -27,7 +27,7 @@ ("Database URL", re.compile(r"(?i)(postgres|mysql|mongodb)://\S+"), Severity.HIGH), ( "Internal URL", - re.compile(r"https?://(?:localhost|127\.0\.0\.1|internal|\.local)\S*"), + re.compile(r"https?://(?:internal|\.local)\S*", re.IGNORECASE), Severity.MEDIUM, ), ] diff --git a/tests/test_analyzers.py b/tests/test_analyzers.py index 8571000..8e83e55 100644 --- a/tests/test_analyzers.py +++ b/tests/test_analyzers.py @@ -29,7 +29,7 @@ def test_data_leakage_scans_source_files(example_server_path: Path) -> None: assert source_findings or any(f.analyzer == "data_leakage" for f in report.findings) -def test_data_leakage_ignores_loopback_urls_in_log_messages() -> None: +def test_data_leakage_ignores_loopback_urls() -> None: server = MCPServerInfo( name="perseus", source_files={ @@ -46,9 +46,31 @@ def test_data_leakage_ignores_loopback_urls_in_log_messages() -> None: findings = DataLeakageAnalyzer().analyze(server) - assert len(findings) == 1 - assert findings[0].location - assert findings[0].location.line == 4 + assert not findings + + +def test_data_leakage_flags_non_loopback_internal_urls() -> None: + server = MCPServerInfo( + name="sso-config", + source_files={ + "config.py": "\n".join( + [ + "SSO_ENDPOINTS = {", + " 'local': 'http://localhost:8080/sso',", + " 'loopback': 'http://127.0.0.1:8080/sso',", + " 'stage': 'https://stage.example.com/sso',", + "}", + "PROD_SSO = 'https://internal.example.com/sso'", + "SERVICE_SSO = 'http://internal-service:8080/sso'", + ] + ) + }, + ) + + findings = DataLeakageAnalyzer().analyze(server) + + assert len(findings) == 2 + assert [finding.location.line if finding.location else None for finding in findings] == [6, 7] def test_docker_dedupe_dockerfile_and_containerfile(tmp_path: Path) -> None: