Secret scans are cheap compared to rotating keys.
A leaked key turns into incident work: revoke, replace, audit usage, rewrite history if needed, update systems, check logs, notify owners, and explain the blast radius. A pre-commit or CI scan is boring by comparison.
A secret scanner is boring right up until it saves a weekend.
secrets enter through normal work
Most leaked secrets do not arrive through cinematic negligence.
They arrive through normal work:
- copying
.envinto an issue - committing a test fixture
- pasting a token into docs
- saving a generated config
- adding a screenshot with a visible key
- checking in a service account file
- logging headers while debugging
- including real credentials in a curl example
- leaving a notebook output in the repo
That is why scanning belongs near the workflow. The boundary is where the secret can enter: local commit, pull request, CI artifact, generated file, docs snippet, or test data.
If the check runs only after merge, it is still useful. If it runs before commit, it is much better.
rotation is the expensive path
Once a secret lands in git history, the question is no longer “can we delete it?”
Git remembers. Forks remember. CI logs may remember. Dependency caches may remember. Search indexes may remember. Someone may have pulled the branch already.
The rotation path is tedious:
- identify the secret
- determine what system it grants access to
- revoke it
- create a replacement
- update every consumer
- verify production still works
- audit usage during exposure
- decide whether history rewrite is needed
- invalidate caches if necessary
- document the incident
That is a lot of work for a mistake that a scanner might have stopped at the door.
scanning should happen in layers
I like layered secret scanning because each layer catches a different failure.
Pre-commit scanning catches mistakes while the developer still has the file open. It is fast and local. The downside is that people can skip hooks, work on machines without setup, or generate files in unusual paths.
CI scanning catches pull requests before merge. It is more reliable because it runs in the shared path. It should scan the diff and, when reasonable, the repository state.
Server-side scanning or hosting-provider scanning catches secrets that still make it upstream. This is the last net, not the first one.
Those layers should agree on the baseline. If the repo already has known test keys, document and suppress them deliberately. A scanner full of ignored false positives becomes background noise.
false positives need ownership
Secret scanners will flag things that are not secrets.
That is fine. The response should be structured.
For each finding, decide:
- real secret
- test credential
- generated false positive
- public example token
- intentionally committed fixture
- needs investigation
Real secrets get rotated. Test credentials should look obviously fake. Public examples should use impossible values. Intentional fixtures should be documented and scoped.
The goal is not zero alerts. The goal is alerts people trust enough to act on.
examples should be fake in a useful way
Documentation often needs examples. Examples should not look like real secrets.
Bad:
OPENAI_API_KEY=sk-abc123
AWS_SECRET_ACCESS_KEY=abc123
Better:
OPENAI_API_KEY=replace-with-your-openai-api-key
AWS_SECRET_ACCESS_KEY=replace-with-your-aws-secret
Even better, use obviously invalid formats when the tool allows it. Realistic-looking fake keys create noise for scanners and confusion for readers.
The same applies to screenshots. Blur keys, tokens, account IDs, internal hostnames, and anything that could be mistaken for a credential or sensitive endpoint.
generated files deserve suspicion
Generated files are a common leak path.
Tools write config. SDKs create credential files. Test harnesses dump environment. Build steps serialize state. Debug scripts save API responses. AI tools generate examples from nearby files and may accidentally include secrets from context.
Generated output should either be ignored, cleaned, or scanned.
I like explicit .gitignore entries for local state:
.env
.env.*
!.env.example
*.pem
*.key
service-account*.json
.local/
tmp/
Ignore rules are not a replacement for scanning. They reduce the chance of accidental staging. Scanning catches the things ignore rules missed.
ai coding makes this more important
AI coding tools change the secret-scanning story because they can move text around quickly.
An agent may read a local file, generate a config example, update docs, or paste an error message into a test fixture. It may not understand that a nearby string is sensitive. It may create a realistic-looking key because examples on the internet use realistic-looking keys.
That makes the boring controls more valuable:
- keep real secrets out of repo-readable files
- scan diffs before commit
- scan generated docs and fixtures
- use fake example values
- review agent-created config changes carefully
The scanner becomes part of the agent boundary.
alerts should have a runbook
When a scanner fires, the team should not improvise from scratch.
The runbook can be small:
- stop the merge
- identify the credential type
- notify the owner
- revoke or rotate if real
- remove the value from the diff
- check whether it reached remote history
- add a test fixture or ignore rule only if it was a false positive
The important part is speed. A real secret should move from detection to revocation quickly, without a debate about whether the scanner is being annoying.
prevention wins
Scanning will not catch everything.
It can miss unknown formats, secrets split across strings, screenshots, encrypted blobs, or credentials that look like ordinary text. It can also generate false positives. That does not make it optional.
Security work often has this shape: a cheap control prevents a boring mistake from becoming a serious incident.
Secret scanning is one of those controls. It changes the economics. Preventing one real credential from landing in history pays for a lot of boring tooling.
Related posts

About Jeremy London
Engineering leader and builder in Denver. I write about AI platforms, agents, security, reliability, homelab infrastructure, and the parts of engineering work that have to survive production.