diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 9c61dd4..e8143f7 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -12,6 +12,7 @@ | workplan | RMGR-WP-ADHOC-2026-09-09 | finished | — | workplans/ADHOC-2026-09-09.md | | workplan | RMGR-WP-ADHOC-2026-09-11 | finished | — | workplans/ADHOC-2026-09-11.md | | workplan | RMGR-WP-ADHOC-2026-10-03 | finished | — | workplans/ADHOC-2026-10-03.md | +| workplan | RMGR-WP-ADHOC-2026-10-27 | finished | — | workplans/ADHOC-2026-10-27.md | | workplan | RMGR-WP-0001 | finished | — | workplans/RMGR-WP-0001-foundation.md | | workplan | RMGR-WP-0002 | finished | — | workplans/RMGR-WP-0002-retirement-dual-run-strangler.md | | workplan | RMGR-WP-0003 | finished | — | workplans/RMGR-WP-0003-production-pilot-dual-run.md | @@ -37,6 +38,7 @@ | task | RMGR-WP-ADHOC-2026-09-09-T01 | done | — | workplans/ADHOC-2026-09-09.md | | task | RMGR-WP-ADHOC-2026-09-11-T01 | done | — | workplans/ADHOC-2026-09-11.md | | task | RMGR-WP-ADHOC-2026-10-03-T01 | done | — | workplans/ADHOC-2026-10-03.md | +| task | RMGR-WP-ADHOC-2026-10-27-T01 | done | — | workplans/ADHOC-2026-10-27.md | | task | RMGR-WP-0001-T01 | done | — | workplans/RMGR-WP-0001-foundation.md | | task | RMGR-WP-0001-T02 | done | — | workplans/RMGR-WP-0001-foundation.md | | task | RMGR-WP-0001-T03 | done | — | workplans/RMGR-WP-0001-foundation.md | diff --git a/docs/secret-detection.md b/docs/secret-detection.md index 690efcb..7020d75 100644 --- a/docs/secret-detection.md +++ b/docs/secret-detection.md @@ -49,9 +49,14 @@ external target. Changed gitlinks, conflicts and unreadable entries are refused. A reviewed configuration extending the scanner's built-in defaults is supplied from an isolated temporary directory. Repository `.gitleaks.toml`, ignore files, inline `gitleaks:allow` comments and ambient scanner environment do not suppress -this check. There is no implicit baseline or exception switch. False positives -need a reviewed follow-up mechanism with exact scope and real approval; do not -silently add broad path exclusions. +this check. There is no implicit baseline or caller-controlled exception switch. +The supplied profile adds one reviewed `generic-api-key` match allowlist: a +filename with a normal text extension (`.md`, `.markdown`, `.txt`, `.yaml`, +`.yml`, `.json`, `.py`, or `.toml`) whose value is exactly 64 lowercase hex +digits. A bare keyword assignment of that same digest still blocks. Keep this +profile identical to the forge receive adapter. Any wider exception needs a +reviewed follow-up with exact scope and real approval; do not silently add +broad path exclusions. The current resource bounds are 500 distinct blobs, 8 MiB per blob and 64 MiB in total; exceeding a bound refuses instead of skipping. Individual Git and scanner diff --git a/src/repo_manager/secret_scan.py b/src/repo_manager/secret_scan.py index c2203bd..3c1f4f8 100644 --- a/src/repo_manager/secret_scan.py +++ b/src/repo_manager/secret_scan.py @@ -43,7 +43,20 @@ MAX_BATCH_BYTES = 8 * 1024 * 1024 DIRECTORY_BLOB_BYTES = 4096 MAX_REPORT_BYTES = 1024 * 1024 SCAN_SECONDS = 120 -CONFIG = '[extend]\nuseDefault = true\n' +# Keep this profile identical to railiance-forge +# tools/receive-secret-scan scannerConfig. The match allowlist is one reviewed +# shape: a text-extension filename whose value is exactly 64 lowercase hex +# digits. A bare keyword assignment of the same digest still matches +# generic-api-key. +CONFIG = r"""[extend] +useDefault = true + +[[allowlists]] +description = "Filename key whose value is a lowercase 64-hex content digest" +targetRules = ["generic-api-key"] +regexTarget = "match" +regexes = ['^[\w.-]+\.(?:md|markdown|txt|yaml|yml|json|py|toml):[ \t]+[a-f0-9]{64}$'] +""" class Unverified(Exception): diff --git a/tests/test_secret_scan.py b/tests/test_secret_scan.py index d3555f9..dfafc69 100644 --- a/tests/test_secret_scan.py +++ b/tests/test_secret_scan.py @@ -50,6 +50,15 @@ def stage(repo, data, name='application.txt'): git(repo, 'add', '--', name) +def test_filename_digest_is_allowed_and_bare_assignment_stays_blocked(binary, repo): + digest = os.urandom(32).hex() + stage(repo, f" cards/access.md: {digest}\n".encode(), "release.yaml") + assert secret_scan.scan_staged(repo, binary)["status"] == "clean" + git(repo, "rm", "-q", "--cached", "release.yaml") + stage(repo, f"access: {digest}\n".encode(), "assignment.txt") + assert secret_scan.scan_staged(repo, binary)["status"] == "blocked" + + def test_clean_staged_blob(binary, repo): stage(repo, b'print("hello")\n') result = secret_scan.scan_staged(repo, binary) diff --git a/workplans/ADHOC-2026-10-27.md b/workplans/ADHOC-2026-10-27.md new file mode 100644 index 0000000..4e558ec --- /dev/null +++ b/workplans/ADHOC-2026-10-27.md @@ -0,0 +1,26 @@ +--- +id: RMGR-WP-ADHOC-2026-10-27 +type: workplan +title: "Allow filename content digests in the staged secret scan" +domain: infotech +repo: repo-manager +status: finished +owner: grok +created: "2026-10-27" +updated: "2026-10-27" +--- + +## Keep the staged scanner profile aligned with the forge + +```task +id: RMGR-WP-ADHOC-2026-10-27-T01 +status: done +priority: high +``` + +`secret_scan.CONFIG` now uses the same reviewed `generic-api-key` match +allowlist as `railiance-forge` `scannerConfig`: a normal text-extension +filename whose value is exactly 64 lowercase hex digits. A bare keyword +assignment of that digest still blocks. This checkout applies the profile on +the next staged scan. The installed Forgejo adapter does not; that admission +remains `FORGE-WP-ADHOC-2026-10-27-T02`.