6.1.4. Supply Chain Defenses: Pinning, Policies, Attestations
💡 First Principle: Every third-party action is a dependency that executes with your credentials, so supply chain defense means controlling three things — which code you allow, which exact bytes you run, and what proof you keep about what you produced.
Which code: organization and enterprise policies (3.1.2) — allowlists, verified-creator-only, or local-only — plus required review for unverified actions. This is the coarse filter and it belongs at the org level so no repository can opt out.
Which bytes: pinning. The strategy table is exam-ready:
| Reference style | Immutable? | Update path | Verdict |
|---|---|---|---|
@<40-char SHA> | ✅ Yes | Dependabot bumps it | ✅ Required for third-party actions |
@v1.2.3 (immutable action) | ✅ Yes | New release | ✅ Strong, syntax stays readable |
@v1.2.3 (ordinary tag) | ❌ Tag can move | New release | ⚠️ Looks precise, isn't |
@v1 (major tag) | ❌ Moves by design | Automatic | ⚠️ First-party/trusted only |
@main | ❌ Every push | Automatic | ❌ Never |
The tj-actions/changed-files compromise in 2025 is the reference incident: attackers gained write access and re-pointed existing tags at malicious code, which then dumped runner memory — including secrets — into public build logs. Every repository pinned by tag was affected; every repository pinned by SHA was not. Dependabot keeps SHA pinning maintainable by proposing bumps with the human-readable version in a trailing comment.
What proof: artifact attestations create signed, verifiable provenance linking a built artifact to the exact workflow, commit, and runner that produced it, following SLSA build provenance concepts and stored with Sigstore signing. Generating one is a step plus a permission:
permissions:
id-token: write
attestations: write
contents: read
steps:
- uses: actions/attest-build-provenance@<sha>
with:
subject-path: 'dist/app.tar.gz'
Consumers then verify with gh attestation verify dist/app.tar.gz --owner my-org, which answers "was this binary really built from our source by our pipeline?" — the question that unsigned artifacts cannot answer. Complementary controls worth naming: dependency-review-action to block risky dependency changes in PRs, secret scanning with push protection, and CodeQL for the workflows themselves.
⚠️ Exam Trap: An allowlist and SHA pinning solve different problems and neither replaces the other. The allowlist stops disallowed actions from being used at all; SHA pinning ensures an allowed action's contents can't change underneath you. A question describing "we allowlisted it, then it turned malicious after an update" is asking for pinning.
Reflection Question: Attestations don't prevent a compromised build from producing a malicious artifact. Given that, what exactly do they buy you — and at which point in an incident does that value appear?