Imagine your recipe says "use Brand X flour." You tasted the dish, you approved it, you wrote it down. Then one night somebody swaps what's inside the bag. Same label. Different contents. Your recipe didn't change, but dinner did.
That's a moved tag in a CI pipeline. When your workflow says uses: some/action@v4, v4 is a label, and labels can be re-pointed. In March 2025, the widely reported tj-actions/changed-files compromise worked this way: existing version tags were repointed at malicious code, so pipelines that hadn't changed a single line ran it anyway.
On October 14, 2026, GitHub put a direct answer into public preview: workflow dependency lockfiles. Let's cook with them.
What We Are Cooking Today
- Understand what a workflow lockfile pins, and what it doesn't
- Compare tag pinning, SHA pinning and lockfiles
- Roll lockfiles out one workflow at a time
- Add a least-privilege baseline using two new September features
How It Works (Facts From GitHub's Changelog)
- The
gh actions-lockextension for the GitHub CLI generates.github/workflows/actions.lock. - The file records reviewable commits for your actions, their transitive dependencies, and reusable workflow dependencies.
- You onboard workflows one at a time. Once a workflow is onboarded, every dependency must have a lockfile entry, and Actions runs the recorded commit even if the tag or branch has moved.
- The extension can check whether the lockfile is stale without modifying files, including inside a CI check.
- A Require lockfile policy lets enterprise, org and repo admins enforce this. It's in public preview too.
- When Dependabot bumps an action in an onboarded workflow, it regenerates the matching lockfile entry in the same pull request.
- Availability: public preview on Enterprise Cloud, Enterprise Cloud with data residency, Team and Free plans. GitHub Enterprise Server isn't in that list, so check before planning around it.
The catch, straight from GitHub: locking doesn't tell you whether a dependency is safe. If you lock a bad commit, you've locked a bad commit very reliably.
Tag vs SHA vs Lockfile
| Approach | Readable | Can it move? | Covers transitive dependencies? |
|---|---|---|---|
Tag (@v4) | Yes | Yes | No |
Full commit SHA (@abc123...) | No, it's a wall of hex | No | Not by itself. Composite actions can pull in their own dependencies. |
| Lockfile | Yes, your workflow keeps readable refs | Not for locked entries | Yes, per GitHub |
My take (opinion): SHA pinning was always the right idea with terrible ergonomics. Nobody enjoys reviewing a Dependabot PR full of 40-character hashes. A lockfile keeps the human-friendly ref in the workflow and moves the exact commit into a file built for machines and reviewers. That's the bit I like.
Ingredients
- A repo on an eligible plan
- GitHub CLI (
gh) installed and authenticated - Dependabot enabled for the
github-actionsecosystem - Admin rights if you want to turn on the Require lockfile policy
Recipe Steps
1. Install the extension
gh extension install github/gh-actions-lock
2. Generate the lockfile for one workflow
Preview tools change flags faster than I change coffee orders, so I'm not going to guess the exact subcommands here. Follow the README in the github/gh-actions-lock repo and GitHub's "workflow dependency lockfiles" docs, and start with a low-risk workflow like your lint job.
3. Review the diff like it matters
Open .github/workflows/actions.lock and look at the commits it records, including the transitive ones. This is the moment to notice an action you didn't know you depended on. Because locking blesses whatever is there, review before you commit.
4. Commit and run the workflow
Commit the lockfile and workflow changes together, open a PR, and confirm the run still goes green.
5. Add the stale check to CI
Use the extension's check mode (see the README for the exact invocation) in a small workflow so a PR that edits a uses: line without updating the lockfile fails fast.
6. Let Dependabot do the boring part
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
For onboarded workflows, those PRs now carry the refreshed lockfile entry too.
7. Roll out, then enforce
Onboard workflows in batches. Once most of a repo or org is covered, consider the Require lockfile policy so nobody quietly skips it.
Bonus: A Least-Privilege Baseline
Locking controls what code runs. Permissions control what that code can touch. You want both. GitHub's September 2026 updates added two relevant pieces:
- A
vulnerability-alertspermission forGITHUB_TOKEN, withreadandnonevalues, so a workflow can read Dependabot alerts without broader scopes. - New
jobcontext properties for reusable workflows, includingjob.workflow_ref, so a reusable workflow can know which workflow file defines the current job. These aren't available on GitHub Enterprise Server.
name: ci
on:
pull_request:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- run: make test
alerts-report:
runs-on: ubuntu-latest
permissions:
contents: read
vulnerability-alerts: read
steps:
- run: echo "Running from ${{ job.workflow_ref }}"
When to use it: every workflow, as a default. When not to: jobs that truly need to write (releases, deployments). Grant those permissions at the job level, never at the top.
Chef Tips
- Start with workflows that have the most third-party actions. That's where your exposure lives.
- Treat a lockfile change in a PR like a dependency change in application code: someone reads it.
- Combine this with branch protection on the workflow files themselves. A lockfile doesn't help if anyone can edit the workflow freely.
Kitchen Disasters
| Symptom | Likely cause | Fix |
|---|---|---|
| Onboarded workflow fails on a new action | Every dependency of an onboarded workflow must be in the lockfile | Regenerate the lockfile and commit it with the workflow change |
| Stale-lockfile check fails | Someone edited a uses: line by hand | Regenerate, or let Dependabot's PR do it |
| Everything is locked, yet you still got burned | You locked a commit that was already bad | Review before locking, and keep scanning dependencies |
| Feature isn't available | Plan not eligible, or you're on GitHub Enterprise Server | Fall back to SHA pinning plus Dependabot |
Serving Suggestions
- Run the same exercise for container images in your pipeline: pin by digest, not by tag.
- Pair this with the DevSecOps posts on this site for scanning and policy gates.
Cleanup and Rollback
Because onboarding is per workflow, the blast radius of a bad rollout is small. If a workflow misbehaves, revert the PR that onboarded it. Remember this is a public preview, so test in a non-critical repo first and expect rough edges.
FAQ
Do lockfiles replace SHA pinning?
For onboarded workflows they give you the same "exact commit" guarantee, plus transitive dependencies, while keeping readable refs in the workflow.
Does a lockfile make an action safe?
No. GitHub says so explicitly. It makes runs consistent with what you reviewed.
Can I use it on GitHub Enterprise Server?
The announcement lists Enterprise Cloud, Team and Free plans. Check GitHub's docs for Enterprise Server status.
Final Thoughts
Most pipeline attacks don't break down the front door. They swap a label on something you already trusted. Lockfiles won't make a bad dependency good, but they'll make sure "the code I reviewed" and "the code that ran" are the same code. That's a low bar, and an awful lot of pipelines still miss it.
No comments:
Post a Comment