From 841cb8fa7e51229a408d798b759d9963579b4b6d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 27 Sep 2026 19:41:55 +0900 Subject: [PATCH] fix(strix): resolve evidence binder from trusted gate source --- scripts/ci/strix_quick_gate.sh | 2 +- ...test_strix_evidence_binder_trusted_path.py | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 tests/test_strix_evidence_binder_trusted_path.py diff --git a/scripts/ci/strix_quick_gate.sh b/scripts/ci/strix_quick_gate.sh index c7d3667465..9eed5e2e71 100755 --- a/scripts/ci/strix_quick_gate.sh +++ b/scripts/ci/strix_quick_gate.sh @@ -243,7 +243,7 @@ PY sanitize_remediation_evidence_claims() { local log_file="$1" local report_root="$2" - local binder="$REPO_ROOT/scripts/ci/strix_evidence_binding.py" + local binder="$SCRIPT_DIR/strix_evidence_binding.py" local report_file if [ ! -f "$binder" ] || [ -L "$binder" ]; then diff --git a/tests/test_strix_evidence_binder_trusted_path.py b/tests/test_strix_evidence_binder_trusted_path.py new file mode 100644 index 0000000000..308bbda0b7 --- /dev/null +++ b/tests/test_strix_evidence_binder_trusted_path.py @@ -0,0 +1,59 @@ +"""The Strix evidence binder resolves from the trusted gate, not the scanned repo.""" + +from __future__ import annotations + +import re +import subprocess +from pathlib import Path + +GATE = Path("scripts/ci/strix_quick_gate.sh") + + +def _function_source(name: str) -> str: + """Return one top-level bash function from the gate script.""" + text = GATE.read_text(encoding="utf-8") + match = re.search(rf"(?ms)^{name}\(\) \{{\n.*?^\}}\n", text) + assert match is not None, name + return match.group(0) + + +def test_binder_runs_when_the_scanned_repository_has_no_central_scripts(tmp_path: Path) -> None: + """A consumer repo need not contain the central Strix binder. + + ``strix.yml`` runs the trusted ``.github`` gate with ``STRIX_REPO_ROOT`` set + to the consumer checkout. The evidence binder must therefore resolve next + to the trusted gate rather than under the scanned repository root. + """ + consumer = tmp_path / "trusted-workspace" + consumer.mkdir() + reports = tmp_path / "strix_runs" + reports.mkdir() + report = reports / "penetration_test_report.md" + report.write_text("# Report\n\nNo vulnerabilities were identified.\n", encoding="utf-8") + log = tmp_path / "strix.log" + log.write_text("scan complete\n", encoding="utf-8") + script = ( + "set -u\n" + f'SCRIPT_DIR="{GATE.parent.resolve()}"\n' + f'REPO_ROOT="{consumer}"\n' + + _function_source("sanitize_remediation_evidence_claims") + + f'sanitize_remediation_evidence_claims "{log}" "{reports}"\n' + ) + result = subprocess.run(["bash", "-c", script], capture_output=True, text=True, check=False) + assert result.returncode == 0, result.stderr + assert "binder is missing" not in result.stderr + assert report.read_text(encoding="utf-8").startswith("# Report") + + +def test_binder_still_fails_closed_when_the_trusted_copy_is_absent(tmp_path: Path) -> None: + """Without the trusted binder the gate still refuses to continue.""" + script = ( + "set -u\n" + f'SCRIPT_DIR="{tmp_path}"\n' + f'REPO_ROOT="{GATE.parent.parent.resolve()}"\n' + + _function_source("sanitize_remediation_evidence_claims") + + 'sanitize_remediation_evidence_claims "" ""\n' + ) + result = subprocess.run(["bash", "-c", script], capture_output=True, text=True, check=False) + assert result.returncode == 2 + assert f"Strix evidence binder is missing: {tmp_path}/strix_evidence_binding.py" in result.stderr