GitHub Actions Security: What the New Workflow Lockfile Does Not Lock Yet
GitHub put workflow dependency lockfiles into public preview on 14 October. The gh-actions-lock repository's source and its first week of issues show three kinds of dependency the lockfile skips without saying so, and one case where a broken file fails open.
The standard GitHub Actions security advice has been to pin every third-party
action to a full commit SHA. On 14 October GitHub offered a replacement.
Workflow dependency lockfiles, in public preview for Enterprise Cloud, Enterprise Cloud with data residency, Team and Free, keep
a file at .github/workflows/actions.lock that records the commit each action
reference resolved to, transitive ones included. The runner runs that commit
even if the tag moves. The docs say a repository that adopts it no longer needs
SHA pins in its YAML.
A GitHub CLI extension, gh-actions-lock, generates the lockfile, so its code
decides what gets covered. This post reads it at v0.2.0, the current release,
along with its issue tracker.
What gh-actions-lock does
Run gh actions-lock from a repository root. It resolves every uses:
reference under .github/workflows/, rewrites loose tags like v3 to the
exact version they resolve to, and writes each dependency with its ref, its
commit, and the numeric owner and repository IDs. According to the README,
every locked pin is checked the way the runner checks it at job start: the
repository was not renamed or transferred, the commit still exists, and
full-version tags still point at it. The docs add that a locked commit must sit
on a branch of the source repository, which is aimed at impostor commits served
from forks. --verify runs these checks in CI without writing anything.
A SHA pin in YAML covers only the action you named, not the actions it calls in turn. The lockfile follows those calls.
Maturity signals
The repository is MIT licensed Go. Its first release, v0.0.1, came out on 23 April, and v0.2.0 on 12 October. The README calls it pre-1.0 and warns that the lockfile format, flags and behavior can change between releases. GitHub Enterprise Server is not supported. When this was written it had 227 stars and 21 open issues that are not pull requests, seven of them filed on or after 14 October, when the preview opened. Three of the four gaps below were reported in those days.
Four gaps the issue tracker found
Remote reusable workflows are not locked. The concept page says the
lockfile pins every action and reusable workflow a workflow depends on. In the
v0.2.0 source, a job-level uses: value goes to the shared library's
ParseActionRef. That function returns nothing for any path under
.github/workflows/, and the scanner throws the empty result away. The library
also has a ParseReusableWorkflowRef function, and the CLI never calls it.
Issue 179 shows the result: a workflow that only calls a tagged reusable
workflow gets an empty entry, and --verify passes. A maintainer
closed the earlier issue 129 on 15 October with a note that pinning job-level
references is not planned for now.
I read the first 40 lockfiles a GitHub code search returned. Three repositories (hashintel/hash, hashintel/labs and guardian/cdk) call remote reusable workflows, each recorded as an empty list. All three had kept the full SHA pin in the YAML. Those adopters are running two pinning schemes side by side, and the lockfile only knows about one.
Container images still run by tag. At the commit tagged v2.4.4,
ossf/scorecard-action pulls ghcr.io/ossf/scorecard-action:v2.4.4, which is a
tag. Locking the action's commit leaves that tag free to move. The author of
issue 168 scanned 1,802 active public repositories (not a random sample) and
found 177 using an action that pulls a prebuilt image by tag. On issue 128 a
maintainer wrote that runtime enforcement of Docker images has no reasonable
path today, and suggested documenting where the lockfile's trust stops.
Parallel step groups are skipped. In workflowfile.go, the step walker reads
uses: only from entries directly under steps:. Actions nested under a
parallel: group never reach the lockfile. Issue 175 reported it on 16 October; the fix,
pull request 176, had not merged at the time of writing.
A broken lockfile fails open. In issue 125, a corrupt lockfile was simply ignored and the workflows ran. The maintainer replied that failing closed would break workflows that were never onboarded, and that the Require lockfile policy is the switch that makes a broken lockfile fail closed.
A fifth report, issue 174, had --verify accept a hand-edited entry for a
fork-only commit, which a hosted runner then executed. A maintainer said on
16 October that the backend was fixed and the CLI would follow. Until then, a
passing --verify does not prove the lockfile was never edited by hand.
Who should not adopt it yet
GitHub Enterprise Server shops, because the tool does not support them. Teams
whose risk sits mostly in shared reusable workflows from other organizations,
because those calls fall outside the lockfile. Organizations that use an
allowed-actions list, until someone rewrites it. The how-to guide notes that
once a workflow is onboarded, the allowlist is checked against the locked SHA,
so tag entries like actions/checkout@v6 stop matching.
My view: adopt it for transitive action coverage, which SHA pins never gave
you, and keep every SHA pin you already have. Nothing in v0.2.0 makes those pins
redundant for reusable workflows or images. This claim is wrong as soon as a
release locks job-level uses: and --verify reports unlocked image tags.
GitHub's own enforcement guide is a pilot plan: Evaluate mode, automations to
keep the file current, a CODEOWNERS rule, then Active, plus a Copilot skill that the guide
calls a convenience rather than a control. The pilot proves the mechanism;
production depends on maintenance nobody budgeted for, which is the argument in
From PoC to Production. Moving tags are not a
hypothetical risk either: the breaking change behind
the pull_request_target deadline
reached builds through moving actions/checkout tags.
Three checks before you onboard
All three were tested against real workflow files during this run. The first
lists remote reusable workflow calls that are not pinned to a full SHA, which
the lockfile will not cover. The second finds parallel step groups, whose actions v0.2.0 skips. The
third shows the image a locked Docker action really pulls (some actions name
the file action.yaml).
grep -nE '^ +uses: +[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+/\.github/workflows/' .github/workflows/*.y*ml | grep -vE '@[0-9a-f]{40}'
grep -nE '^ +- parallel:' .github/workflows/*.y*ml
gh api 'repos/OWNER/REPO/contents/action.yml?ref=LOCKED_SHA' --jq .content | base64 -d | grep -n 'image:'
The first should come back empty: anything it prints is a reusable workflow on
a moving ref that the lockfile will not catch. In the third, any image without @sha256: is still floating. Then add actions.lock to
CODEOWNERS, pilot the Require lockfile policy in Evaluate mode, and move it to
Active. Only the active policy makes a broken file fail closed.
Sources
- GitHub Actions workflow lockfiles in public preview - GitHub Changelog
- Workflow dependency lockfiles - GitHub Docs
- Locking action dependencies in a repository's workflows - GitHub Docs
- Enforcing lockfiles for action dependencies - GitHub Docs
- gh-actions-lock README at v0.2.0 - github/gh-actions-lock
- workflowfile.go at v0.2.0 - github/gh-actions-lock
- uses.go at go/v0.0.6 - github/actions-lockfile
- Lock remote reusable workflow calls - gh-actions-lock issue 179
- Job-level reusable-workflow uses refs are invisible - gh-actions-lock issue 129
- Warn when a locked Docker container action pulls its image by tag - gh-actions-lock issue 168
- Unpinnable local docker image in pypa/gh-action-pypi-publish - gh-actions-lock issue 128
- Actions inside parallel step groups are left out of the lockfile - gh-actions-lock issue 175
- A broken action.lock file causes it to be ignored - gh-actions-lock issue 125
- Full-SHA lock entries can accept a fork-only commit - gh-actions-lock issue 174
- action.yaml at v2.4.4 - ossf/scorecard-action
- actions.lock - hashintel/hash
- actions.lock - guardian/cdk
Related reading
- Dev Tools
AI Agent Sandboxing with NVIDIA OpenShell: The Sandbox Holds. The Gateway Can Start Wide Open.
By Petru Popa · Read - Dev Tools
LiteLLM Supply Chain Attack: The Hardened Pipeline Published the Malware
By Petru Popa · Read - Dev Tools
AI SAST on GitHub: AI Scan Skips the Pull Requests Most Open Source Projects Merge
By Petru Popa · Read
Turn this into a plan for your team.
One week, fixed fee: a working session with your team, a prioritized use-case backlog, and an ROI model for the opportunities worth chasing.