pin-deep: you pinned your Actions, but your Actions didn't pin theirs
Everyone knows the drill: pin your GitHub Actions uses: to a commit SHA so a compromised tag can't rug-pull your CI. Done, right? Not quite. There's a depth problem nobody checks. The tj-actions incident (CVE-2025-30066
Everyone knows the drill: pin your GitHub Actions uses: to a commit SHA so a compromised tag can't rug-pull your CI. Done, right?
Not quite. There's a depth problem nobody checks. The tj-actions incident (CVE-2025-30066) is the textbook case: the compromise reached victims through reviewdog, which referenced the compromised action. Pinning reviewdog itself wasn't enough — the hole was one level deeper, inside the dependency of a pinned dependency.
"You pinned your Actions, but the Actions you pinned didn't pin their dependencies."
That's what pin-deep checks. Give it a workflow file or a single pinned uses: value, and it recursively fetches the action.yml of every SHA-pinned action — up to a configurable depth, with cycle protection — and flags every nested uses: that isn't pinned to a full commit SHA.
$ pip install pin-deep
$ pin-deep .github/workflows/ci.yml
[unpinned] depth=2 actions/cache@v3
source: action.yml of actions/setup-python@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
chain: acme/root@aaa... -> actions/setup-python@aaa... -> actions/cache@v3
note: not pinned to a full commit SHA
A few design decisions worth knowing:
- Only full SHAs get recursed into. A floating tag has no fixed content to analyze — auditing its children would audit today's state, not the state you ship. Unpinned refs are flagged, never followed.
-
Short SHAs are warnings, not passes. Seven hex chars technically resolve, but they're weaker than a full 40/64-char SHA.
--strictfails CI on those too. - Depth defaults to 3. Deep enough to catch the reviewdog-shaped hole, shallow enough to finish before your coffee does.
-
Exit code 1 on findings, so it drops straight into CI.
GITHUB_TOKENis used for the API fallback; public repos need nothing.
Differentiation
Static workflow linters like zizmor analyze your uses: lines. pin-deep checks one level further: what your pinned actions themselves depend on, recursively. Nobody else does the recursive nested check — pinning has a depth dimension, and this is the tool for it.
stdlib only, no dependencies. If it finds a hole in your supply chain, you'll know exactly which chain of actions put it there.
- GitHub: https://github.com/hahahahahahahahah6/pin-deep
- PyPI: https://pypi.org/project/pin-deep/
pip install pin-deep
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.