Chapter 21Lesson 05~225 minutes

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.

CheckpointDependency registerFull-SHA validationUpgrade diffChapter 22 bridge

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: write permission 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.

Next lesson

Secure Pull Requests, Forks, pull_request_target, and Untrusted Code: Core Concepts and Mental Model

Continue with the next lesson to build on the current concepts, evidence, security boundaries, and operational practices.

Knowledge check

The repaired workflow uses full SHAs. Why does the register still record release tags?

Why does the checkpoint preserve the unpinned workflow before fixing it?

The Docker action is from a Verified creator and pinned. Why keep push: false and omit registry secrets?

What should happen if a new release asks for a new write permission?

What trust boundary does Chapter 22 add next?

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.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.