Checkpoint Lab — Third-Party Actions, Commit-SHA Pinning, Marketplace Risk, and Dependency Governance
Convert a sample workflow to fully reviewed immutable action references, build a dependency register and demonstrate a safe upgrade-review process.
Learning objectives
- Build a dependency register from a sample workflow and classify first-party versus third-party executable dependencies.
- Resolve each third-party release to a verified full commit SHA and record the human-readable release mapping.
- Record minimum permissions, secrets, network and runtime assumptions for each dependency.
- Demonstrate a controlled update as a source/release diff plus a reviewed SHA change.
- Produce an evidence packet that makes the dependency state independently auditable.
1. Checkpoint scenario
You inherit a disposable container-build workflow that uses
convenient moving tags. Your task is to create a dependency
register, replace every external action with a reviewed full SHA,
record required authority and demonstrate how a future upgrade would
be reviewed. The build remains local with push: false;
no registry credential, release or production side effect exists.
2. Preflight and current assumptions
| Item | Checkpoint value |
|---|---|
| Verification date | 2026-09-10 |
| Repository | Disposable learner-owned repository only |
| Runner | ubuntu-24.04 |
| GitHub token | Explicit contents: read only |
| Secrets | None |
| External target | None; Docker image is not pushed |
| Checkout release |
v7.0.1 → 3d3c42e5aac5ba805825da76410c181273ba90b1
|
| Setup Buildx release |
v4.3.0 → 37fe631027851001ddb9b187196cc803df7f5f0e
|
| Build Push release |
v7.3.0 → 53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
|
3. Predict before changing the workflow
Prediction A: replacing tags with full SHAs changes
workflow dependency identity but does not grant new token
permissions or secrets. Prediction B: keeping
push: false means the Docker build may mutate only
runner-local Docker/BuildKit state; no registry image is published.
Prediction C: a future candidate release changes
the workflow only after its new SHA, metadata/runtime and source
diff are reviewed.
Record these predictions in the pull request or checkpoint notes before editing. The purpose is to make later evidence falsifiable rather than retrospective.
4. Preserve the intentionally weak starting point
Save this as evidence/workflow-before.yml. It is
intentionally unpinned so you can prove the correction.
name: supply-chain-checkpoint
on:
workflow_dispatch:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: docker/setup-buildx-action@v4
- uses: docker/build-push-action@v7
with:
context: .
push: false
tags: local/checkpoint:test
5. Build the dependency register before the repair
| Dependency | Class / signal | Reviewed release | Full SHA | Runtime / side effect | Caller authority |
|---|---|---|---|---|---|
actions/checkout |
GitHub-authored | v7.0.1 | 3d3c42e5aac5ba805825da76410c181273ba90b1 |
Node 24; populates workspace/Git config | contents: read |
docker/setup-buildx-action |
Third-party; Docker Verified creator | v4.3.0 | 37fe631027851001ddb9b187196cc803df7f5f0e |
Node 24; creates/configures Buildx and post cleanup | No additional GitHub write permission |
docker/build-push-action |
Third-party; Docker Verified creator | v7.3.0 | 53b7df96c91f9c12dcc8a07bcb9ccacbed38856a |
BuildKit build; local output when push:false
|
No registry secret; no package write |
For each row, retain links to release notes and exact source. The Verified creator signal is recorded but is not the approval rationale by itself.
6. Repair every external action reference
name: supply-chain-checkpoint
on:
workflow_dispatch:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-24.04
steps:
- name: Check out exact source revision
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up reviewed Buildx release
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
with:
cleanup: true
keep-state: false
- name: Build locally without registry push
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: .
push: false
tags: local/checkpoint:test
7. Verify the workflow contains only full-SHA external references
Use a small local checker so the checkpoint does not depend on
enterprise policy being available. It intentionally ignores
workspace-local ./ and self-repository
$/ references because those have different semantics.
# verify_pins.py
import pathlib, re, sys
sha = re.compile(r"^[0-9a-f]{40}$")
uses = re.compile(r"^\s*(?:-\s*)?uses:\s*([^\s#]+)")
errors = []
for path in pathlib.Path('.github/workflows').glob('*.y*ml'):
for n, line in enumerate(path.read_text(encoding='utf-8').splitlines(), 1):
m = uses.match(line)
if not m:
continue
ref = m.group(1)
if ref.startswith('./') or ref.startswith('$/') or ref.startswith('docker://'):
continue
if '@' not in ref:
errors.append((path, n, ref, 'missing @ref'))
continue
_, version = ref.rsplit('@', 1)
if not sha.fullmatch(version):
errors.append((path, n, ref, 'not a full 40-char SHA'))
for item in errors:
print(*item, sep=':')
sys.exit(1 if errors else 0)
python verify_pins.py
# Expected after repair: exit 0 and no findings.
8. Optional disposable run and evidence
If Docker is available on the hosted runner, dispatch the repaired
workflow after adding a tiny Dockerfile. Preserve the run ID,
attempt, source SHA, runner label/image, job conclusion and exact
workflow revision. Because push: false, verify there is
no package/registry publication or deployment record. The exact
image/build digest is build evidence, not dependency provenance;
record both separately.
FROM alpine:3.22
RUN printf 'supply-chain-checkpoint
' > /proof.txt
CMD ["cat", "/proof.txt"]
9. Configure controlled candidate updates
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
Require review for bot PRs. The candidate SHA change should carry an updated release comment, release-note link and dependency-register row.
10. Demonstrate a safe review/update diff
Do not invent a future version. Use a hypothetical candidate SHA in
a local branch or a real newer release when one exists. The review
sequence is fixed: resolve candidate tag → confirm upstream
repository → inspect release notes → compare old/new
action.yml and code/bundle → check
runtime/permissions/network implications → update
SHA/comment/register → run disposable validation.
# Example review commands; NEW must be a real candidate resolved from upstream.
OLD=37fe631027851001ddb9b187196cc803df7f5f0e
NEW=<candidate-full-sha>
git -C upstream-buildx diff --stat "$OLD" "$NEW"
git -C upstream-buildx diff "$OLD" "$NEW" -- action.yml src package.json package-lock.json
# Preserve the diff summary in evidence/ before accepting the workflow change.
11. Policy and free-path verification
If your repository/organization exposes the “Require actions to be pinned to a full-length commit SHA” policy, you may enable it only for an authorized disposable scope and record the policy result. Otherwise the local checker above is the faithful free simulation. Do not alter an employer or shared organization policy for this checkpoint.
12. Required evidence packet
| Evidence file/record | Required contents |
|---|---|
workflow-before.yml |
Original tag references and explicit permissions |
dependency-register.md |
Owner/repo, signal, release→SHA, runtime, permissions, secrets, network, review owner |
release-resolution.txt |
Read-only tag→commit lookups and timestamps |
source-review.md |
What metadata/code was inspected and limitations |
workflow-after.yml |
All external actions pinned to full SHAs with release comments |
pin-check.txt |
Local validator result or authorized GitHub policy denial/pass evidence |
| Run evidence if executed | Run ID, attempt, source SHA, runner/image, job result, no external push |
upgrade-review.md |
Candidate release mapping, old/new diff summary and acceptance decision |
13. Verification checklist
- Every third-party action reference has a 40-character upstream commit SHA.
- GitHub-authored action references are also pinned where the checkpoint policy requires all actions to be immutable.
- Release comments match the reviewed upstream tag mappings.
-
No secret, registry credential or
packages: writepermission exists. - Docker build remains local; no release, package, environment deployment or external target is changed.
- Dependency register captures runtime, network/side-effect assumptions and review owner.
- The original weak workflow and any first failure remain preserved.
14. Cleanup and rollback
Remove only disposable checkpoint files/repository resources you created. If the repaired workflow itself is the desired result, keep it. If an optional execution produced runner-local images, GitHub-hosted runner teardown removes that ephemeral machine state. If you enabled a temporary repository-scoped Actions policy solely for this lab, restore its exact prior value and record that rollback.
15. What Chapter 21 adds to a secure production model
The operating model now has an explicit chain for executable
dependencies: intended upstream owner/release → exact reviewed
commit → caller authority → runner execution → retained dependency
evidence → controlled update. This closes the gap between “workflow
YAML is reviewed” and “the code selected by uses: is
known.” Chapter 22 will apply the same distrust-by-default
discipline to pull requests, fork content and privileged triggers.
16. Checkpoint summary
You converted convenience references into auditable code identities, preserved the human release mapping, bounded the authority of each dependency, created a repeatable validator and designed upgrade review as a code/provenance diff. The workflow is now easier to reproduce, audit and contain if an upstream dependency later becomes risky.
Knowledge check
The repaired workflow uses full SHAs. Why does the register still record release tags?
The SHA determines exact code execution; the release tag provides human provenance and review context and helps controlled update tooling.
Why does the checkpoint preserve the unpinned workflow before fixing it?
It proves the original dependency state and makes the security correction independently reviewable instead of rewriting history.
The Docker action is from a Verified creator and pinned. Why
keep push: false and omit registry secrets?
Publisher signal and pinning do not justify broader side effects. The lab’s goal is dependency governance, so registry authority would add unnecessary blast radius.
What should happen if a new release asks for a new write permission?
Stop and reassess the dependency/use case. Verify the exact API need, isolate it if justified, review source and side effects, and never broaden authority automatically.
What trust boundary does Chapter 22 add next?
Untrusted pull-request and fork-controlled content/events, especially the separation between safe validation and privileged base-repository authority.
Official references and version notes
Version-sensitive GitHub Actions behavior in this lesson was rechecked on 2026-09-10. Re-verify release tags, commit mappings, runtime requirements and organization policy before adopting the examples in a production repository.
- GitHub Docs — Secure use reference
- GitHub Docs — Publishing actions in GitHub Marketplace and Verified creator badges
- GitHub Docs — Managing GitHub Actions settings and SHA-pinning policy
- GitHub Docs — Dependency graph support for GitHub Actions workflows
- GitHub Changelog — Actions policy SHA pinning and blocking
- GitHub Changelog — Node 20 deprecation / Node 24 migration
- Docker Setup Buildx v4.3.0 release
- Docker Build Push v7.3.0 release
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.