Leaked credentials almost never happen because someone decided to commit a key. They happen because a .env, a config file, or a debug snippet slipped into a staged change and nobody noticed until a bot on the internet did.
The fix is to make the mistake impossible to commit — a gate that runs before every commit and fails loudly if anything credential-shaped is staged.
The shape of the problem
Most leaked secrets have recognizable prefixes:
| Provider | Looks like |
|---|---|
| OpenAI | sk-... |
| Anthropic | sk-ant-... |
| AWS | AKIA... |
| GitHub PAT |
ghp_... / github_pat_...
|
| Stripe | sk_live_... |
AIza... |
|
| Private keys | -----BEGIN ... PRIVATE KEY----- |
A scanner that knows these can catch the overwhelming majority of accidental leaks with almost no false positives.
The 2-minute setup
You don't need a heavyweight platform. A single command in a git hook does it. Here's one using ctxpack (a zero-dependency CLI), but the pattern works with any scanner that exits non-zero on a hit:
Create .git/hooks/pre-commit:
#!/bin/sh
npx github:trongtruong110-ux/ctxpack . --check -i "test/**" -i "**/*.example" || {
echo "Commit blocked: a possible secret was found above."
exit 1
}
chmod +x .git/hooks/pre-commit
Now every commit is scanned. When something slips in, you get:
ctxpack --check: 1 potential secret(s) in 34 files:
src/config.js:12 ANTHROPIC_KEY
✗ failing — remove or ignore these before committing.
Note it reports the location and type, never the secret value itself — so the finding is safe to show in CI logs.
Two details that make it stick
-
Ignore your fixtures. Test suites are full of deliberately-fake keys. Use an ignore glob (
-i "test/**") so the gate doesn't cry wolf — a scanner that fires on fixtures gets disabled within a week. -
Run it in CI too. Local hooks can be skipped with
--no-verify. Add the same--checkline as a CI step so nothing merges with a live credential in it.
Why bother if you have secret scanning on the host?
GitHub and others scan after the push — by then the secret is in history (and history is forever unless you rewrite it). A pre-commit gate stops it before it exists in a commit at all. Defense in depth: keep the host scanner, but don't let it be your first line.
ctxpack is MIT-licensed and free: https://github.com/trongtruong110-ux/ctxpack. What's your current setup for catching secrets before they land — pre-commit, CI, host-side, or a mix?
Top comments (0)