A gitleaks pre-commit hook stops a secret from ever reaching your git history by scanning staged changes before the commit completes. The setup is a .pre-commit-config.yaml file plus the pre-commit framework, but if you copy a command from an older tutorial it will fail silently or not run at all, because gitleaks changed its command structure in v8.19.0 and most guides online still show the old syntax.
Wiring gitleaks into pre-commit
You need the pre-commit framework installed first — it’s what actually runs hooks on every commit attempt; gitleaks itself is just one hook definition. With pre-commit installed, add this to a .pre-commit-config.yaml at your repository root, following the configuration documented in the gitleaks repository:
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.24.2
hooks:
- id: gitleaks
Then run two commands once per clone:
pre-commit autoupdate— pins the hook to the latest tagged gitleaks releasepre-commit install— actually registers the hook in.git/hooks; skipping this step is the single most common reason the hook “doesn’t work” after a fresh clone, since the config file alone does nothing until it’s installed
The command changed in v8.19.0 — most tutorials still show the old one
Gitleaks deprecated its detect and protect subcommands as of v8.19.0; they still run but are hidden from --help. If you’re scripting a manual scan outside the pre-commit framework — in a custom Husky hook, for example — the commands you need now are different from what most existing tutorials show.
| What you want to do | Old command (deprecated) | Current command |
|---|---|---|
| Scan full git history | gitleaks detect --source={repo} | gitleaks git {repo} |
| Scan unstaged changes before commit | gitleaks protect --source={repo} | gitleaks git --pre-commit {repo} |
| Scan only staged changes | gitleaks protect --staged --source={repo} | gitleaks git --pre-commit --staged {repo} |
| Scan a directory with no git history | gitleaks detect --no-git --source={repo} | gitleaks directory {directory/file} |
| Scan piped input | gitleaks detect --no-git --pipe | gitleaks stdin |
If you’re using the official pre-commit hook config above, you don’t need to run these manually — pre-commit calls the right one internally. They matter when you’re writing a custom script, a CI step, or a non-pre-commit git hook by hand, where copying the old protect --staged syntax from a guide will either error out or quietly no-op depending on your installed version.
What the hook actually blocks
Gitleaks matches staged content against a built-in ruleset of regex patterns for things like AWS access keys, Stripe secret keys, and generic high-entropy strings assigned to variables named like api_key or token. This is exactly the kind of pattern an AI coding assistant can introduce without anyone noticing — a hardcoded key pasted into a generated config example, or a placeholder credential that was real in the assistant’s suggestion and got committed verbatim. When a match is found, the commit is rejected and gitleaks prints the file, line, and matched rule to the terminal; nothing is committed until the match is removed or explicitly allowlisted.
Allowlisting a false positive without disabling the rule
Some matches are false positives — a test fixture with a fake key, or a hash that happens to look high-entropy. Gitleaks’ own configuration format supports scoping an allowlist to specific commits or paths rather than turning the rule off everywhere, documented in its README:
[[rules.allowlists]]
description = "ignore known test fixtures"
condition = "OR"
paths = [
'''test/fixtures/.*''',
]
Put this in a .gitleaks.toml at your repo root. Scoping the allowlist by path, as shown here, is safer than disabling a rule globally, because a global disable also stops catching that same pattern in files the fixture exclusion was never meant to cover.
Why this matters more when an AI assistant is writing the code
An AI coding assistant completes patterns from the surrounding context, and config files, example .env snippets, and onboarding docs are exactly the kind of context that sometimes contains a real credential a teammate pasted in once and forgot about. The assistant isn’t leaking anything on its own — it’s suggesting plausible-looking code the same way it would for any other pattern, and a hardcoded key in a suggestion looks identical to a hardcoded key a human typed by hand. A pre-commit hook doesn’t care who or what produced the line; it only cares whether the line matches a known secret pattern before it’s staged for commit, which makes it one of the few checks that’s equally effective regardless of whether a human or an assistant wrote the code.
How this differs from GitHub’s own secret scanning
GitHub has its own secret scanning, but it works differently from a local pre-commit hook, and the two are complementary rather than redundant. Per GitHub’s documentation, standard secret scanning examines repository history across branches, issues, pull requests, and wikis after content has already been pushed, generating a security alert when it finds a match. GitHub’s separate push protection feature is the one that blocks a push proactively — but it isn’t available or enabled on every plan and repository, and either way it only runs at push time, after a commit already exists locally.
A gitleaks pre-commit hook runs earlier still — before the commit is even created — so a secret never makes it into local git history in the first place, not just the remote’s. That matters because removing a secret from a later commit doesn’t remove it from the commits before it; anyone who already pulled or cloned the repo in between still has it. Catching the match at commit time, on the developer’s own machine, is the only point in the pipeline where the secret genuinely never gets recorded anywhere.
Common errors and how to fix them
- Error: the hook never runs after cloning the repo. Cause:
pre-commit installwas never run on this machine — the config file is just data until that command registers it. Fix: runpre-commit installonce per clone; it isn’t tracked by git and doesn’t carry over automatically. - Error: “command not found: gitleaks” in CI but not locally. Cause: the pre-commit framework downloads and caches gitleaks itself via the hook’s
repoandrevfields, but a CI runner with network restrictions may block that download. Fix: pre-warm the pre-commit cache in your CI image, or install gitleaks directly in the runner and call it without going through the pre-commit framework. - Error: a real secret was committed before the hook was set up. Removing it from the latest commit does not remove it from history — it’s still readable in every earlier commit. Fix: rotate the credential immediately at the provider, then separately deal with purging history; per the OWASP Secrets Management Cheat Sheet, treating a leaked secret as compromised and rotating it is the reliable fix — history rewriting alone doesn’t undo exposure if the repo was ever pushed or cloned.
- Error: old
gitleaks protect --stagedscripts stop working after an update. Cause: upgrading past v8.19.0 hid the deprecated subcommands further. Fix: swap ingitleaks git --pre-commit --stagedper the table above, and search any custom CI scripts or Husky hooks for the old syntax rather than assuming the pre-commit framework config is the only place it appears. - Error: the hook flags something on every single commit in a noisy repo. Cause: usually a config file or seed script that legitimately contains placeholder-looking values. Fix: scope a path-based allowlist as shown above instead of disabling the hook, so the rest of the repo stays protected.
This hook is one layer, not the whole defense. It catches what gets staged locally, which pairs well with scanning pull requests in your CI/CD pipeline as a second check, and with treating AI-suggested code the way you’d treat any other code review — reading what a coding assistant generated before it’s staged, not after.
Where this fits with other safeguards
A secrets-scanning hook is a narrow, mechanical guardrail — it’s one example of the broader category covered in what an AI guardrail actually is. It doesn’t replace reviewing AI-suggested changes before merging, the same discipline that matters when refactoring legacy code with AI assistance or when writing database migration scripts — both places where a plausible-looking but wrong suggestion is more dangerous than an obviously broken one.
FAQ
Does the hook still run if someone commits through a GUI git client instead of the terminal?
Yes — pre-commit hooks are a git mechanism, not a terminal feature, so any client that invokes the standard git commit flow (GitHub Desktop, your editor’s built-in git panel, and so on) triggers the same hook. The one exception is a client or script that bypasses hooks explicitly with a flag like --no-verify, which is why relying on the hook alone, without also scanning in CI, leaves a gap for anyone who passes that flag.
Will this noticeably slow down every commit?
Gitleaks only scans the diff of staged changes, not the full repository, so the scan time scales with how much you changed in that commit rather than the size of the codebase. A typical commit of a few modified files adds a small, usually sub-second check; the main cost is the one-time download when pre-commit first sets up the gitleaks hook in its cache.



