← All research

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, 2026 5 min readBy 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

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:

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:

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)

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.

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