Best practice · Security

Pin GitHub Actions to commit SHAs

Reference every third-party action by its full 40-character commit SHA (with the version as a trailing comment), not a mutable `@v4` tag or `@main` branch that its maintainer, or an attacker, can re-point.

Do this

Third-party actions are pinned to an immutable 40-character commit SHA, with the human-readable version kept as a trailing comment so Dependabot and Renovate still track upgrades. A compromised upstream tag can no longer silently change the code that runs under your workflow's secrets and GITHUB_TOKEN.123

Pinned to an immutable SHA
steps:
  # Pin to the 40-char SHA of the release you use; keep the version as a
  # comment. Run `pinact run` on your @v-pinned file to fill these in.
  - uses: actions/checkout@<40-char-sha>            # v4.2.2
  - uses: pnpm/action-setup@<40-char-sha>           # v4.1.0

Avoid this

A re-pointed tag runs new code under your secrets and GITHUB_TOKEN on the next CI run.

Mutable tags and branches
steps:
  - uses: actions/checkout@v4        # tag, mutable, can be re-pointed
  - uses: some-org/some-action@main  # branch, anyone with push can change it

Why it matters

A tag like @v4 is a moving pointer. If the action's maintainer account is compromised, the attacker re-points the tag at malicious code and your next CI run executes it with full access to your secrets. This is not hypothetical, the tj-actions/changed-files compromise (March 2025, CVE-2025-30066) worked exactly this way. Pinning to a SHA makes the code immutable; a branch ref like @main is the worst case and should be pinned or vendored.

When to use

Use it when

Every third-party action reference. First-party actions/* are lower-risk but still worth pinning for a consistent policy.

Be careful when

Pinning has no real downside beyond upgrade friction, which Dependabot/Renovate handle via the version comment. For an action that only ships a main branch, vendor it (fork and pin your own SHA) or push upstream to cut releases.

Verify on your repo

Hand this prompt to your coding agent (Claude Code, Cursor, and the like) to audit and fix this practice in your own repo.

Prompt for your coding agent
Inspect this repo's .github/workflows for third-party actions referenced by a mutable ref: `uses:` lines ending in `@v1` / `@v4` / `@main` / `@master` rather than a full 40-character commit SHA. For each, pin it to the immutable 40-char SHA of the release currently in use and keep the human-readable version as a trailing comment (`@<sha> # v4.2.2`) so Dependabot and Renovate still track upgrades; running `pinact run` performs this rewrite for you. Show me the diff and open a PR rather than applying it blindly.
Prefer to check by hand?
  1. Scan for unpinned references: look for uses: lines ending in @v1/@v2/@main/@master rather than a 40-char hex SHA.

  2. Convert them in one shot with `pinact` (pinact run), which rewrites @v refs to SHAs and preserves the version as a trailing comment.

  3. For stronger enforcement, run `zizmor` in CI: its unpinned-uses audit catches tag-pinned actions and impostor-commit catches SHAs that don't exist in the action's history.

More best practices for GitHub Actions

Where to go next in the CI best-practices catalog.

All CI best practices

FAQ

Do I need to pin first-party actions like actions/checkout?

The immediate risk is highest for third-party actions, but pinning everything gives you one consistent, auditable policy, and tools like `pinact` and `zizmor` treat the whole uses: set uniformly. Pinning first-party actions too is the safer default.

Won't pinning to a SHA break my automated updates?

No, keep the version as a trailing comment (@<sha> # v4.2.2). Dependabot and Renovate read that comment and still open upgrade PRs, so you get immutability and updates.

Sources

1GitHub · security hardening (pin actions to a full-length SHA) (opens in new tab)

2pinact (opens in new tab)

3zizmor · audits (opens in new tab)

Last updated 2026-07-06

Get started

Pin every action to a commit you control.

One line to install. Faster runs on day one, and agents that open reviewable PRs to keep your pipeline following practices like this one.