GitHub Actions Got a Lockfile on October 14. It Locks the Code You Reviewed. It Does Not Review It.

On Wednesday, October 14, GitHub put workflow dependency lockfiles for GitHub Actions in public preview, for Enterprise Cloud (with or without data residency), Team and Free plans. A file at .github/workflows/actions.lock, generated by a GitHub CLI extension called gh actions-lock, records the exact commit behind every action a workflow uses, including the actions those actions call, and the reusable workflows it depends on. Once a workflow is onboarded, GitHub Actions runs the recorded commits, even if a tag or branch moves. A companion policy, Require lockfile, lets enterprise, organization and repository admins make it mandatory.
The announcement is short. The docs, the extension's README and the design discussion behind it are not, and they hold the parts that decide whether this protects you or just adds a file to your repository.
I am the CTO of a LegalTech built on Claude, and I ship iOS apps and a strategy game as an indie developer. Different products, same question: a CI job that holds secrets runs every third-party step right next to them. So I read all of it.
Why a tag is the weak point
When a workflow says uses: some-org/some-action@v3, Actions resolves v3 to whatever commit the tag points to when the job starts. Tags can be moved. That is the whole attack.
In March 2025, attackers "retroactively modified multiple version tags" of tj-actions/changed-files to point at a malicious commit, according to its entry in the GitHub Advisory Database, which dates the exposure to March 14 and 15 and puts the impact at over 23,000 repositories. The payload read secrets from the runner's memory and printed them in the build logs.
On March 19, 2026, it happened again. Aqua Security's timeline says an attacker force-pushed 76 of the 77 version tags of aquasecurity/trivy-action to malicious commits (early outside counts, from Socket and Snyk, said 75 of 76). Pipelines kept running, Aqua writes, "without any indication that the underlying code had changed."
A week later, on March 26, GitHub published its 2026 security roadmap for Actions. It named tj-actions/changed-files, Nx and trivy-action, and set a target of three to six months for a public preview of dependency locking. It arrived six months and 18 days later.
What the file records
The docs describe two sections. workflows lists which dependencies each onboarded workflow uses. dependencies lists, for each one, the ref you wrote (a tag or branch), the commit it resolved to, and the numeric owner_id and repo_id of the source repository.
Three details matter more than the pin itself.
The locked commit must exist within a branch of the source repository. The README says this is "to make impostor commit style attacks harder." GitHub's design discussion made the point in May: a plain SHA pin in a workflow file does not guarantee that the commit lives in the named repository rather than in an attacker-controlled fork.
The repository's identity is checked at job start. According to the README, the runner confirms that the repository was not renamed or transferred, that the pinned commit still exists, and that a full version tag such as v4.2.1 still points at it. A failed check blocks the run.
And composite actions no longer hide their own uses: lines, because transitive dependencies are pinned too.
For repositories that adopt a lockfile, GitHub now says SHA pinning inside workflow files "is no longer necessary."
The shipped design is not the one GitHub first proposed. The March roadmap described a dependencies: section inside the workflow YAML, resolved through the GitHub CLI, and the May discussion named the command that would write it, gh actions pin. What shipped is one separate file per repository, maintained by an extension. I think that is the better choice: one file to diff, one path to protect.
What the announcement does not tell you
The extension edits more than the lockfile. It rewrites references to the specific version it resolved, so @v3 can become @v3.1.0, and it converts same-repository ./… actions to a new $/… syntax that always resolves to the running commit. Review the whole diff, not just the new file.
If your organization restricts which actions may run, check the allowlist before you commit. After onboarding, the allowlist is checked against locked commit SHAs, and entries pinned to a tag, such as actions/checkout@v6, "no longer match." The docs say to use a wildcard or the full SHA.
Reusable workflows are a boundary. Your lockfile pins the reusable workflow you call, but the docs say its own dependencies are covered by the lockfile of the repository where it is defined, and recommend onboarding shared-workflow repositories first. I read that as: a shared workflow from another repository is only as locked as that repository.
Dependabot keeps onboarded workflows in sync, regenerating the lockfile entry in the same pull request as the version bump. It will not onboard a workflow for you. It "just reports that onboarding is required."
The Require lockfile policy can be piloted in Evaluate mode, but the docs say Evaluate mode is only available on GitHub Enterprise. On Free or Team, the policy has no dry run.
And it is a preview. The README calls the extension pre-1.0 and warns that the lockfile format, command flags and behavior "may change without notice between releases." GitHub Enterprise Server is not supported. On October 19, the how-to pages still listed only GitHub Enterprise Cloud under "Who can use this feature?", while the changelog listed Team and Free under an editor's note, dated October 14, that "updated which plans have access."
The limit GitHub writes down itself
"Locking does not determine whether the dependency itself is safe, so review dependencies before locking them." That sentence is in the announcement, and it is the most important one. If you generate a lockfile while a tag is poisoned, you have pinned the malware, and every later run will execute it faithfully.
What the lockfile changes is when you find out. Branches and partial versions such as v3 or main are trusted from the lockfile and not re-resolved on a normal run. If an upstream tag now points somewhere else, the extension stops with an error and you decide. gh actions-lock --relock accepts a legitimate move. If the commit you locked is no longer reachable at all, "for example because the upstream repository force-pushed or deleted it," even --relock reports an error, and you need --accept-moved. That is the flag I would never let a script pass on its own.
The impostor-commit check has already been tested in public. On October 15, François Proulx of Boost Security reported in the extension's issue tracker that a hand-edited lockfile entry, keyed to a full SHA that existed only in a fork of actions/checkout, passed gh actions-lock --verify and ran under the upstream name in a repository with Require lockfile active. A fresh gh actions-lock run rejected the same commit. On October 16, a maintainer replied that the backend defect was fixed, adding: "I'll come back to the gap in the cli next week." On October 19, the issue was still open. The report notes that the attack needs a contributor to make visible changes to both the workflow and the lockfile.
The enforcement guide puts it plainly: "A lockfile is only a security control if someone reads the diff." It also suggests a Copilot skill that regenerates the lockfile whenever the agent edits a workflow, and calls that "a convenience rather than a control." I orchestrate my work with Claude Code, and the same logic holds for any coding agent: an instruction keeps the file fresh, it does not review it.
What I would do first
- Turn on Dependabot for GitHub Actions before creating the lockfile, as the docs recommend.
- List every
uses:line, check that each points to a supported, current version, and look at the recent tag history of each third-party action you are about to lock. - Install the extension with
gh extension install github/gh-actions-lockand onboard one workflow by passing its path. I would start with the one that holds deploy or signing secrets. - Review the full diff and the allowlist, then commit.
- Add a job that runs
gh actions-lock --verifyon pull requests and make it a required check. It catches a stale lockfile; the October 15 report shows it is not a substitute for reading a hand-edited entry. Locally,gh actions-lock --verify-local --no-fixruns offline in a pre-commit hook, but it only checks that every reference has an entry, not that the entries are trustworthy. - Put
.github/workflows/actions.lockin CODEOWNERS and require a code owner's approval. - Turn on Require lockfile only once every targeted workflow is onboarded and something keeps it that way. Know what it buys you: in another issue, a maintainer explained on October 15 why a broken lockfile does not stop runs (failing closed would hit workflows that were never onboarded) and said the Require lockfile policy "is what gets you fail closed behavior."
It fits a direction GitHub has already taken: on September 10, it made cache-mode generally available on all plans, a least-privilege setting for the Actions cache. Pinning what runs and limiting what it can touch are two halves of the same job.
A tag is a promise made by someone else's account. A lockfile turns it into a commit you chose. Choosing well is still on you.
Sources
- GitHub Changelog, "GitHub Actions workflow lockfiles in public preview" (October 14, 2026)
- GitHub Docs, "Workflow dependency lockfiles" (read October 19, 2026)
- GitHub Docs, "Locking action dependencies in a repository's workflows" (read October 19, 2026)
- GitHub Docs, "Enforcing lockfiles for action dependencies" (read October 19, 2026)
- GitHub, "gh-actions-lock" (README) (read October 19, 2026)
- gh-actions-lock issue #174, "Full-SHA lock entries can accept a fork-only commit under the upstream action name" (opened October 15, 2026, maintainer reply October 16, read October 19, 2026)
- gh-actions-lock issue #125, "A broken action.lock file causes it to be ignored" (maintainer reply October 15, 2026)
- The GitHub Blog, "What's coming to our GitHub Actions 2026 security roadmap" (March 26, 2026)
- GitHub Community, "Help Shape Workflow Dependency Locking" (May 1, 2026)
- GitHub Advisory Database, "tj-actions changed-files through 45.0.7 allows remote attackers to discover secrets by reading actions logs" (read October 19, 2026)
- Aqua Security, "Update: Ongoing Investigation and Continued Remediation" (Trivy incident write-up) (first posted March 22, 2026, updated April 1, 2026)
- Snyk, "Trivy GitHub Actions Supply Chain Compromise" (March 20, 2026)
- GitHub Changelog, "Control GitHub Actions cache access with cache-mode" (September 10, 2026)
