Similar to GitHub Actions, the pre-commit ecosystem can be both immensely powerful and dangerous, since it, by design, allows for arbitrary code execution. In the spirit of Astral's recent blog post on OSS security, I wanted to flag two potential supply-chain hardening opportunities regarding this repo's usage of prek.
-
Use immutable pre-commit hook refs
- The pre-commit ecosystem uses Git refs as its sole versioning system. Unfortunately, this means that mutable refs can be used as an attack vector and force-pushed with malicious code in the event of repo/maintainer compromise (again, similar to GitHub Actions).
- This project currently uses mutable tag refs in all 11 of its remote pre-commit hooks. These can be pinned to immutable commit SHAs; both prek and pre-commit respect SHA refs, and Renovate updates SHA-pinned hooks alongside their version comments, such that the existing Renovate config keeps working with no edits.
- Since the revs are already current, the following command can pin SHAs without upgrading any dependencies:
prek auto-update --freeze --cooldown-days 14. The --freeze flag rewrites each rev to the SHA the tag currently points at (appending the tag as a comment), and --cooldown-days 14 will prevent any actual version updates in the same pass.
-
Use pinned prek version in CI workflows
- In addition to pinning the prek/pre-commit hooks, it's likely also worthwhile to pin prek itself. The
prek CI job currently uses an unpinned uvx prek command, which resolves the latest prek version from the package index at execution time.
- This could be remediated by a couple of approaches:
- Adding an inline version pin (i.e.
uvx prek==<version>). This is the lowest-friction and most self-contained option; prek is a standalone Rust binary with no Python package dependencies (similar to uv/ruff/ty), so a single version constraint fully pins its dependency tree.
- Adding
prek to a relevant PEP 735 dependency group in pyproject.toml and resolving in CI via uv.lock.
- Switching to
j178/prek-action, which supports pinning the prek version via its prek-version input. This could also eliminate some extra plumbing the CI job is currently doing (e.g., caching), but I would understand hesitancy to add a new third-party action.
Note: suggestion 1 above also applies to the pre-commit config for uv and ty as well. Suggestion 2 applies to only ty's CI; it appears that uv does not run prek in CI directly, since all hooks are covered by other jobs.
Happy to put up a PR for either :^) Thanks!
Similar to GitHub Actions, the pre-commit ecosystem can be both immensely powerful and dangerous, since it, by design, allows for arbitrary code execution. In the spirit of Astral's recent blog post on OSS security, I wanted to flag two potential supply-chain hardening opportunities regarding this repo's usage of prek.
Use immutable pre-commit hook refs
prek auto-update --freeze --cooldown-days 14. The--freezeflag rewrites eachrevto the SHA the tag currently points at (appending the tag as a comment), and--cooldown-days 14will prevent any actual version updates in the same pass.Use pinned prek version in CI workflows
prekCI job currently uses an unpinneduvx prekcommand, which resolves the latestprekversion from the package index at execution time.uvx prek==<version>). This is the lowest-friction and most self-contained option; prek is a standalone Rust binary with no Python package dependencies (similar to uv/ruff/ty), so a single version constraint fully pins its dependency tree.prekto a relevant PEP 735 dependency group inpyproject.tomland resolving in CI viauv.lock.j178/prek-action, which supports pinning the prek version via itsprek-versioninput. This could also eliminate some extra plumbing the CI job is currently doing (e.g., caching), but I would understand hesitancy to add a new third-party action.Note: suggestion 1 above also applies to the pre-commit config for uv and ty as well. Suggestion 2 applies to only ty's CI; it appears that uv does not run prek in CI directly, since all hooks are covered by other jobs.
Happy to put up a PR for either :^) Thanks!