Summary
The hygiene job's secret scan is failing on open PRs for a finding that belongs to a
branch those PRs do not contain. As of this writing that is every PR CI runs, plus main's
next push. The finding itself is a false positive.
Reproduction
.github/workflows/ci.yml:137-144 runs the scan with scan-mode: git. In that mode
gitleaks walks the commits reachable from all refs in the clone, not the checked-out
ref. actions/checkout with fetch-depth: 0 fetches every branch, so each PR's scan
covers unrelated in-flight branches.
Reproduced locally against gitleaks 8.30.1, the version CI installs:
# only origin/main plus the PR branch present
$ gitleaks git --config .gitleaks.toml --redact
INF 1977 commits scanned.
INF no leaks found
# after git fetch origin '+refs/heads/*:refs/remotes/origin/*'
$ gitleaks git --config .gitleaks.toml --redact
INF 2018 commits scanned.
WRN leaks found: 1
Same config, same binary, same checked-out commit. The only variable is which other
branches happen to be in the clone.
The finding
RuleID : generic-api-key
File : plugins/claude-config/lib/resolve-convention-home.sh
Line : 248
Commit : f7aec1549dec60407a24a7d8ea720696b7b099af
Branch : chore/retired-conventions-mechanism
Match : CLAUDE_TOKEN=""
The matched text is an empty shell variable initialisation, next to its sibling on the
line above:
AGENTS_STATE="none"; AGENTS_TOKEN=""
CLAUDE_STATE="none"; CLAUDE_TOKEN=""
These are parser state variables for a file scan. There is no credential here, and the
assigned value is the empty string, so nothing is disclosed. generic-api-key fired on
the identifier alone.
Neither the file nor the line exists on main, which is why main's last push run
(33562372229, f43bd07, 21:40Z) was green and PR runs started failing at 22:12Z: the
commit landed on its branch at 21:47Z, between the two.
Two separate defects
- The false positive.
CLAUDE_TOKEN="" is a state variable, not a secret.
- The blast radius. A finding on any branch fails hygiene on every other branch. A
contributor sees a red check citing a file their PR never touched, which is the part
that actually costs time. Defect 1 is a one-line fix on one branch; defect 2 will do
this again on the next false positive.
Options
For the finding, per .gitleaks.toml's own policy ("Intentional findings use a
repository-owned .gitleaksignore or inline gitleaks:allow comment"), an inline
# gitleaks:allow on that line in the owning branch is the narrower of the two and does
not need a fingerprint that goes stale when the commit is rebased.
For the blast radius, scan-mode: git could be scoped to the range under review
(--log-opts over the PR's own commits) so a PR is judged on its own diff, with
full-history scanning kept on the main-branch push run where it still catches
history rewrites. That is a change to the shared ci-workflows action's contract or its
inputs, so it wants its own decision rather than a drive-by edit.
Not fixed here
Neither fix belongs in an unrelated PR: the false positive is on a branch whose author
should decide the annotation, and the scoping change touches shared CI. Filed so the
red check has an owner instead of being re-diagnosed by whoever hits it next.
Summary
The
hygienejob's secret scan is failing on open PRs for a finding that belongs to abranch those PRs do not contain. As of this writing that is every PR CI runs, plus main's
next push. The finding itself is a false positive.
Reproduction
.github/workflows/ci.yml:137-144runs the scan withscan-mode: git. In that modegitleaks walks the commits reachable from all refs in the clone, not the checked-out
ref.
actions/checkoutwithfetch-depth: 0fetches every branch, so each PR's scancovers unrelated in-flight branches.
Reproduced locally against gitleaks 8.30.1, the version CI installs:
Same config, same binary, same checked-out commit. The only variable is which other
branches happen to be in the clone.
The finding
The matched text is an empty shell variable initialisation, next to its sibling on the
line above:
These are parser state variables for a file scan. There is no credential here, and the
assigned value is the empty string, so nothing is disclosed.
generic-api-keyfired onthe identifier alone.
Neither the file nor the line exists on
main, which is why main's last push run(
33562372229,f43bd07, 21:40Z) was green and PR runs started failing at 22:12Z: thecommit landed on its branch at 21:47Z, between the two.
Two separate defects
CLAUDE_TOKEN=""is a state variable, not a secret.contributor sees a red check citing a file their PR never touched, which is the part
that actually costs time. Defect 1 is a one-line fix on one branch; defect 2 will do
this again on the next false positive.
Options
For the finding, per
.gitleaks.toml's own policy ("Intentional findings use arepository-owned
.gitleaksignoreor inlinegitleaks:allowcomment"), an inline# gitleaks:allowon that line in the owning branch is the narrower of the two and doesnot need a fingerprint that goes stale when the commit is rebased.
For the blast radius,
scan-mode: gitcould be scoped to the range under review(
--log-optsover the PR's own commits) so a PR is judged on its own diff, withfull-history scanning kept on the main-branch push run where it still catches
history rewrites. That is a change to the shared
ci-workflowsaction's contract or itsinputs, so it wants its own decision rather than a drive-by edit.
Not fixed here
Neither fix belongs in an unrelated PR: the false positive is on a branch whose author
should decide the annotation, and the scoping change touches shared CI. Filed so the
red check has an owner instead of being re-diagnosed by whoever hits it next.