Skip to content

fix(leak-scan): repair four rules that could never fire - #127

Merged
ExtraToast merged 1 commit into
mainfrom
fix/leak-scan-pattern-and-exclude-bugs
Aug 29, 2026
Merged

fix(leak-scan): repair four rules that could never fire#127
ExtraToast merged 1 commit into
mainfrom
fix/leak-scan-pattern-and-exclude-bugs

Conversation

@ExtraToast

Copy link
Copy Markdown
Contributor

Summary

The deny-list matches with grep -E, but every test asserts against Python's re. Those engines disagree silently, so patterns using Perl-only syntax passed the whole suite while matching nothing in production.

Same pattern, same input 10.42.0.0/16:

engine result
Python re (what the tests use) matches
GNU grep -E (what actually runs) 0 matches

Four separate defects, all of which meant a rule reported clean when it should have fired:

1. ssh_keys — private keys were undetectable. -----BEGIN .* PRIVATE KEY----- begins with -, so grep parsed it as an option and exited with a usage error visible in every run:

grep: unrecognized option '-----BEGIN .* PRIVATE KEY-----'

A private key added in a diff scanned clean. Both call sites now pass patterns with -e.

2. Perl-only syntax in six patterns. \d and (?:...) are meaningless in POSIX ERE:

category was effect
ipv4_literal \d{1,3} entirely dead
ipv6_literal (?:...) both patterns dead; source of the ? at start of expression warnings
cgnat [7-9]\d, 1[01]\d 100.70–119 undetected
rfc1918 2\d 172.20–29 undetected

3. EXCLUDE_PATHS was wired into every mode except the one it documents. The input says "Only effective in pr-diff mode", but run_diff_deny_list_scan never read it — callers passing exclude-paths to suppress a known false-positive class got no exclusion at all. Now applied to the added-line scan, using the same literal-prefix semantics as the path scan.

4. The exclusion counter crashed the scan. Found while fixing 3. grep -c . prints 0 and exits 1 on no match, so || echo 0 appended a second zero and the arithmetic saw 1 - 0\n0:

run.sh: line 305: 1 - 0
0 : syntax error in expression

Excluding every changed file didn't skip the scan, it crashed it — a docs-only PR in a repo passing exclude-paths would have failed on a shell syntax error.

Validation

The new test module shells out to grep, not re — that engine divergence is the actual defect class, and no amount of Python-side testing would have caught any of this.

  • Verified the new test fails against the previous patterns before it passed against these: 6 subtest failures, one per dead pattern.
  • Full suite: 116 passed, 228 subtests, no regressions.
  • End-to-end against the real run.sh with gitleaks installed, GNU grep, linux/amd64:
scenario expected result
private key in diff fail fails (was: passed clean)
IPv4 literal in diff fail fails (was: passed clean)
IPv4 literal in an excluded path pass passes (was: crashed)
clean diff pass passes

Risk

The IP rules going live is the real behavioural change here, so I measured it rather than guessing. Over the last 17 commits on fleet-infra main, deny-list hits on added lines:

0 would have been blocked under the old patterns, and 0 under the new ones.

Repos that legitimately carry subnet literals now have a working valve for the first time, since exclude-paths actually functions.

Consumers pin by tag (fleet-infra is on @v0.12.9), so nothing changes for anyone until they bump — this cannot surprise a repo mid-flight.

Links

  • Found while investigating a false positive on JorisJonkers-dev/fleet-infra#167, where two lines of standard RFC1918 except: boilerplate in a NetworkPolicy block the required check.

The deny-list matches with `grep -E`, but every test asserted against
Python's `re`. Those engines disagree silently, so patterns using
Perl-only syntax passed the whole suite while matching nothing in
production. Proven with one pattern and one input, 10.42.0.0/16:
python re matches, GNU grep -E matches 0.

Dead under grep -E, now fixed:

  ipv4_literal   \d          -> [0-9]   (entirely dead)
  ipv6_literal   (?:...)     -> (...)   (both patterns dead; source of
                                         the "? at start of expression"
                                         warnings in every run)
  cgnat          \d          -> [0-9]   (100.70-119 undetected)
  rfc1918        2\d         -> 2[0-9]  (172.20-29 undetected)

The ssh_keys rule failed differently: `-----BEGIN .* PRIVATE KEY-----`
begins with '-', so grep parsed it as an option and exited with a usage
error. A private key in a diff scanned clean. Both call sites now pass
patterns with `-e`.

EXCLUDE_PATHS is documented as "Only effective in pr-diff mode" and was
wired into every mode except pr-diff, so callers passing exclude-paths
to suppress a known false-positive class got no exclusion at all. It is
now applied to the added-line scan, matching the path scan's literal
prefix semantics.

Fixing that exposed a fourth bug in the exclusion counter: `grep -c .`
prints 0 AND exits 1 on no match, so `|| echo 0` appended a second zero
and the arithmetic saw "1 - 0\n0". Excluding every changed file did not
skip the scan, it crashed it -- a docs-only PR in a repo passing
exclude-paths would have failed on a shell syntax error.

The new test module shells out to grep rather than trusting `re`, which
is the actual defect class here. Verified it fails against the previous
patterns (6 subtest failures, one per dead pattern) before it passed
against these. End-to-end against real run.sh with gitleaks present: a
private key now fails the scan, an IPv4 literal now fails, an excluded
path is skipped, a clean diff still passes.

Blast radius of the newly-live IP rules, measured over the last 17
commits on fleet-infra main: zero would have been blocked under either
the old or the new patterns.
@ExtraToast ExtraToast added type: bug Something is broken or behaving incorrectly. area: tooling Reusable workflows, Gradle, templates, Renovate, and API tooling. component: security Security, permissions, secrets, or vulnerability handling. priority: P1 High; important and should be handled in the current iteration. labels Aug 29, 2026
@ExtraToast
ExtraToast merged commit 300e8af into main Aug 29, 2026
6 checks passed
@ExtraToast
ExtraToast deleted the fix/leak-scan-pattern-and-exclude-bugs branch August 29, 2026 20:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: tooling Reusable workflows, Gradle, templates, Renovate, and API tooling. component: security Security, permissions, secrets, or vulnerability handling. priority: P1 High; important and should be handled in the current iteration. type: bug Something is broken or behaving incorrectly.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant