---
name: history-leak-scan
description: >-
  Deterministic secret scanner for git repos — full history (--history) or
  staged diff (--staged): 17 provider-key rules, private-key blocks, JWTs,
  sensitive filenames (.env, .pem, id_rsa, *_keys.env), high-entropy +
  weak-password heuristics. Backs commit-gate in staged mode. Use when: "scan
  for secrets", "leak scan", "did I commit a secret", after any repo goes
  public or any suspected exposure. Reads .claude/secrets-inventory.md. No
  deps, no API key.
---

# history-leak-scan — deterministic secret scanner

The engine is `pm-secretscan.js` (portable Node, zero deps). It streams
`git log -p --all` (history) or `git diff --cached` (staged) and flags added
lines against per-provider regexes (16 rules) + a sensitive-filename rule
(.env*, *.pem/.p12/.pfx, id_rsa*, *_keys.env — a file of that NAME should never
be committed, whatever it contains; example/sample/template/fixture names
exempt) + a generic high-entropy-assignment detector + a weak-password rule,
with token-level placeholder suppression.

## Commands

- **Full-history scan (one or more repos):**
  `node pm-secretscan.js --history <repo> [<repo>...]`
  Exit 1 if any finding, 0 if clean. Redacts matched tokens in output.
- **Staged scan (what commit-gate runs):**
  `node pm-secretscan.js --staged <repo>`
- **Self-test (part of the definition of done):**
  `node pm-secretscan.js --canary`
  Plants real-format secrets + placeholders in a throwaway repo, asserts
  ≥7 real caught and 0 false positives. MUST print `PASS` before you trust a
  scan result — an unverified gate is theater.

## When invoked

1. **Run `--canary` first** if the scanner was touched since last use; paste the
   PASS line. Never report a "clean" scan from an unverified scanner.
2. Enumerate the repos to scan (`find <root> -name .git -type d`). Scan all with
   `--history`.
3. Triage every finding by READING the actual line — distinguish a live
   credential from a guarded dev-default or an example. Do NOT auto-rotate;
   report, and on a real leak point at secret-rotation + the secrets-inventory.
4. Report outcome-first: repos clean vs. findings, each finding as
   file@commit + rule + redacted snippet, and the honest severity.

## Known limits
- Catches secrets that were COMMITTED. Files that were always gitignored
  (`.env`, key files) are correctly out of history — verify they were never
  committed by a clean history scan, not by their current absence.
- Entropy detection can miss bespoke low-entropy formats — that's why the
  per-provider regexes exist; extend `RULES` when a new provider appears (and
  add a canary fixture for every new rule — a rule without a canary is
  unverified).
- No live credential verification (TruffleHog-style API checks) BY DESIGN:
  that would transmit candidate secrets to provider endpoints. Triage of
  live-vs-dev-default stays a human/model judgment step.
- Heuristic exemption also covers rendered-doc twins under `docs/*.html`
  (a generated HTML render of an exempt `.md`); strong provider/private-key
  rules still apply there. Diff headers with special-char filenames (git
  C-quotes them, e.g. an em-dash path) are unquoted before the filename rules
  run — otherwise every rule keyed on the filename would silently no-op.
- Redaction shows first-4 + last-2 of long tokens; still treat output as
  sensitive.
