Skip to content

fix(ci): Secret Scan covered 0 commits on PRs, and main was red on a fake-JWT fixture - #2389

Merged
lilyshen0722 merged 3 commits into
mainfrom
fix/gitleaks-fake-jwt-fixture
Oct 21, 2026
Merged

lilyshen0722 merged 3 commits into
mainfrom
fix/gitleaks-fake-jwt-fixture

Conversation

@lilyshen0722

@lilyshen0722 lilyshen0722 commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

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)

2. Main red on a test fixture (.gitleaks.toml)

3. The lesson, written down and enforced

  • docs/development/review-checklist.md gets 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.yml now counts the commits it is about to scan and fails on zero, before gitleaks runs. On this head the count is 2,637.
  • Audit of the blind period: audit(ci): one-time secret scan of every branch and PR head (do not merge) #2390, a throwaway PR, scanned every branch and PR head once (8,883 commits across 3,267 refs). The one hit was an example JWT header with "..." on an unmerged April branch, not a credential.

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

lilyshen0722 and others added 2 commits October 21, 2026 09:57
…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
@lilyshen0722 lilyshen0722 changed the title fix(ci): allowlist the fake JWT fixture from #2213; main's Secret Scan red since 10-16 fix(ci): Secret Scan covered 0 commits on PRs, and main was red on a fake-JWT fixture Oct 21, 2026
… 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
lilyshen0722 marked this pull request as ready for review October 21, 2026 17:29

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@lilyshen0722
lilyshen0722 added this pull request to the merge queue Oct 21, 2026
Merged via the queue into main with commit 7876fc1 Oct 21, 2026
26 of 30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant