Securely Specifying GitHub Actions Action Versions — From Hash Values and Immutable Tags to Workflow-Level Dependency Locking
This page has been translated by machine translation. View original
In the tj-actions/changed-files compromise incident that occurred in March 2025 (CVE-2025-30066), version tags were rewritten, once again highlighting the importance of specifying actions securely.
This article explains the following two approaches that are already available as countermeasures:
- Commit hash
- Immutable tag
And also covers:
- Workflow-Level Dependency Locking
which is announced for release within 2026.
The Problem with Naive Specification
Specifications like uses: owner/action@v4 that appear in GitHub Actions samples are convenient, but they have structural problems on both the provider and consumer sides.
Provider perspective
- Git tags can be rewritten. They can be redirected to a different commit.
- Major version tags are dynamic. Major tags like
actions/checkout@v4move every time a new minor/patch is released.
Consumer perspective
- When the above happens, the reference target changes even though the version specification in the workflow file hasn't changed.
In short, specifications like uses: owner/action@v4 are convenient but not immutable.
There are officially two methods for creating references that cannot be substituted.
Pinning with a Commit Hash Value
This method involves writing @<Git commit SHA> directly. Git commit SHAs are guaranteed to be immutable.
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
Since the hash value alone doesn't tell you which version it is, this approach is paired with the practice of supplementing the version number in a comment.
Using tools like pinact, you can easily achieve version specification with hash values and comments.
Tools such as Dependabot and Renovate, which update dependencies, also support updating hash values.
There are also options to enforce pinning actions to commit SHAs at the organization or repository level.
- Verification of GitHub Actions Enforce SHA Pinning
- GitHub Actions: Enforce SHA Pinning Caveats and Current Best Practices
Note that for Impostor Commits, where commits are accumulated on a fork, it is necessary to use other tools such as zizmor in combination.
Using Immutable Tags
This method uses tags that support immutable releases, which GitHub made GA on October 28, 2025.
- uses: astral-sh/setup-uv@v8.1.0
At a glance, the configuration looks the same as before.
The list of releases for astral-sh/setup-uv shows the word "Immutable".

With immutable releases, a version once issued cannot be reused. If there is a problem with a release, it must be re-released as a new version, no matter how trivial the commit.
With the format v<Major>.<Minor>.<Patch>, providing major and minor versions like v8 or v8.0 creates a problem where version and resource are not in a 1:1 relationship.
For this reason, astral-sh/setup-uv and others have gone a step further, adopting a policy of not releasing major or minor versions from v8 onwards.
To increase security even more we will stop publishing minor tags. You won't be able to use @v8 or @v8.0 any longer. We do this because pinning to major releases opens up users to supply chain attacks like what happened to tj-actions.
When updating
- uses: astral-sh/setup-uv@v7
instead of
- uses: astral-sh/setup-uv@v8
you must specify down to the patch version like
- uses: astral-sh/setup-uv@v8.1.0
or it won't work.
To enable immutable releases, go to the repository's Settings → General → Releases → check "Enable release immutability".
Next-Generation Workflow-Level Dependency Locking
GitHub announced a security roadmap in late March 2026.
Workflow-Level Dependency Locking, scheduled for release by September 2026, follows a model similar to Go's go.mod (version) + go.sum (checksum), where the uses: section declares the version and the new dependencies: section declares the version + hash value (meaning mismatches between version and hash value can also be detected). Just as go.sum covers transitive dependencies, this feature is planned to also cover nested dependencies such as composite actions.
name: build-and-attest
on:
workflow_dispatch:
permissions:
contents: read
id-token: write
attestations: write
jobs:
build-and-attest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/attest-build-provenance@v3
with:
subject-path: README.md
dependencies:
- github.com/actions/checkout@v6.0.1:sha1-8e8c483db84b4bee98b60c0593521ed34d9990e8
- github.com/actions/attest-build-provenance@v3.1.0:sha1-00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8
- github.com/actions/attest@v3.1.0:sha1-7667f588f2f73a90cea6c7ac70e78266c4f76616
Closing
The following methods are available for specifying GitHub Actions actions securely.
- Hash value pinning like
actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - Using immutable tags like
astral-sh/setup-uv@v8.1.0(when supported by the provider) - A more robust mechanism similar to Go's go.mod + go.sum (Workflow-Level Dependency Locking) is planned for release within 2026
Since immutable release (tags) depend partly on the action provider, it is recommended to start with hash value pinning first if you want to prioritize consistency.