DevSecOps

Pre-Commit Hooks Security Recipes for 2026

Practical pre-commit framework recipes that catch secrets, malicious packages, and risky changes before they reach your remote, without slowing developers down.

Shadab Khan
Developer Advocate
6 min read

Pre-commit hooks are the cheapest place in the pipeline to enforce a security control. They run before code leaves the developer's machine, the feedback loop is measured in seconds, and they catch the boring categories of mistake that account for a depressing share of real incidents. The framework that has won this space is the Python-based pre-commit from Yelp, and its config format has become a de facto standard.

This is not a beginner's introduction to the tool. The audience here is the engineer who already has a .pre-commit-config.yaml and wants to know which hooks are worth adding in 2026, which to drop, and how to keep the whole thing fast enough that developers do not bypass it.

Which secret scanners belong in pre-commit?

The two candidates worth running locally are gitleaks and detect-secrets, and the right answer is usually both. gitleaks is a pattern matcher with a maintained ruleset that catches AWS keys, GitHub tokens, Stripe keys, and a long tail of cloud and SaaS credentials. It is fast enough to run on every commit without complaint. detect-secrets is entropy-based and catches the generic secrets that gitleaks patterns miss, things like high-entropy strings in JSON fixtures or YAML config. It produces more false positives, which is why it ships with a baseline file that lets you mark a finding as acknowledged.

A reasonable configuration runs gitleaks on staged diffs only, not full repo scans, and runs detect-secrets with its --baseline flag pointing at a checked-in .secrets.baseline. The baseline file should be updated through a deliberate audit step rather than auto-regenerated, because that is where developers will be tempted to silence findings without looking at them. The 2023 CircleCI incident, where a compromised employee laptop leaked customer secrets, is a reminder that local secret hygiene is not a hypothetical concern.

How do you keep hooks fast enough that developers do not bypass them?

The single most important property of a pre-commit setup is that it stays under about three seconds for a typical commit. Past that, developers learn the --no-verify flag and your controls evaporate. The two main levers are limiting hooks to changed files, which the framework does by default if you let it, and avoiding hooks that need to spin up a heavy runtime.

Hooks that compile a project or run a full lint pass do not belong in pre-commit. Move those to pre-push, where the latency budget is larger and developers expect a longer wait. Hooks that scan only staged files, like formatters, linters with incremental modes, and pattern-based scanners, are the right fit. If a hook needs a Docker image, pin it and use the language: docker_image entry so pre-commit caches it. Run pre-commit run --all-files in CI to catch the cases where a local hook was skipped.

What about dependency review in pre-commit?

Dependency review at the pre-commit stage is where teams get the most leverage for the least friction. A hook that runs on changes to package.json, requirements.txt, go.mod, or Cargo.toml and rejects additions of packages on a known-malicious list is straightforward to implement and catches a category of attack that has become routine. The ua-parser-js incident in 2021 and the eslint-scope poisoning in 2018 both involved compromised maintainer accounts pushing malicious versions to npm, and both would have been caught by a hook that checked new versions against a freshness threshold.

A reasonable rule is to reject any package version published in the last seven days unless an exception is logged. This blocks the typical typosquat and account-takeover window. The osv-scanner tool from Google has a pre-commit mode that handles the vulnerability check side. Combine it with a custom hook that calls the npm or PyPI API for publication date, and you have a meaningful supply chain control at the developer's keyboard.

How should you handle hook updates and pinning?

Pre-commit hooks themselves are a supply chain. The .pre-commit-config.yaml references third-party repositories, and the framework pulls and executes code from those repos on every developer machine. Pinning by tag is the default, and it is not enough. A compromised maintainer can re-tag a release to point at malicious code, and pre-commit autoupdate will silently pick it up the next time a developer runs it.

Pin every hook to a full commit SHA, not a tag. The pre-commit autoupdate --freeze flag does this automatically. Review hook updates the same way you review any other dependency bump: read the diff, check the maintainer, and prefer hooks from well-known organizations over individual maintainers. The Apple xz Utils incident in 2024 is the canonical example of why this matters, where a long-trusted maintainer turned out to be a long-running social engineering operation.

What hooks have stopped being worth running?

Several hooks that were defaults a few years ago have aged out. The Python bandit hook in pre-commit produces too many false positives on modern codebases to be worth the developer interruption; it belongs in CI with a tuned config, not on the laptop. Generic eslint --fix hooks have largely been replaced by editor-integrated LSP fixes that run on save. Trailing-whitespace and end-of-file hooks are still fine but their security value is zero, so do not count them toward your control coverage.

What has moved up the priority list is hooks that catch IaC misconfigurations, specifically checkov or tflint running against Terraform changes, and hooks that validate Dockerfile bases against an approved list. The latter is a five-line custom hook and it prevents the common mistake of pulling a base image from a developer's personal namespace into production code.

How Safeguard Helps

Safeguard treats pre-commit as the first layer of a defense-in-depth supply chain control set, not the only one. Our policy gates run in CI and re-check everything a pre-commit hook claimed to enforce, because local hooks can be bypassed with --no-verify and you should not trust them alone. Griffin AI cross-references new dependency additions against our zero-day feed and TPRM scoring, so a package that passed a freshness check on the laptop can still be blocked at PR time if a fresh advisory drops. Reachability analysis on the resulting SBOM tells you which of the dependencies you pulled in actually matter for your service. The pre-commit layer catches mistakes; the platform catches the attacks that mistakes invite.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.