Repository navigation
fix(ci): Secret Scan covered 0 commits on PRs, and main was red on a fake-JWT fixture - #2389
Conversation
…n has been red since 10-16
generic-api-key matched a test fixture in agentProvisionerServiceK8s.test.js:456,
the base64 of {"alg":"RS256"} followed by ".jwt" (no payload, no signature).
Because the scan walks main's full history, every push to main has failed
Secret Scan since #2213 (f660d6f), which hides any real finding behind a
known red. The allowlist names that exact literal, so a real JWT still fails.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015eTFc9yqm2vKZiUyvLuBT4
…of a --remotes glob `--remotes=origin/main` is a git glob that becomes origin/main/*, so it matched nothing. On a push the scan still worked, because `--branches` held the checked-out branch, but a pull request checks out a detached merge commit with no local branch, and every PR scan reported "0 commits scanned, no leaks found": #2375, #2368 and #2389 all did. That is how #2213's fixture passed review and only went red on main. Scan HEAD (the PR merge commit, or the pushed branch) plus origin/main and origin/v1.0.x by name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015eTFc9yqm2vKZiUyvLuBT4
… it says what it covered; Secret Scan now fails on 0 commits Secret Scan passed every pull request from #84 (2026-04-03) with "0 commits scanned … no leaks found", and nobody noticed because main's push scan was real. Rule 56 makes the coverage line the first thing a reviewer reads and an empty input a failure. The workflow enforces it: it counts the commits it is about to scan and fails on zero. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015eTFc9yqm2vKZiUyvLuBT4
lilyshen0722
left a comment
There was a problem hiding this comment.
Code gate — #2389 at 6270d767b3431818c0e2b1625cdf25f2dc56622c
origin/main = 7e21f48658ace2a6820435c3299fddadd90c8357, which is this PR's merge-base. Every check row is pass or skipping, and I enumerated the branch's runs rather than reading the rollup alone: the four cancelled runs sit beside a success for the same workflow at this same head, so nothing is concealed behind a cancellation, and no startup_failure is hiding outside gh pr checks.
All three properties hold, and I measured each with the real instrument rather than reading it. One finding, which is rule 56 pointed at the guard itself.
1. The root cause and the fix, measured in this repo
git rev-list --count --remotes=origin/main -> 0
git rev-list --count --remotes=origin/main/* -> 0
git rev-list --count --remotes=origin/main* -> 2635
git rev-list --count origin/main -> 2635
git rev-list --count HEAD origin/main origin/v1.0.x -> 2635
So --remotes=origin/main really does match nothing, the origin/main/* explanation is consistent with it, and naming the refs is the right fix. origin/v1.0.x exists (a1b654eb9) and is currently an ancestor of origin/main, so that third term contributes zero commits today — harmless and future-proof, worth knowing only so nobody reads it as covering history main doesn't have.
And verified at the consumer rather than in the config: the Secret Scan run at this exact head (39641872897, head_sha read back) logs
commits to scan: 2639
INF 2582 commits scanned.
INF no leaks found
fetch-depth: 0 is what makes those refs resolvable, and it is already there.
2. The allowlist, through the pinned scanner, in both directions
I installed gitleaks v8.21.2 — the version the workflow pins — and ran it with this PR's own .gitleaks.toml against two fixtures: the exact literal, and a real three-part JWT (header, payload, signature).
| config | findings |
|---|---|
| this PR's | 1 — jwt on the real token. The fake fixture is clean. |
| the same with the new entry removed | 2 — generic-api-key on the fake fixture, plus the same jwt |
So the entry is necessary, it is generic-api-key that was firing (as the comment says, by name), it suppresses exactly that one finding, and a real JWT still fails — caught by the built-in jwt rule, which the allowlist cannot reach because a real token's header is followed by a base64 payload, never the literal .jwt.
It is also not redundant with a path rule: the fixture lives at backend/__tests__/unit/services/agentProvisionerServiceK8s.test.js, and the paths allowlist covers only two files under __tests__/integration/, not that directory.
3. Rule 56
node scripts/verify-numbered-rules.js at this head:
✓ docs/development/review-checklist.md: 56 rules, numbers 1..56 ascending with no gap, 58 citation(s) all resolve.
exit=0
The rule itself is the right generalisation, and its last sentence is the part I would keep hardest: the trigger you watch is real, and it vouches for the one you don't. That is why this sat for six months — main's scan was genuine, so the green on every pull request inherited its credibility.
The finding: the commit spec is written twice, with nothing tying the copies together
n=$(git rev-list --count HEAD origin/main origin/v1.0.x)
...
--log-opts="HEAD origin/main origin/v1.0.x"
Two separate literals. Today they agree, so the guard measures the input the scan actually reads — which is exactly the right design, and better than counting a proxy. But a later edit to one and not the other leaves the guard reporting a healthy count for a spec the scanner isn't using, which is the same shape as the bug being fixed: a number that vouches for coverage it no longer measures. One variable used in both places (spec="HEAD origin/main origin/v1.0.x", then --log-opts="$spec") removes the possibility entirely, and it is the guard's own rule applied to itself.
A precision note in the same spirit, since rule 56 now tells reviewers to read the coverage line: commits to scan: 2639 and 2582 commits scanned are two different measurements, 57 apart — the guard counts revisions, gitleaks counts the commits it diffed. Both are coverage, neither is the other, and a reader chasing a discrepancy between them would be chasing nothing.
Written by sprint-review, a Commonly agent — https://commonly.me/v2/pods/6a692a1be833c668acdb84cf
GATE: PASS
Secret Scan was failing in two ways. Main has been red on every push since 2026-10-16, and on pull requests it has been scanning nothing.
1. Pull-request scans covered 0 commits (
.github/workflows/secret-scan.yml)--log-opts="--branches --remotes=origin/main --remotes=origin/v1.0.x". Ingit log, a--remotes=<pattern>with no glob character gets/*appended, so--remotes=origin/mainmeansorigin/main/*and matches nothing. Locally it lists 0 commits, against 2,635 fororigin/main.--branchesheld the checked-outmain.HEAD origin/main origin/v1.0.x. HEAD is the merge commit on a PR and the pushed branch on a push. On this PR's head, the scan covered 2,581 commits and found no leaks.2. Main red on a test fixture (
.gitleaks.toml)generic-api-keyfires onbackend/__tests__/unit/services/agentProvisionerServiceK8s.test.js:456, added in fix(provisioner): reuse an agent's LiteLLM key from the slot it is written to #2213 (f660d6f).{"alg":"RS256"}plus the literal.jwt, with no payload and no signature.3. The lesson, written down and enforced
docs/development/review-checklist.mdgets rule 56: a scan that reports nothing found has proved nothing until it says what it covered, and zero items is a failure, not a pass.secret-scan.ymlnow counts the commits it is about to scan and fails on zero, before gitleaks runs. On this head the count is 2,637.The two fixes ship together because they depend on each other: a working PR scan would hit fixture 2 on every PR.
🤖 Generated with Claude Code
https://claude.ai/code/session_015eTFc9yqm2vKZiUyvLuBT4