Zennoxa Shield

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.

July 21, 2026By Zennoxa Researchsupply-chaingithub-actionsci-cddevsecops

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 *.disabled files excluded), and classify each external uses: 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-001 rule, 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.

Shield flags GitHub Actions pinned to a mutable tag instead of a full commit SHA — the same weakness behind the tj-actions compromise.

See what Shield finds in your code

Free to start, no credit card. One pass covers SAST, secrets, vulnerable dependencies, containers and IaC — ranked by what’s actually reachable, not just severity.

Scan your repo free

Prefer the terminal? The offline CLI runs with no account

More research
State of GitHub Actions Pinning 2026: we checked 30 popular repos — Zennoxa Shield