How to pin GitHub Actions to a commit SHA and keep them updated
How to pin GitHub Actions to a full commit SHA, keep pins fresh with Dependabot, Renovate or pinact, and enforce SHA pinning with GitHub policy.
To pin a GitHub Action to a commit SHA, replace the tag or branch after the @ in your uses: line with the action's full 40-character commit hash, and keep the human-readable version in a comment. GitHub's own secure use guidance recommends this because tags can be moved or deleted, while a commit hash cannot be quietly pointed at different code.
Pinning only helps if the pins are refreshed. The second half of this guide covers Dependabot, Renovate and command-line tools that keep SHAs current, and how an organisation can make unpinned actions fail. Details are as of October 2026.
Why tags and branches are not safe references
A line such as uses: actions/checkout@v5 asks GitHub to resolve v5 every time the workflow runs. A tag is a pointer, and whoever controls the action's repository can move it to a different commit. A branch name such as main moves with every push.
That matters because a workflow step runs with access to whatever the job has: the repository contents, the GITHUB_TOKEN and any secrets exposed to the job. If the code behind a tag changes, your next run executes the new code without a pull request on your side.
This is not hypothetical. In March 2025 the third-party action tj-actions/changed-files was compromised, tracked as CVE-2025-30066. CISA's alert warned that the compromise could disclose secrets such as access keys, GitHub personal access tokens, npm tokens and private RSA keys, and it listed a related reviewdog/action-setup compromise, CVE-2025-30154. Workflows that referenced the action by a moving tag picked up the malicious version. Workflows pinned to an earlier full SHA kept running the code they had been reviewed with.
For a recent example of what happens when a workflow ends up pointing at bad code, see our report on GitHub Actions left pointing to malicious code after re-enablement without cleanup in the Security section.
How to pin an action to a commit SHA
The result should look like this:
uses: actions/checkout@<40-character-sha> # v5.0.0
The trailing comment is not read by GitHub. It exists for people and for update tools, which use it to show which release the hash corresponds to.
Manual steps
- Open the action's repository on GitHub and go to its releases or tags page.
- Pick the release you want and copy the full 40-character commit hash, not the 7-character abbreviation.
- Replace the tag in your workflow with that hash and add the version as a comment.
- Confirm the commit belongs to the action's own repository and not to a fork. GitHub's documentation specifically tells you to verify this when you choose a SHA.
Note that for annotated tags, the tag object has its own hash that differs from the commit it points to. Use the commit hash, which is what the workflow needs.
Why SHA-1 is still acceptable here
GitHub's reasoning is that an attacker would have to produce a SHA-1 collision for a valid Git object payload to substitute different code under the same hash. That is considered impractical for this use, so a full-length hash is treated as an immutable reference.
Pinning does not remove the need to read the code
A SHA fixes what runs. It does not tell you the code is safe. GitHub advises auditing an action's source to confirm it handles your repository contents and secrets as you expect, whichever way you reference it.
Two further points are easy to miss:
- Transitive dependencies. A composite action may itself call other actions by tag. Pinning the outer action does not pin what it calls. GitHub's documentation notes that workflow dependency lockfiles, for repositories that can adopt them, record exact commits including transitive dependencies and supersede SHA pinning.
- Vulnerability alerts. According to GitHub's guidance, Dependabot does not create alerts for vulnerable actions that are pinned to SHA values. You have to rely on update pull requests and your own review, so do not let pins go stale.
Keep pinned actions up to date
A pinned workflow that nobody updates slowly collects known bugs. Three common ways to automate the refresh:
Dependabot
Dependabot version updates support the github-actions ecosystem. Add a .github/dependabot.yml with package-ecosystem: "github-actions", directory: "/" and a schedule such as weekly, as described in the Dependabot documentation. It then opens pull requests for outdated action references. One caveat from GitHub's secure use page: if a pinned commit has no associated tag, Dependabot moves it to the latest commit, which may not be the latest release. Pin to commits that correspond to tagged releases to avoid that.
Renovate
Renovate ships a preset, helpers:pinGitHubActionDigests, which turns on digest pinning for GitHub Actions and reusable workflows. Add it to the extends list in your Renovate configuration and it will rewrite tag references to hashes, then keep them updated. See the Renovate helper presets.
Command-line tools
pinact is a CLI that edits workflow and composite action files to pin actions and reusable workflows to commit SHAs. Its README shows an example in which a tag becomes a hash with the old tag kept as a comment. Running pinact run with no argument pins the workflow files in the repository, and pinact run --check exits with a non-zero code if something still needs pinning, which makes it usable as a CI check. It can also update versions and verify that version comments are accurate. Other tools in the same space include ratchet and the workflow linters zizmor and StepSecurity, which flag unpinned references; check each project's documentation for current options.
Enforce SHA pinning across an organisation
Since GitHub's August 15, 2025 changelog entry, the allowed actions and reusable workflows policy can require full-SHA pinning. A checkbox appears under each selection in the policy settings, except when Actions is disabled at the enterprise, organisation or repository level. When the policy is on, a workflow that uses an action not pinned to a full commit SHA fails.
The same update lets administrators block specific actions or versions by prefixing an entry with !. The blocklist is evaluated last, so it overrides rules that would otherwise allow the action. That gives you a fast way to stop a known-bad action across all repositories while you investigate.
The changelog does not state which GitHub plans include these controls, so check the settings page for your enterprise, organisation or repository before planning a rollout. Test the policy on one repository first: any workflow that references a tag will start failing, including those inherited from templates.
Related references worth pinning
- Reusable workflows. A uses: org/repo/.github/workflows/build.yml@v2 reference has the same mutable-tag problem and can be pinned the same way.
- Container images. Docker steps and job containers can be referenced by digest (image@sha256:...) instead of a tag.
- Immutable releases. GitHub's immutable releases lock a release's Git tag and assets after publication, and the tag cannot be moved or deleted while the release exists. This helps only when an action's maintainer enables it, so you cannot count on it for third-party actions. Check the action's releases page rather than assuming it.
A short checklist
- Pin every third-party action and reusable workflow to a full 40-character commit SHA from the official repository.
- Keep the version as a trailing comment.
- Turn on Dependabot or Renovate so pins are refreshed through reviewable pull requests.
- Add a CI check, for example pinact run --check, so unpinned references do not slip back in.
- Where you have admin rights, enable the SHA pinning policy and keep a blocklist ready.
- Limit job permissions and secrets, since pinning does not reduce what a trusted action can access.
For more on CI and build tooling, browse DevOps tooling and DevOps platforms, and for incident coverage see Breaches.
Frequently asked questions
Should I pin GitHub Actions to a SHA or a tag?
A full commit SHA is the only reference that cannot be moved after you review it, so GitHub recommends it for third-party actions. Pin to a tag only for creators you trust, since tags can be changed if the action's repository is compromised.
Does Dependabot update SHA-pinned actions?
Yes, Dependabot version updates cover the github-actions ecosystem and open pull requests for outdated references. If a pinned commit has no matching tag, it may move to the latest commit rather than the latest release.
How do I enforce SHA pinning in a GitHub organisation?
Use the allowed actions and reusable workflows policy and enable the SHA pinning option. Workflows that use an action not pinned to a full commit SHA then fail.
What does pinact do?
pinact is a CLI that rewrites workflow files so actions and reusable workflows use commit SHAs, keeping the old version as a comment. Its check mode can fail a CI job when unpinned references remain.
Is a short SHA enough to pin an action?
No. Use the full 40-character hash, which is what the policy checks for and what GitHub's guidance describes.