Why Pre-Commit Beats PR-Time Scanning: The Case for Catching Secrets Before They Exist

Written by the Rafter Team

Every major code security platform — DeepSource, SonarQube, Semgrep, Snyk — follows the same architecture: scan code after it's pushed, flag issues in the pull request. It works. It catches things. But for one critical category of findings — leaked secrets — PR-time scanning has a fundamental problem.
By the time your PR scanner flags it, the secret is already in Git history.
The Git History Problem
When you commit a file containing an AWS key and push it to a branch, that key is now in at least three places:
- The working tree (the file itself)
- The Git object store (the commit)
- The remote (GitHub/GitLab/Bitbucket)
Your PR scanner runs, flags the key, and you delete it in a follow-up commit. Problem solved?
No. The key is still in commit abc123 on that branch. Anyone with read access to the repo can run git log -p and find it. You now have two options:
git filter-repoor BFG Repo Cleaner — rewrites history, force-pushes, invalidates every open PR and local clone. On a team of 10, this is a fire drill. On a team of 100, it's an incident.- Rotate the credential — which you should do anyway, but now you're doing emergency rotation instead of planned rotation.
Both paths cost hours. The PR scanner caught the issue but didn't prevent the damage.
Pre-Commit: Prevent, Don't Detect
A pre-commit secret scanner runs before git commit executes. The secret never enters the object store. There's nothing to scrub, nothing to rotate (unless the key was already live elsewhere), nothing to explain in a post-mortem.
$ git commit -m "add config"
🚫 rafter: blocked — AWS secret access key detected in config/settings.py:14
Pattern: AKIA[0-9A-Z]{16}
Commit aborted. Remove the secret and try again.
The developer fixes it in 30 seconds. No branch history contamination. No force-push. No credential rotation. No Slack thread.
Why This Matters More with AI Coding Agents
AI coding agents (Claude Code, Cursor, Copilot, Codex) generate code at a pace humans never did. An agent scaffolding a new service might drop a placeholder that matches a real key pattern, copy a snippet from context that includes credentials, or generate a .env file with example values that are actually live.
The feedback loop matters here:
| Approach | When the agent learns | Damage window |
|---|---|---|
| PR-time scan | After push, minutes to hours later | Secret in branch history |
| Pre-commit hook | At commit time, seconds | Secret never enters history |
With a pre-commit hook, the secret never enters Git history. The agent gets immediate feedback and can fix the issue before it propagates.
"But We Use GitHub Secret Scanning"
GitHub's push protection is excellent and catches secrets at push time. But:
- It only covers GitHub-hosted repos. GitLab, Bitbucket, Azure DevOps, and self-hosted Git get nothing.
- It runs at push, not commit. If you make 5 commits locally before pushing, the secret is in your local history. Your local
.gitdirectory, your backup system, your Time Machine — all now contain the credential. - It doesn't cover AI agent workflows. There's no hook into Cursor or Claude Code's tool-use loop.
Pre-commit scanning is complementary, not competitive. Use both. But pre-commit is the earlier, cheaper intervention.
The Cost Math
| Scenario | Detection point | Remediation cost |
|---|---|---|
| Pre-commit hook blocks it | Before commit | ~30 seconds |
| PR scanner flags it | After push | 1-4 hours (history rewrite + rotation) |
| Nobody catches it | Production | Incident response, potential breach |
For secrets specifically, the ROI of shifting left from PR-time to pre-commit is enormous. You're not trading accuracy — the same regex patterns work at both stages. You're trading when the pattern runs.
How to Set This Up
Most pre-commit secret scanning tools work the same way:
# Betterleaks (standalone)
betterleaks protect --staged
# trufflehog (standalone)
trufflehog filesystem --staged
# rafter (with git hook installation)
rafter agent install-hook --global
Rafter's agent install-hook sets up a global git pre-commit hook that runs secret scanning on every commit across all your repos. rafter agent init configures the full agent setup in one command.
The Takeaway
PR-time scanning is necessary. It catches architectural issues, code quality problems, dependency vulnerabilities, and the secrets that slip through other layers. Keep your PR scanner.
But for secrets — the one category where the finding itself is the breach — waiting until PR time is too late. The secret is already in history, already on a remote, already a rotation event.
Pre-commit scanning costs nothing, prevents the most expensive remediation scenarios, and is the only approach that works at the speed AI coding agents operate.
Shift left one more step. Your future self will thank you.