← All posts

Stop using @v4: pin your GitHub Actions to a commit SHA

Almost every GitHub Actions workflow starts the same way:

- uses: actions/checkout@v4

It looks fixed. It isn’t. v4 is a git tag, and a tag is just a pointer that the owner of the repository can move to a different commit at any time.

Why that’s a problem

If the account of an action’s maintainer gets compromised, an attacker can push malicious code and move the tag to it. The next time your workflow runs, it executes that code, inside your CI, with your secrets and tokens in the environment. You didn’t change a single line.

This isn’t theoretical. In March 2025 the popular tj-actions/changed-files action was compromised and its version tags were pointed at a malicious commit that dumped CI secrets into the build logs (CVE-2025-30066). Thousands of repositories were using it by tag.

The fix

Reference the full 40-character commit SHA instead of the tag, and keep the version in a comment so humans can still read it:

- uses: actions/checkout@<full-40-character-commit-sha> # v4.x.x

A commit SHA can’t be moved. Your workflow now runs exactly the code you (or someone you trust) reviewed.

But then I never get updates?

You still do, just on purpose. Dependabot understands SHA-pinned actions and opens a pull request when a new release comes out, updating both the hash and the version comment. Add this to .github/dependabot.yml:

version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"

Now every update is a diff you can review before it runs.

Quick checklist

  • Pin third-party actions to a full commit SHA, not a tag or a branch.
  • Keep the version as a comment next to the SHA.
  • Let Dependabot or Renovate propose updates as pull requests.
  • Give the workflow token the minimum permissions: it needs.