In March 2025, the tj-actions/changed-files action was compromised in a way that affected an estimated 23,000 workflows: the attacker rewrote the action's tagged releases to point at malicious code that exfiltrated GitHub runner secrets. The incident is the canonical example of why mutable tag references in GitHub Actions are a supply-chain weakness, and it directly informed the 2026 Actions security roadmap that GitHub published in early 2026. The centerpiece of that roadmap is the General Availability of Immutable Actions, which distributes actions as OCI artifacts in GitHub Packages with hash-based identity rather than mutable Git tags. This post walks through what the GA actually changes from a defender perspective, how to migrate, and what policy gates make sense once the feature is broadly available.
What did GitHub change with Immutable Actions?
The Immutable Actions model treats published action versions like container images. A maintainer publishes an action version, and GitHub stores it as an immutable OCI artifact at pkg.actions.githubusercontent.com, addressed by both a semantic version and a digest. The crucial property is that the digest never changes for a published version; a maintainer who wants to "fix" a bad release publishes a new version rather than rewriting a tag. The Actions runner ships with hash-mismatch fail-fast behavior: if the resolved digest at execution time does not match the digest recorded for the version reference, the run stops before any user-supplied code executes. Composite actions are unrolled with full visibility, so when an action uses another action internally, the consumer sees the entire dependency tree in the workflow's resolved dependency graph rather than the previous opaque "trust me" model.
How did the response to tj-actions inform the design?
The tj-actions response gave the Actions team an unusually clear lesson set. The first lesson was that mutable tags are a single point of failure: the attack succeeded because the maintainer's compromised credential could rewrite every existing version reference at once. The second was that composite actions hide blast radius: many downstream users of tj-actions did not realize that the action invoked other community actions internally, so the surface area of their exposure was larger than the workflow file suggested. The third was that SHA-pinning, while widely recommended, is incomplete: a user pinned to a SHA was protected against the tag rewrite but still had no signal that the dependency chain underneath the pinned action might be mutable. Immutable Actions addresses all three: hash-based identity makes tag rewrites moot, OCI distribution gives composite dependencies their own immutable identities, and the resolved-dependency graph surfaces the full tree.
What signals can a consumer read once Immutable Actions is GA?
Three signals appear in the workflow run UI and the API. The first is the resolved digest for every action version used in the run, surfaced alongside the human-readable version reference. The second is the composite-action expansion view, which lists every nested action and its digest. The third is the action's OCI provenance: GitHub Actions runs in OIDC-issuing environments are already producing Sigstore-signed provenance, and Immutable Actions extends that to the publishing path itself, so consumers can verify that the OCI artifact was built by the maintainer's expected workflow. The combination means a consuming organization can pin to a digest, verify the publishing provenance, and trust that the resolved chain at runtime matches what was reviewed at policy time.
How do you migrate workflows to take advantage of this?
The migration is a two-step process. Step one is to pin to digests rather than tags, which is good practice independent of Immutable Actions and is the only thing that protected SHA-pinners during the tj-actions incident. Step two is to opt into the Immutable Actions distribution path for action versions you depend on heavily, which prevents future tag-rewrite attacks against those specific dependencies.
# Before: mutable tag, vulnerable to maintainer compromise
- uses: actions/checkout@v4
# After: digest-pinned, with comment showing the version for human review
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.2.2
# Once Immutable Actions is enabled for your org, the runner verifies
# the digest at execution time and fails fast on any mismatch
For organizations with many workflows, Dependabot and Renovate both support digest-pinning rules that keep the SHA references current while preserving the immutability property. GitHub's own actions/dependency-review-action will surface mutable-tag uses as findings in pull-request review, which is a useful enforcement point for org-wide adoption.
What policy gate catches the next tj-actions going forward?
Three gates close the class of issue. Gate one is "deny any workflow that references an action by tag rather than digest for any sensitive job," with sensitive defined per-org but typically including any job that handles release artifacts or production deploys. Gate two is "require Immutable Actions distribution for any third-party action above a usage threshold inside the org," which removes the tag-rewrite attack from the residual surface area. Gate three is "verify the publishing provenance of any newly used action against an allow-list of known publisher identities," which is essentially Trusted Publishing for actions rather than for packages. The OpenSSF Scorecard's Pinned-Dependencies check already grades repositories on tag-vs-digest pinning, so an org can use Scorecard threshold gates as a coarse-grained signal during dependency review.
What still has to mature?
Two gaps remain visible in mid-2026. The first is that the existing action ecosystem has thousands of low-traffic actions that may never migrate to Immutable Actions distribution; gating "high-traffic actions" works, but the long-tail is harder. The second is that Immutable Actions does not by itself solve the problem of a malicious maintainer publishing a freshly malicious version: the artifact is immutable, but if the action you trusted in version 1.0 ships a backdoor in version 1.1, you still have to detect that through review or scanning. Provenance-aware review tools that flag "the workflow file that built this version changed since the previous release" are useful here, and the Actions team has been clear that GA is a step, not a destination.
How Safeguard Helps
Safeguard's GitHub Actions analyzer ingests every workflow file in your tenant's repositories, surfaces mutable-tag references that should be digest-pinned, and flags composite actions whose nested dependencies are not yet on the Immutable Actions distribution path. The provenance verification engine reads OCI artifact provenance for actions the same way it reads npm and PyPI provenance, with per-org policies that can require provenance from known publisher identities for selected action lists. The malicious-package feed includes Actions-marketplace removals so a tj-actions-class compromise raises findings within minutes of GitHub's response, tied directly to every workflow that consumes the affected action. Policy gates can enforce digest pinning, refuse builds that resolve to non-immutable action distributions for sensitive jobs, and require Scorecard thresholds for any new dependency, closing the loop between the registry-side improvements and the consuming organization's policy. The result is that a 2025-style tj-actions incident becomes a contained finding in your dashboard rather than a four-figure-workflow eviction project.