Lesson 5 of 9 · 20m
Secrets Scanning: Committed Keys Are Forever
Deleting the line doesn't delete the secret — git is append-only, bots watch every public commit, and the only real remediation is rotation.
Years ago I watched an engineer commit an AWS access key "just to test something," delete it four minutes later, and open an incident anyway — because the key was already in git history, already in every clone and CI cache, and the bots that watch public commits don't sleep. We spent a weekend rotating keys and reading CloudTrail. The deletion commit changed nothing. That's the lesson.
Deleting the line doesn't delete the secret
Git is append-only. A credential in any commit survives in every clone, every fork, every checkout cache, every mirror. Once a real credential touches a repo — private or public — the only remediation is rotate it, then scrub history if you must. git filter-repo and BFG help with copies you control; they do nothing for copies you don't.
Two scanners, two jobs
# gitleaks: regex + entropy scanning of history and the working tree
gitleaks detect --source . --redact --exit-code 1 # CI / audit mode
gitleaks protect --staged --verbose # pre-commit mode
# trufflehog: similar coverage, plus live verification
trufflehog git file://. --only-verified--only-verified is the flag that matters: trufflehog actually tries each candidate credential against its provider and reports only the ones that work. That separates a live AWS key — page someone now — from a thousand test fixtures and documentation examples that belong on a cleanup queue.
Coverage has to exist in three places
- Pre-commit —
gitleaks protect --stagedvia.pre-commit-config.yamlcatches the secret before history exists. - CI —
gitleaks detectwithfetch-depth: 0catches what bypassed the hooks, and hooks do get bypassed. - Platform — GitHub push protection, GitLab secret detection. A third net for what both of yours miss.
# .pre-commit-config.yaml
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.4
hooks:
- id: gitleaksWhen it fires
Rotate first, investigate after. Write the playbook per secret type before you need it: revoke at the provider, check provider-side audit logs for use (aws iam get-access-key-last-used, CloudTrail for calls made with the key), commit the removal, then baseline the historical string in the scanner so it stops re-firing.
Field notes
- Entropy detection finds high-entropy strings that regex misses — and flags every minified JS blob in the repo. Tune it or drown.
# gitleaks:allowinline markers exist for fixtures and test vectors — require a comment explaining why, and review them.- The real fix is upstream: if developers have a secrets manager (Doppler, Vault, cloud-native) they actually like, they stop pasting keys into
.envin the first place.