From d139e3bd4ea8f00fa11750405898915a0cc5d268 Mon Sep 17 00:00:00 2001 From: Divyam Talwar Date: Thu, 26 Mar 2026 14:45:18 +0530 Subject: [PATCH 1/2] fix(core): secure secret exclusions, language states, and memory bounds 1. Security: Remove test/config files from security detector exclusion list to prevent leaked secrets. 2. State: Make plan file paths language-specific (e.g., plan-python.json) to prevent multi-language run collisions. 3. Cache: Add LRU eviction policy to FileTextCache to prevent OOM errors on large repos. --- core/runtime_state.py | 13 +++++++++++-- engine/_plan/persistence.py | 4 ++++ engine/detectors/security/filters.py | 2 +- tests/detectors/test_security_internals.py | 18 +++++++++--------- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/core/runtime_state.py b/core/runtime_state.py index 137d1a2..a325a1e 100644 --- a/core/runtime_state.py +++ b/core/runtime_state.py @@ -11,9 +11,10 @@ class FileTextCache: """Optional read-through file-text cache used by scan/review passes.""" - def __init__(self) -> None: + def __init__(self, *, max_entries: int = 256) -> None: self._enabled = False self._values: dict[str, str | None] = {} + self.max_entries = max_entries def enable(self) -> None: self._enabled = True @@ -25,14 +26,22 @@ def disable(self) -> None: def read(self, filepath: str) -> str | None: if self._enabled and filepath in self._values: - return self._values[filepath] + # Refresh position for LRU-like behavior + content = self._values.pop(filepath) + self._values[filepath] = content + return content try: content = Path(filepath).read_text(errors="replace") except OSError: content = None + if self._enabled: + # Enforce bounds + if len(self._values) >= self.max_entries: + self._values.pop(next(iter(self._values))) self._values[filepath] = content + return content diff --git a/engine/_plan/persistence.py b/engine/_plan/persistence.py index b152304..367ae7e 100644 --- a/engine/_plan/persistence.py +++ b/engine/_plan/persistence.py @@ -86,6 +86,10 @@ def save_plan(plan: PlanModel | dict, path: Path | None = None) -> None: def plan_path_for_state(state_path: Path) -> Path: """Derive plan.json path from a state file path.""" + name = state_path.name + if name.startswith("state-") and name.endswith(".json"): + lang_part = name[len("state-") : -len(".json")] + return state_path.parent / f"plan-{lang_part}.json" return state_path.parent / "plan.json" diff --git a/engine/detectors/security/filters.py b/engine/detectors/security/filters.py index 2d434c8..c6109ba 100644 --- a/engine/detectors/security/filters.py +++ b/engine/detectors/security/filters.py @@ -10,7 +10,7 @@ ) from engine.policy.zones import FileZoneMap, Zone -_EXCLUDED_SECURITY_ZONES = (Zone.TEST, Zone.CONFIG, Zone.GENERATED, Zone.VENDOR) +_EXCLUDED_SECURITY_ZONES = (Zone.GENERATED, Zone.VENDOR) def _should_scan_file(filepath: str, zone_map: FileZoneMap | None) -> bool: diff --git a/tests/detectors/test_security_internals.py b/tests/detectors/test_security_internals.py index 6aa6e5c..1f35755 100644 --- a/tests/detectors/test_security_internals.py +++ b/tests/detectors/test_security_internals.py @@ -38,10 +38,10 @@ def test_should_scan_file_production_zone(): assert _should_scan_file("/app/main.py", zm) is True -def test_should_scan_file_test_zone_excluded(): - """Test files are excluded from security scanning.""" +def test_should_scan_file_test_zone_allowed(): + """Test files are included in security scanning.""" zm = _make_zone_map([("/tests/test_foo.py", Zone.TEST)]) - assert _should_scan_file("/tests/test_foo.py", zm) is False + assert _should_scan_file("/tests/test_foo.py", zm) is True def test_should_scan_file_vendor_zone_excluded(): @@ -56,10 +56,10 @@ def test_should_scan_file_generated_zone_excluded(): assert _should_scan_file("/generated/schema.py", zm) is False -def test_should_scan_file_config_zone_excluded(): - """Config files are excluded from security scanning.""" +def test_should_scan_file_config_zone_allowed(): + """Config files are included in security scanning.""" zm = _make_zone_map([("/config.py", Zone.CONFIG)]) - assert _should_scan_file("/config.py", zm) is False + assert _should_scan_file("/config.py", zm) is True def test_should_scan_file_script_zone_allowed(): @@ -122,8 +122,8 @@ def test_should_skip_line_star_comment(): def test_excluded_security_zones_membership(): """Verify the exact membership of excluded zones.""" - assert Zone.TEST in _EXCLUDED_SECURITY_ZONES - assert Zone.CONFIG in _EXCLUDED_SECURITY_ZONES + assert Zone.TEST not in _EXCLUDED_SECURITY_ZONES + assert Zone.CONFIG not in _EXCLUDED_SECURITY_ZONES assert Zone.GENERATED in _EXCLUDED_SECURITY_ZONES assert Zone.VENDOR in _EXCLUDED_SECURITY_ZONES assert Zone.PRODUCTION not in _EXCLUDED_SECURITY_ZONES @@ -191,7 +191,7 @@ def test_detect_security_issues_skips_excluded_zone(tmp_path): """Files in excluded zones are not scanned.""" f = tmp_path / "test_creds.py" f.write_text('aws_key = "AKIAIOSFODNN7EXAMPLE"\n') - zm = _make_zone_map([(str(f), Zone.TEST)]) + zm = _make_zone_map([(str(f), Zone.GENERATED)]) entries, scanned = detect_security_issues([str(f)], zm, "python") assert scanned == 0 assert entries == [] From 1030aef8b7a70289fac1e0ea2028d3f84ef6097a Mon Sep 17 00:00:00 2001 From: Divyam Talwar Date: Thu, 26 Mar 2026 14:50:20 +0530 Subject: [PATCH 2/2] fix: update legacy tests for new security scan rules --- tests/detectors/security/test_security.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/detectors/security/test_security.py b/tests/detectors/security/test_security.py index 1ee7b5e..bbe546e 100644 --- a/tests/detectors/security/test_security.py +++ b/tests/detectors/security/test_security.py @@ -185,14 +185,14 @@ def test_hardcoded_secret_short_value_ok(self): finally: os.unlink(path) - def test_hardcoded_secret_in_test_zone_skipped(self): + def test_hardcoded_secret_in_test_zone_allowed(self): content = 'password = "test_secret_value123"' path = _write_temp_file(content) try: zm = _make_zone_map([path], Zone.TEST) entries, scanned = detect_security_issues([path], zm, "python") - assert entries == [] - assert scanned == 0 + assert len(entries) > 0 + assert scanned == 1 finally: os.unlink(path) @@ -322,25 +322,25 @@ def test_vendor_zone_skipped(self): finally: os.unlink(path) - def test_test_zone_skipped(self): + def test_test_zone_allowed(self): content = 'AWS_KEY = "AKIAIOSFODNN7EXAMPLE"' path = _write_temp_file(content) try: zm = _make_zone_map([path], Zone.TEST) entries, scanned = detect_security_issues([path], zm, "python") - assert len(entries) == 0 - assert scanned == 0 + assert len(entries) > 0 + assert scanned == 1 finally: os.unlink(path) - def test_config_zone_skipped(self): + def test_config_zone_allowed(self): content = 'DB_URL = "postgres://admin:s3cret@localhost:5432/mydb"' path = _write_temp_file(content) try: zm = _make_zone_map([path], Zone.CONFIG) entries, scanned = detect_security_issues([path], zm, "python") - assert len(entries) == 0 - assert scanned == 0 + assert len(entries) > 0 + assert scanned == 1 finally: os.unlink(path)