$ git push origin main
# A few seconds later, email from AWS
# Next morning: $8,000
Leaked API keys are a solved problem — in theory. In practice, they keep happening. GitHub says exposed credentials are scraped by bots within seconds of being pushed.
The usual advice is "use a secrets manager" or "scan your repo with a tool." But the best time to catch a leaked secret is before it ever leaves your machine.
That's why I built push-sentinel — a zero-dependency CLI that sits in your pre-push hook and scans the exact diff being pushed.
Install in one command
npx --yes --prefer-online push-sentinel@latest install
That's it. From now on, every git push runs the scan automatically.
What it looks like
Clean push:
[push-sentinel] ✓ No secrets detected.
When something is found:
[push-sentinel] ⚠ Potential secrets found:
[HIGH] src/config.ts:12
AKIAIO...
→ Risk: Full access to AWS resources. Attacker can create/delete
instances, incur charges, or exfiltrate data.
→ To ignore this line: push-sentinel ignore src/config.ts:12
Push continues. Double-check before sharing.
By default it warns and lets the push through — no friction, no temptation to reach for --no-verify.
What it detects
| Pattern | Severity |
|---|---|
| Private Key (RSA, EC, OPENSSH, DSA, PKCS#8) | 🔴 HIGH |
AWS Access Key (AKIA...) |
🔴 HIGH |
| AWS Secret Key (variable name + entropy) | 🔴 HIGH |
GitHub Token (ghp_, github_pat_) |
🔴 HIGH |
Anthropic API Key (sk-ant-...) |
🟡 MEDIUM |
OpenAI API Key (sk-...) |
🟡 MEDIUM |
| Generic API Key (variable name + high entropy) | 🟢 LOW |
.env file committed |
🟡 MEDIUM |
The entropy check on generic keys keeps false positives low — short or repetitive strings are skipped even if the variable name matches.
False positives? One command to ignore
push-sentinel ignore src/config.ts:12 # ignore a specific line
push-sentinel ignore --pattern OPENAI_API_KEY # ignore a pattern everywhere
push-sentinel ignore --list # see all rules
push-sentinel ignore --remove OPENAI_API_KEY # remove a rule
Rules go into .push-sentinel-ignore at your repo root.
Why warning-only by default?
Hard blocks feel safer, but they train people to use --no-verify. A warning at push time is early enough to catch real accidents, and people actually leave it installed.
If you want hard blocking for HIGH findings:
# In .git/hooks/pre-push, change the scan line to:
npx --yes --prefer-online push-sentinel@latest scan --local-sha "$local_sha" --remote-sha "$remote_sha" --block-on-high
Team or org-wide? Use the GitHub Action
For enforcing secret scanning across PRs without relying on every developer having the hook installed, there's also a GitHub Action:
- uses: Pmaind/push-sentinel-action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
It blocks merges on HIGH findings and posts a PR comment with a findings table.
How it works under the hood
Git passes pushed ref info to the pre-push hook via stdin:
<local-ref> <local-sha> <remote-ref> <remote-sha>
push-sentinel uses those SHAs to compute the exact range of commits being pushed (git log remoteSha..localSha -p), so it only scans what's actually new. It handles edge cases too:
- New branch (no remote SHA): scans commits not yet reachable from any remote
- Ref deletion (zero SHA for local): skips the scan, nothing is being pushed
-
Manual scan (no SHAs): falls back through
@{u}..HEAD→ staged → working tree → last commit
Zero dependencies
The entire tool uses only Node.js stdlib — no node_modules, no supply chain risk. Node.js >= 16 required.
- npm: https://www.npmjs.com/package/push-sentinel
- GitHub: https://github.com/Pmaind/pre-push-secrets
- GitHub Action: https://github.com/Pmaind/push-sentinel-action
Feedback, false positive reports, and PRs welcome.
Top comments (0)