How to Set Up Pre-Commit Secret Scanning
Cleaning secrets out of git history is painful, slow, and involves force-pushing, which nobody enjoys. Preventing them from entering history in the first place takes about forty minutes, once, and then works forever. That is the trade this tutorial makes.
You will set up the pre-commit framework with gitleaks so that every commit is scanned for credentials before it is created. A secret that never reaches a commit never needs rotating, purging, or an awkward incident channel. If you have not yet checked whether your history is already contaminated, do a full repo scan first; hooks protect the future, not the past. The background reading is exposed secrets in your repo.
What you’ll need
- A git repository you can commit to
- Python and pip (the pre-commit framework is a Python tool, but it manages hooks for any language)
- About 40 minutes
1. Install the pre-commit framework
You could write a raw shell script in .git/hooks/pre-commit, but raw hooks are not versioned, not shared with your team, and silently absent on fresh clones. The pre-commit framework fixes all three: the hook definition lives in a committed YAML file, and each developer activates it once.
pip install pre-commit
# or on macOS
brew install pre-commit
pre-commit --versionThe framework reads a config file from your repo root, installs the tools each hook needs into an isolated environment, and wires everything into git for you.
2. Add gitleaks as a hook
Create .pre-commit-config.yaml in the repository root:
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.4
hooks:
- id: gitleaksTwo things worth understanding here. The rev pins an exact gitleaks release, so every developer runs the same rules and an upstream change cannot surprise you. And the published gitleaks hook runs in protect mode against staged changes, which is exactly what you want at commit time: fast, scoped to the diff, no full-history walk.
Now activate it in your clone:
pre-commit installThis writes the shim into .git/hooks/pre-commit. From now on, git commit runs gitleaks first and refuses to proceed if it finds a secret in the staged changes.
3. Test the hook with a fake secret
Never trust a safety system you have not seen fail. Stage a fake credential and try to commit it:
echo 'aws_access_key_id = "AKIAIOSFODNN7EXAMPLE"' > leak-test.txt
git add leak-test.txt
git commit -m "test: should be blocked"The commit should fail with a gitleaks finding pointing at the AWS key pattern. If it goes through, the hook is not installed; run pre-commit install again and check that you are in the right repo.
Clean up:
git restore --staged leak-test.txt
rm leak-test.txtYou can also run the scan across the whole repository on demand, which is useful right after setup:
pre-commit run --all-files4. Handle false positives with an allowlist
Within a week, someone will hit a false positive: a dummy key in a test fixture, a sample token in documentation, a high-entropy string that is just a hash. If the hook cries wolf, developers will start bypassing it, and a bypassed hook is worse than no hook because it creates false confidence.
Create a gitleaks.toml in the repo root that extends the default rules and allowlists the known-safe cases:
[extend]
useDefault = true
[allowlist]
description = "Known safe test fixtures and docs"
paths = [
'''tests/fixtures/.*''',
'''docs/examples\.md''',
]
regexes = [
'''AKIAIOSFODNN7EXAMPLE''',
]Gitleaks picks up gitleaks.toml from the repo root automatically. Keep the allowlist tight and reviewed: every entry is a hole in the net, so each one should be a path or a specific known-fake value, never a broad pattern like “anything in src”.
The escape hatch for a genuine emergency is git commit with the SKIP=gitleaks environment variable set, which skips only this hook and leaves an obvious trace in your shell history. Treat its use as a code review question, not a habit.
5. Roll it out to the whole team
The config file is only half the rollout; the other half is people. Commit the two files:
git add .pre-commit-config.yaml gitleaks.toml
git commit -m "chore: add pre-commit secret scanning"Then handle the one-time activation on each machine. Add it to your README and onboarding doc:
pip install pre-commit && pre-commit installIf your project has a bootstrap script (make setup, npm run prepare, a devcontainer), put pre-commit install in it so new clones are protected without anyone remembering anything. Hooks that depend on memory fail exactly when someone is rushed, and rushed commits are where secrets leak. This kind of process hardening is a standard line item in our security review checklist template.
6. Add a CI backstop
Local hooks can be skipped, uninstalled, or missing on a brand new laptop. The backstop is running the same scan server-side, where nobody can forget it. For GitHub Actions, create .github/workflows/gitleaks.yml:
name: gitleaks
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}The fetch-depth: 0 matters: it fetches full history so the scan covers every commit in the push, not just the tip. With the hook catching secrets at commit time and CI catching anything that slips past, a credential now has to defeat two independent layers to reach your default branch. That is the difference between “we ask people to be careful” and an actual control.
Get a second pair of eyes (free)
Pre-commit scanning closes one door. An auditor looks at the whole house: how secrets are stored and injected at runtime, whether tokens expire, what your CI logs leak, and everything in the free code audit checklist. Once your hooks are in place, get a free code audit and a Webisoft engineer will manually review your repository and send you a prioritized report of what to fix next. It is free, and it is a human reading your code, not another scanner.