How to pin GitHub Actions to a commit SHA
A uses: line that names a tag runs whatever commit that tag points at when the job starts. Pinning replaces the tag with the full 40-character commit SHA, so the code that runs is the code you reviewed.
Why tags are not enough
Tags in Git are pointers. Anyone with push access to an action's repository can move @v4, or a version tag like @v4.2.0, to a different commit, and many maintainers move the major tag on every release by design. If an action's repository is compromised, moving its tags is the fastest way to reach every workflow that uses it.
A commit SHA cannot be moved. Pinning to the full SHA is the mitigation GitHub's own security hardening guide recommends for third-party actions.
The pinned format
Keep the tag you started from as a trailing comment. Dependabot's github-actions ecosystem and Renovate both read that comment, so they keep opening update pull requests that bump the SHA and the comment together.
# before: a tag that can be moved - uses: actions/checkout@v4 # after: the commit v4 points at today, with the tag kept as a comment - uses: actions/checkout@<40-character commit SHA> # v4
Finding the right SHA
Look the tag up in the action's repository. For an annotated tag the tag object has its own SHA, which is not the commit; you need the commit it points at (the peeled ref). git ls-remote shows both, and the line ending in ^{} is the commit.
git ls-remote https://github.com/actions/checkout "refs/tags/v4*"
# <tag-object-sha> refs/tags/v4
# <commit-sha> refs/tags/v4^{} <- pin to this oneWhat pinning does not do
A pin freezes code; it does not vet it. You still choose which actions to trust, and a pinned action can still fetch and run other code at runtime. Re-check pins when you update them, and prefer actions from publishers you would trust with your token.
What Anchorflow checks
Anchorflow lists every uses: in your workflow, marks moving refs (branches, bare majors) and plain tags as not pinned, and on request resolves each one to its commit with read-only requests from your browser. It rewrites only the uses: values, keeps every comment and blank line, adds the tag comment, and gives each pin a receipt with the API calls and a git ls-remote line to check it by hand.
Check your workflow