Scan Commits for Secrets Before They Reach main
Run gitleaks on every push and pull request, rotate anything it finds, and block merges until the history is clean.
Task
Detect committed credentials within seconds and stop the commit from being merged and deployed.
When to use this
A developer commits a .env file with a database password while debugging; the branch is pushed and nobody notices until a scanner alerts weeks later.
Prerequisites
- gitleaks 8.18 or newer
- Full Git history available in CI (fetch-depth: 0)
- An incident process for rotating leaked credentials
Steps
1. Scan the full history locally and redact matches in the output.
gitleaks detect --source . --redact --verboseExpected output: Exit code 0 on a clean repository; findings print the rule, file, and commit without exposing the secret.
2. Scan only the commits introduced by the pull request in CI.
gitleaks detect --source . --log-opts "origin/main..HEAD" --redact --exit-code 1Expected output: Only branch commits are scanned, so the job stays fast while still failing on a new secret.
3. Add the pre-commit hook so the secret never leaves the laptop.
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.21.2
hooks:
- id: gitleaks
Expected output: A commit containing a token is rejected before it is created.
4. Rotate the credential first, then remove the file from history.
git filter-repo --path .env --invert-pathsExpected output: The file disappears from history; the rotated credential is useless even if a copy survives.
Troubleshooting
Problem
A test fixture triggers a false positive
Solution
Add a narrow fingerprint or path allowlist in .gitleaks.toml with a comment explaining why, and never disable the rule globally.
Problem
CI passes while the local scan fails
Solution
The CI clone is shallow or uses a different commit range; set fetch-depth: 0 and scan the exact range you intend to protect.
Limitations
- Secret scanning matches patterns and entropy heuristics; it will miss secrets that look like ordinary text.
- Removing a secret from history does not revoke it; rotation is mandatory and must come first.
Secrets escape through ordinary commits
A .env file, a hardcoded token in a test, or a connection string pasted into a notebook all reach main through normal review because reviewers read logic, not strings. Once pushed, the secret is in every clone, every CI cache, and often every fork. Scanning at commit time is the only point where removal is cheap. Gitleaks checks the diff against a rule set of known credential formats plus entropy heuristics, and it can run locally, in a pre-commit hook, and in CI with the same rules.
Scan the range, fail the build
In CI, scan only the commits introduced by the branch. A full-history scan on every push gets slower as the repository grows and eventually gets skipped. Use --log-opts with the base branch range, keep --redact so logs do not become a second leak, and set --exit-code 1 so the job fails on a finding. Keep a scheduled full-history scan as a backstop for secrets that predate the gate.
gitleaks detect --source . --log-opts "origin/main..HEAD" --redact --exit-code 1
Rotate before rewriting
When a secret is found, rotate it first. History rewriting removes the text but not the copies, and a credential that was valid during the exposure window must be treated as compromised. Then purge the file with git filter-repo, force-push the affected branches, and invalidate CI caches. Finally, add a targeted allowlist entry only for the exact false positive, with a comment, so the rule stays active for everything else.