ci: match the fixture allowlist on the secret, not the whole line - #72
Merged
Conversation
The line-target regex was wider than the comment claimed. Two ways it could suppress findings it should not: 1. Same-line co-occurrence -- a second, unrelated credential sharing the lane_token line would also be suppressed, because the allowlist regex is tested against the entire line and still matches. 2. Reformatting -- json.dumps without indent, or any compacting rewrite, collapses this fixture to a single line that contains the literal. A line-target regex would then match on every finding in the file and silently exempt the whole fixture. Match the anchored literal against the secret instead (gitleaks' default target). A finding is suppressed only when the detected secret IS exactly this value, so layout cannot widen the exemption and no other secret -- same line or not -- can be caught by it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #68. The allowlist it added was wider than its own comment claimed, and this narrows it.
#68 used a line-target regex:
and asserted "any other secret added to that fixture is still reported." That is false.
detect.gosetsglobalAllowlistTarget = currentLinewhenRegexTarget == "line", and the regex is an unanchored substring match, so two things leak through:lane_tokenline is suppressed too, because that line still contains the fixture literal.json.dumpswithoutindent, or any compacting rewrite, collapses this fixture to a single line containing the literal. A line-target regex would then match for every finding in the file and silently exempt the whole fixture. This is the serious one: it converts a one-value exemption into a whole-file blind spot on a routine reformat, with no signal.The fix
Match the anchored literal against the secret (gitleaks' default target when
regexTargetis omitted):Per
config/allowlist.go,RegexAllowed()testsfinding.SecretwhenRegexTargetis empty. Anchored^…$exempts a finding only when the detected secret is exactly this literal, so layout cannot widen it and no other secret — same line or not — can be caught by it.Honest scope note: this is not strictly narrower
It is narrower on co-occurrence and on layout, but marginally wider on location: the exact secret is now exempt anywhere in history rather than only on a
"lane_token": …line. That residual is theoretical for a fixed 128-bit synthetic value — an unrelated real credential would have to equal it exactly, and any occurrence of this value is by construction the same non-credential fixture token.A rule-level
[[rules.allowlists]]withcondition = "AND"pluspathswould close even that. I deliberately did not do it: it requires redefining the extendedgeneric-api-keyrule and relying on allowlist-merge semantics, and the failure mode of getting that wrong is a silently disabled exemption — precisely the bug class that caused this whole thread (#68's first attempt used top-level[[allowlists]], which gitleaks drops without warning). Not worth the risk to close a theoretical gap. Happy to add it if you'd rather have belt-and-suspenders.Verification
workflow_dispatchof Secret scan on this branch — run 30339346924, same full-historygitleaks detect, 41 commits scanned, no leaks found, both jobs green. This also empirically proves gitleaks'Secretfor this finding is the bare token: if it weren't, the anchored regex would not match and the scan would have failed with 3 leaks.python3 scripts/build_skills.py --check— PASSpython3 scripts/build_marketplace.py --check— PASSpython3 scripts/build-changelog.py --dry-run— PASS (the gate CI applies to fragment-only PRs)python3 scripts/check_release_consistency.py— PASSpython3 scripts/secret_scan.py— PASSpython3 scripts/check-public-export-safety.py --active-tree— SAFEpython3 -m unittest discover -s tests -t .— PASS (Ran 588 tests, OK)python3 -m unittest discover -s scripts -p 'test_*.py'— PASS (Ran 254 tests, OK)git diff --check— cleanbuild-changelog.py --checkfails, but it fails identically on pristineorigin/main(verified in a clean worktree) — that is the expected steady state of the fragment convention, where fragments accumulate andCHANGELOG.mdcompiles at release time. This PR does not touchCHANGELOG.md, so CI applies--dry-run, which passes.Boundary declaration
Generated and release surfaces
SKILL.mdfiles are in parity.changelog.d/fragment: CI-tooling fix, no user-visible behavior change.CHANGELOG.mduntouched.Review and post-condition
Tier 3 — narrows the coverage of a credential-scanning gate on a public repository. Not an operator-reserved path.
Independent cross-family review — Grok (xAI), distinct from the Anthropic-family author:
Concern 1 is integrated as the "Honest scope note" section above rather than silently dropped, with the reason the optional tightener was declined.
Post-condition: confirm the next scheduled Secret scan on
mainis green, or dispatch it manually onmainafter merge.Compliance trace
author: claude
standing_directives: public boundary declaration; independent cross-family review with the reviewer's concern integrated verbatim rather than dropped; full verification-gate run with pre-existing failures distinguished from introduced ones
tier: 3
cross_check: PROCEED — Grok (xAI) governance review, APPROVE, confidence H, 1 concern (location residual) integrated into the PR body; distinct family from the Anthropic-family author
post_condition: next scheduled or manually dispatched Secret scan on main is green
mcp_coverage_gap: NONE
contributor_rights: OWNER-AUTHORED
operator_reserved: no