Decision Always pin GitHub Actions to a commit hash
acceptedGitHub Actions referenced by a mutable tag or branch can be silently replaced with malicious code, exposing CI/CD secrets across every repository that uses them.
Decision
All GitHub Actions must be pinned to a full commit SHA rather than a tag or branch name.
# Bad - mutable, can be silently replaced
- uses: actions/checkout@v4
# Good - immutable, tied to a specific commit
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
Include the corresponding version tag as a comment so the pinned commit remains human-readable and can be updated intentionally.
Enforcement
At the organization level
GitHub now supports enforcing SHA pinning at the organization level. Enable this policy for GitHub organizations when possible. This causes any workflow using a non-pinned action to fail outright.
At the project level
Projects should pin all actions regardless of whether the organization policy is in place. Use Renovate to keep pinned hashes up to date automatically by adding the helpers:pinGitHubActionDigests preset to the extends array:
{
"extends": ["helpers:pinGitHubActionDigests"]
}
Renovate will update the commit SHA whenever the referenced tag moves, keeping the pin current without manual effort.
Background
In March 2025, a cascading supply chain attack compromised the widely used tj-actions/changed-files action by pushing malicious code to an existing tag. Any repository referencing the action by tag automatically ran the malicious code and exposed CI/CD secrets. Pinning to a commit SHA prevents this class of attack entirely: a tag can be moved to point at new code, but a SHA is immutable.
Additional resources
While not mandatory, projects can use the Zizmor tool to perform static checks on GitHub Actions, ensuring that all actions are pinned to a specific commit SHA.
Consequences
All existing workflows should be audited and updated to use commit SHA pins. New workflows must follow this pattern from the start.
Pinned SHAs require active maintenance to stay current. Using Renovate with the helpers:pinGitHubActionDigests preset automates this, making the maintenance overhead negligible.