Repository navigation
fix(ci): stop Secrets gate false failures from baseline line drift (A-2059) #3630
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| #!/usr/bin/env python3 | ||
| """Accept detect-secrets exit 3 only for baseline line-number drift.""" | ||
|
|
||
| import json | ||
| import subprocess | ||
| from pathlib import Path | ||
|
|
||
|
|
||
| def without_scan_metadata(baseline: dict) -> dict: | ||
| """Keep every finding and scanner setting; ignore only scan bookkeeping.""" | ||
| normalized = {key: value for key, value in baseline.items() if key != "generated_at"} | ||
| normalized["results"] = { | ||
| path: [{key: value for key, value in finding.items() if key != "line_number"} for finding in findings] | ||
| for path, findings in baseline["results"].items() | ||
| } | ||
| return normalized | ||
|
|
||
|
|
||
| def main() -> int: | ||
| committed = json.loads( | ||
| subprocess.run( | ||
| ["git", "show", "HEAD:.secrets.baseline"], | ||
| check=True, | ||
| capture_output=True, | ||
| ).stdout | ||
| ) | ||
| updated = json.loads(Path(".secrets.baseline").read_text(encoding="utf-8")) | ||
| if without_scan_metadata(committed) == without_scan_metadata(updated): | ||
| print("::notice::detect-secrets baseline line numbers drifted; findings are unchanged") | ||
| return 0 | ||
| print("::error::detect-secrets baseline changed beyond line numbers or generated_at") | ||
| return 1 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| raise SystemExit(main()) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| """The exit-3 exception must never hide a changed secret finding.""" | ||
|
|
||
| import runpy | ||
| from pathlib import Path | ||
|
|
||
| without_scan_metadata = runpy.run_path( | ||
| str(Path(__file__).resolve().parents[2] / "scripts/check_secrets_baseline_drift.py") | ||
| )["without_scan_metadata"] | ||
|
|
||
|
|
||
| def test_line_number_and_generated_at_drift_only(): | ||
| original = { | ||
| "version": "1.5.0", | ||
| "plugins_used": [{"name": "KeywordDetector"}], | ||
| "results": { | ||
| "sample.py": [ | ||
| { | ||
| "type": "Secret Keyword", | ||
| "hashed_secret": "synthetic", # pragma: allowlist secret | ||
| "line_number": 1, | ||
| } | ||
| ] | ||
| }, | ||
| "generated_at": "before", | ||
| } | ||
| drifted = { | ||
| **original, | ||
| "generated_at": "after", | ||
| "results": {"sample.py": [{**original["results"]["sample.py"][0], "line_number": 2}]}, | ||
| } | ||
| assert without_scan_metadata(original) == without_scan_metadata(drifted) | ||
|
|
||
|
|
||
| def test_changed_finding_and_scanner_settings_fail_comparison(): | ||
| original = { | ||
| "results": { | ||
| "sample.py": [ | ||
| { | ||
| "hashed_secret": "synthetic", # pragma: allowlist secret | ||
| "line_number": 1, | ||
| } | ||
| ] | ||
| }, | ||
| "plugins_used": [{"name": "KeywordDetector"}], | ||
| } | ||
| changed_finding = { | ||
| **original, | ||
| "results": { | ||
| "sample.py": [ | ||
| { | ||
| "hashed_secret": "different", # pragma: allowlist secret | ||
| "line_number": 2, | ||
| } | ||
| ] | ||
| }, | ||
| } | ||
| added_finding = { | ||
| **original, | ||
| "results": { | ||
| "sample.py": original["results"]["sample.py"] | ||
| + [ | ||
| { | ||
| "hashed_secret": "added", # pragma: allowlist secret | ||
| "line_number": 3, | ||
| } | ||
| ] | ||
| }, | ||
| } | ||
| changed_scanner = {**original, "plugins_used": []} | ||
| for changed in (changed_finding, added_finding, changed_scanner): | ||
| assert without_scan_metadata(original) != without_scan_metadata(changed) | ||
|
Comment on lines
+34
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔎 Supported by static analysis
Add a focused The tests cover only
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compare findings independently of line-number order.
If two existing findings exchange positions in one file, detect-secrets writes them in the new line-number order. This function removes their line numbers but keeps the reversed list order. The checker then rejects a status-3 scan even though only locations changed. Sort the normalized findings by their remaining fields, while preserving duplicates, and add a test for this case. (raw.githubusercontent.com)