Safeguard
Supply Chain•

Pin pre-commit hook revisions and update them on purpose

Pre-commit hooks run third party code on every commit. Pin each rev to a tag or SHA, review autoupdate changes, and consider the freeze option for hashes.

Safeguard Security Team
3 min read

Every repo in a pre-commit config is code you run on developers' machines at each commit. Pin each rev to an immutable tag or commit SHA, update with pre-commit autoupdate as a reviewed change, and consider the freeze option to record exact hashes.

Hooks are dependencies too

A line in .pre-commit-config.yaml points at a git repository and a rev. The framework clones it and runs the hook. That makes it a dependency in the same sense as a package, with the same question: what happens if the thing at that address changes?

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v6.0.0
    hooks:
      - id: detect-private-key

What the docs say about rev

The pre-commit documentation says to pin rev to an immutable ref such as a tag or commit SHA. A branch name or HEAD is not supported, since it reflects the branch only at install time and does not update on its own. So a floating ref gives you the worst mix: unpredictable code and no automatic refresh.

A tag is readable. A commit SHA is stronger, because tags can in principle be moved by whoever controls the repo, while a full SHA cannot point at different content.

Updating on purpose

pre-commit autoupdate moves each repo's rev to the latest tag on the default branch. Three notes from the docs:

  • The bleeding edge option moves to the latest commit instead of the latest tag. Avoid it for anything you rely on.
  • The freeze option stores commit hashes in rev and keeps the tag name as a comment, which gives you readable and immutable at once.
  • The hosted pre-commit.ci service can open update pull requests periodically, if you use it.

Treat the resulting diff like any dependency bump. Look at the changelog of the hook repo, and note which hooks changed behavior.

A review routine that fits in ten minutes

  1. Run the autoupdate command with its freeze option on a branch (shown below).
  2. Read the diff. New revs only, no new repos.
  3. Run every hook against every file and see what now fails.
  4. Open a pull request with a CODEOWNERS owner for the config file.

Step two matters. An update that adds a new repo entry is not an update, it is a new dependency, and deserves a closer look.

pre-commit autoupdate --freeze
pre-commit run --all-files

Local hooks

Hooks declared with the local sentinel run code that lives in your own repo, so there is no rev to pin. Review them as normal code.

Do not forget the extras

Some hooks install further packages through additional_dependencies. Pin those versions too, or the pinned hook still pulls floating code.

Dependency scanning does not usually look inside hook environments. Safeguard scans your application dependencies and SBOMs, so keep that coverage separate from your hook config. A free scan is available at gold.safeguard.sh/scan.

Quick check

Is a tag good enough for rev?

It is accepted and readable. A commit SHA is stronger because it cannot be repointed. The freeze option gives you the SHA with the tag kept as a comment.

Sources

Last reviewed: 19 October 2026.

Never miss an update

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

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.