← Actions Transitive Pin Check
Every uses: line in your own workflow file is a direct dependency — the thing you chose and, ideally, pinned yourself. A reusable workflow or a composite action is just another file with its own steps, and if one of those steps has its own uses: line, that is a transitive dependency: something your workflow depends on that you did not write, did not review, and cannot see by reading your own repository. Pinning your direct reference to a commit SHA fixes what that one line points to. It says nothing about what the file at that commit itself loads.
# your workflow — this line is pinned, and it is genuinely fixed
- uses: octo-org/shared-workflows/.github/workflows/build.yml@2c4d34e6cff37bafe66eba7a6c1c8a5c9d9c1a6e
# the file AT that commit — a step nobody reviewing your diff ever sees
- uses: some-third-party/build-helper@v3
Your SHA pin proves build.yml at that exact commit cannot be silently repointed to a different commit. It does not touch the second line, because that line lives in a file you neither wrote nor pinned. If some-third-party/build-helper's v3 tag is ever repointed — by its maintainer, or by whoever compromises that maintainer's account — every workflow that trusts your pinned reusable workflow inherits whatever code v3 now resolves to, on the next run, with no change to your own repository at all.
A top-level SHA pin is the well-known fix for the well-known problem: a mutable tag on a reference you wrote yourself. Reviewing that takes one look at your own workflow files. The transitive case takes fetching and reading a file that lives in someone else's repository, at a specific commit, before you can even tell whether there is anything to check — which is why most pinning checkers, this portfolio's own Pinned Actions Check included, are explicit that they classify only the uses: lines in the file you scan, and say nothing about what a pinned reusable workflow or composite action loads underneath it.
Paste a workflow into Actions Transitive Pin Check to fetch every pinned reference's own definition from raw.githubusercontent.com and flag one that loads something unpinned underneath, or run the CLI in your own repository:
node check.mjs --fail-on-risk
That exits nonzero if any pinned reference loads an unpinned one underneath it — wire it into your own CI to block a newly-introduced gap before it merges.