State of GitHub Actions Pinning 2026: we checked 30 popular repos
Across 30 popular public repositories, 1 in 3 GitHub Action references still points at a mutable tag instead of a pinned commit — the exact pattern behind the tj-actions/changed-files and reviewdog compromises. Reproducible data.
TL;DR — We measured how 30 popular public repositories reference third-party
GitHub Actions. Of 3,195 external action references, 32.4% still point at a
mutable tag (@v4, @main) instead of a pinned commit SHA. The practice is
sharply bimodal: 16 of 30 projects pin everything, while 5 pin almost nothing.
Every number here is reproducible with a one-line clone and a regex.
Why a @v4 is a supply-chain risk
When a workflow says uses: some/action@v4, it runs whatever code the tag v4
points at right now. Tags are mutable — whoever controls that action can move
v4 to new code, and it executes on your runner with your secrets. That is not
hypothetical: the tj-actions/changed-files and reviewdog compromises in
2025 worked exactly this way — a popular action's tag was repointed to
credential-stealing code, and every workflow tracking the tag ran it.
GitHub's own hardening guidance is unambiguous: pin third-party actions to a
full 40-character commit SHA. A SHA is immutable — @de0fac2e… is the same
code forever. So "what fraction of the ecosystem actually pins?" is a concrete,
checkable question. We checked.
What we measured
- Corpus: 30 widely-used public repositories that use GitHub Actions (frameworks, runtimes, tooling — the projects a lot of other software depends on).
- For each repo: sparse-clone only
.github, then read every active workflow file (.github/workflows/*.yml|*.yaml; commented-out lines and*.disabledfiles excluded), and classify each externaluses:reference as either SHA-pinned (@followed by a 40-hex commit) or mutable-tag (everything else —@v4,@main,@latest). Local (./) actions are excluded. - Tool: the classification is Zennoxa Shield's
SHIELD-CI-001rule, cross-checked against an independent regex parser. The two agreed to within ~1–5% on every repo; the small residuals are edge cases (e.g. reusable-workflow calls) that don't move the headline.
The full per-repo dataset is in data.json — 30 rows, raw counts,
reproduce steps.
Finding 1 — 1 in 3 references is still mutable
| References | Share | |
|---|---|---|
| SHA-pinned (immutable) | 2,161 | 67.6% |
Mutable tag (@v4, @main) |
1,034 | 32.4% |
| Total external action refs | 3,195 | 100% |
Two-thirds of references are pinned — better than folklore suggests — but a third are not, and these are among the most-depended-on projects in open source.
Finding 2 — pinning is bimodal: teams either do it or they don't
The interesting part isn't the average, it's the shape. Repo-level pinning rate has a median of 100% — yet the mean is only 71%. That gap is the story:
- 16 of 30 repos pin 100% of their external actions.
- 5 of 30 pin under 10% — effectively nothing.
- Only 5 of 30 sit in the messy 10–90% middle.
Pinning is a discipline you adopt wholesale or skip, rarely a gradient. It's
usually one CI policy decision (or a bot like pin-github-action / Dependabot for
SHAs) applied repo-wide — so a project tends to be all-in or all-out.
Fully pinned (the gold standard): vue, angular, node, deno, moby, prometheus, rust, flask, fastapi, express, astro, vite, webpack, axios, terraform, bun.
Least pinned (most tag-exposed):
| Repo | SHA-pinned | Mutable refs |
|---|---|---|
| microsoft/playwright | 0.0% | 91 |
| nestjs/nest | 0.0% | 4 |
| facebook/react | 2.7% | 177 |
| microsoft/vscode | 3.2% | 92 |
| django/django | 7.8% | 59 |
| eslint/eslint | 10.0% | 45 |
Finding 3 — same company, opposite discipline
Pinning isn't set at the org level — it's per-repo culture. Within Microsoft, three flagship projects land in completely different places:
- microsoft/TypeScript — 94.7% pinned
- microsoft/vscode — 3.2% pinned
- microsoft/playwright — 0% pinned
Same security org, same resources, three different answers. Pinning discipline lives with the team that owns the workflow, not the company logo on the repo.
A fair caveat
Mutable tags aren't automatically "wrong". Plenty of teams accept the tradeoff:
tags auto-receive upstream fixes, and if you trust an action's maintainer you may
prefer @v4 for the convenience. But that trust is transitive and revocable — the
2025 compromises hit teams who trusted popular, reputable actions. Pinning to a SHA
plus a bot that proposes SHA bumps gets you both safety and updates. That's why
it's the documented best practice, and why the gap above is worth watching.
How to fix it in your own repo
# before — runs whatever v4 points at today
- uses: actions/checkout@v4
# after — immutable: the exact commit, with the version in a trailing comment
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin to a full commit SHA
Then let Dependabot (package-ecosystem: github-actions) or
pin-github-action propose SHA bumps so you stay current without tracking a
mutable tag. Zennoxa Shield flags every unpinned third-party action
(SHIELD-CI-001) if you want it enforced in CI.
Limitations (so you can weigh the numbers)
- 30 popular/large repos — not a random sample of all of GitHub; this skews toward well-resourced projects, so the true ecosystem-wide mutable rate is plausibly higher, not lower.
- We count references, not unique actions — a heavily-reused action weights a repo's number.
- Local (
./) actions and reusable-workflow calls are excluded from the denominator. - Point-in-time (2026-07-21). Pinning changes; a repo at 3% today can be at 100% next month. This is why the dataset is dated and reproducible.
Reproduce it yourself
# for any repo:
git clone --depth 1 --filter=blob:none --sparse https://github.com/facebook/react
cd react && git sparse-checkout set .github
# every external ref whose @ref is NOT a 40-hex SHA is mutable-tag pinned:
grep -rhoE 'uses:\s*[A-Za-z0-9._-]+/[A-Za-z0-9._/-]+@[A-Za-z0-9._/-]+' .github/workflows \
| grep -vE '@[0-9a-f]{40}\b'
Numbers, method, and every per-repo count are in data.json. If a
figure looks off, re-run the clone and check — that's the point.
Zennoxa Research publishes reproducible security data. This study measures only public repositories with public tooling; no third-party scanner is involved.