diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 1db81e9..29980fa 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -18,6 +18,13 @@ jobs: - uses: actions/setup-python@v6 with: python-version: "3.12" + - name: Install Python packaging tools + run: | + set -euo pipefail + python3 -m pip install --upgrade pip + python3 -m pip install build -e ./python + - name: Verify Python dependencies + run: python3 -m pip check - name: Check whitespace run: | set -euo pipefail @@ -33,6 +40,8 @@ jobs: run: python3 python/scripts/runtime_settings.py validate - name: Run Python unit tests run: python3 -m unittest discover -s python/tests -v + - name: Build Python package + run: python3 -m build ./python - name: Checkout internal dependency consumer repos env: GH_TOKEN: ${{ github.token }} diff --git a/python/scripts/gate_codex_app_review.py b/python/scripts/gate_codex_app_review.py index 4b71e9f..84e80da 100644 --- a/python/scripts/gate_codex_app_review.py +++ b/python/scripts/gate_codex_app_review.py @@ -100,7 +100,7 @@ def compile_patterns(policy: dict[str, Any]) -> list[re.Pattern[str]]: # ─── static guard ──────────────────────────────────────────────────────────── _SENSITIVE = re.compile( - r'(?:api[_\s]?key|secret|password|token|credential|private[_\s]?key)\s*[:=]\s*["\']' + r'(?Papi[_\s]?key|secret|password|token|credential|private[_\s]?key)\s*[:=]\s*["\']' r'(?!\$\{\{|{{|example|placeholder|test|your[-_\s]|xxx|TODO|CHANGEME)[^"\']{12,}["\']', re.IGNORECASE, ) @@ -125,7 +125,7 @@ def scan_diff(diff_text: str, path_patterns: list[re.Pattern[str]]) -> list[str] continue m = _SENSITIVE.search(line[1:]) if m: - violations.append(f"**Hardcoded secret** in `{current}`: `{m.group(0)[:100]}`") + violations.append(f"**Hardcoded secret** in `{current}`: `{m.group('field')}=`") return list(dict.fromkeys(violations)) diff --git a/python/tests/test_gate_codex_app_review.py b/python/tests/test_gate_codex_app_review.py new file mode 100644 index 0000000..d530067 --- /dev/null +++ b/python/tests/test_gate_codex_app_review.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +import importlib.util +import sys +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +MODULE_PATH = ROOT / "python" / "scripts" / "gate_codex_app_review.py" +SPEC = importlib.util.spec_from_file_location("gate_codex_app_review", MODULE_PATH) +gate_codex_app_review = importlib.util.module_from_spec(SPEC) +assert SPEC.loader is not None +sys.modules[SPEC.name] = gate_codex_app_review +SPEC.loader.exec_module(gate_codex_app_review) + + +class GateCodexAppReviewTest(unittest.TestCase): + def test_scan_diff_redacts_secret_values(self): + diff = "\n".join( + [ + "diff --git a/example.py b/example.py", + "+++ b/example.py", + '+api_key = "sk-' 'live-12345678901234567890"', + ] + ) + + violations = gate_codex_app_review.scan_diff(diff, []) + + self.assertEqual(len(violations), 1) + self.assertIn("api_key=", violations[0]) + self.assertNotIn("sk-live-12345678901234567890", violations[0]) + + +if __name__ == "__main__": + unittest.main()