Credential audit — leaks
commitbrief leaks scans every tracked file plus the added lines of your git history for committed credentials — the same pattern set the review path uses, with no provider call, no cache, and no cost. Exits 1 on a hit, so it gates CI out of the box.
commitbrief leaks audits the repository for committed credentials —
every tracked file in the working tree, plus the lines added by
historical commits. It is fully deterministic: the same pattern set the
secret scanner uses, no
provider call, no cache, no cost. New in v1.15.0 (ADR-0036).
Why this is a separate command
The pre-send secret scanner is a gate. It runs before a review is sent and sees exactly one thing: the added lines of that diff. That is the right shape for stopping a leak in flight, and the wrong shape for two questions people actually ask:
- Is there a secret in my working tree right now? The gate never reads files — only a diff.
- Did anyone ever commit one? The gate reads the added lines of one diff. A key that was committed and later removed is still in the history — and still reachable by anyone who cloned or forked the repo before you scrubbed it.
leaks answers both.
Usage
commitbrief leaks # tracked files + last 200 commits
commitbrief leaks --no-history # working tree only, fast
commitbrief leaks main..HEAD --no-worktree # exactly that range
commitbrief leaks --author alice --start-date 2026-01-01
commitbrief leaks --max-commits 0 # the whole history (slow)
commitbrief leaks --patterns # what it looks for, then exit
The two halves
Both run by default, and each has its own off-switch — so a positional range narrows the history half without silently disabling the tree.
| Half | What it reads |
|---|---|
| Working tree | Every tracked file, whole — not just added lines. |
| History | The added lines of each selected commit, attributed to its hash, author, and date. |
Untracked files are deliberately out of scope. An untracked, gitignored
.env is where a secret is supposed to live, and it cannot leak
through git — flagging it would be noise.
Added lines only, for history, is precisely how a removed-but-committed key is found. Scanning context lines instead would re-report the same key once per commit that happened to touch the file near it.
Flags
| Flag | Default | Notes |
|---|---|---|
--no-worktree |
off | Skip the working-tree half. |
--no-history |
off | Skip the commit-history half. Disabling both is an error. |
--patterns |
off | Print the effective pattern set (built-ins + your guard.secret_patterns) and exit. |
--max-commits <N> |
200 |
Bound the history half. 0 walks everything. Truncation is always reported. |
--fail-on <sev> |
any |
See Exit code — the default differs from every other command. |
Every filter applies. The
commit filters
(--author, --committer, --start-date, --end-date, --text)
narrow which commits the history half reads; the path filters
(--file, --dir, --exclude-file, --exclude-dir) narrow which
files either half reads.
Output
Scanned 613 file(s) across 50 commit(s).
Skipped 4 file(s): binary or over the size cap.
CRITICAL config/prod.yml:14 AWS Access Key
CRITICAL deploy/legacy.env:3 Stripe Live Key (a1b2c3d, alice)
2 possible credential(s) found. Rotate them — a key in git history stays reachable in every existing clone.
The trailing (hash, author) marks a history hit: the line may be
long gone from your working tree. Coverage is always stated — files
scanned, commits scanned, files skipped, and whether the walk was
truncated — because “no credentials found” means nothing without knowing
what was looked at.
What it never prints
The matched text. A finding carries a file, a line, and the pattern
names — nothing else. The scanner reads whole files, so its own report
must not become a second copy of the secret in your terminal scrollback,
your CI log, or the pastebin you were about to send it to. In --json,
snippet is always absent for the same reason.
Severity
Hits are critical, with two deliberate exceptions at high:
- JWT — expired and sample tokens are common in fixtures; it is the one built-in with a real false-positive rate.
- User patterns from
guard.secret_patterns— CommitBrief cannot judge how bad a hit on your house format is.
Exit code
leaks is the one command where --fail-on defaults to any: a hit
exits 1, so it gates CI with no extra configuration. The report is
printed first, then the run fails — you get the findings and the
non-zero exit.
commitbrief leaks # exit 1 if anything is found
commitbrief leaks --fail-on none # report only, always exit 0
commitbrief leaks --fail-on critical # ignore the `high` tier
Feeding the policy gate
--json emits the standard
schema v1 document, so
guard consumes it directly with no new
plumbing:
commitbrief leaks --json --fail-on none | commitbrief guard --from-json -
meta.provider is "builtin" and cost_usd is 0 — there was no model
call. That reuse is deliberate: a separate scan schema would have bought
nothing and broken this pipeline.
Limits — read these before trusting a clean report
- The ignore layers apply.
leakshonors the built-in patterns and.commitbriefignoreexactly as a review does, so a key committed intovendor/**,dist/**, or a*.min.jsis not reported. This keeps.commitbriefignorethe single place a project says “not this”. Narrow with--dirif you need the wider sweep. - Regex only, no entropy scoring. A high-entropy blob with no recognizable prefix is invisible. The patterns are deliberately tight — length floors and fixed prefixes — which is what keeps false positives rare. This is a targeted check, not a general-purpose secret scanner.
- Binaries and large files are skipped (a NUL byte in the first 8 KiB, or over 5 MiB). Both are counted in the report.
- Untracked files are not scanned.
--max-commitsbounds history at 200 by default. Truncation is reported, never silent.
If it finds something
Rotate the credential at its provider. That is the fix.
Removing the line, amending the commit, or rewriting history does not reach clones, forks, CI caches, or anyone’s local checkout that already has the commit. Treat a committed key as compromised from the moment it was pushed.
See also
- Safety, cost, and cache — the pre-send gate this complements, and where you register your own patterns.
- Review scopes — the commit and path filters that narrow a scan.
- Policy gate — guard — the gate
--jsonfeeds. - Output formats — the
document
--jsonemits.