Checkpoint Lab — Artifact Attestations, SBOM Provenance, Verification, and SLSA-Oriented Builds
Produce and verify an attested tiny artifact plus SBOM, prove tampering and wrong-identity checks fail, and document residual supply-chain risk.
Learning objectives
- Complete an end-to-end release-evidence checkpoint without production credentials or infrastructure.
- Predict subject, permission, attestation and verification state changes before execution.
- Generate provenance and SBOM attestations for one exact artifact and retain immutable action references.
- Demonstrate both tamper rejection and wrong-builder rejection while preserving first-failure evidence.
- Write a residual-risk statement that distinguishes provenance confidence from software-security confidence.
1. Checkpoint mission
You are preparing a tiny command-line asset for release from a disposable repository. Your gate requires two pieces of signed evidence: build provenance and an SPDX SBOM attestation. A consumer must verify the exact artifact against the intended repository and signer workflow before promotion. You will then prove that both byte tampering and wrong-signer policy are rejected.
No production package, registry, cloud environment or deployment is required. The live path uses a public disposable repository and GitHub-hosted runner. The fallback is an explicitly unsigned local simulation.
2. Preflight contract
| Check | Required state |
|---|---|
| Repo |
Disposable public gha-provenance-checkpoint; no
proprietary source/data.
|
| Branch | Use the repository default branch for the checkpoint; record exact ref/SHA. |
| Runner | ubuntu-24.04. |
| Actions |
Checkout v7.0.1 @
3d3c42e5aac5ba805825da76410c181273ba90b1;
Attest v4.2.2 @
1e69f48acb82d1966a394da916b4c1698aa569d6.
|
| Permissions |
contents: read, id-token: write,
attestations: write,
artifact-metadata: write; nothing broader.
|
| Credentials | No repository/cloud secrets. GitHub-provided token/OIDC only. |
| CLI |
Authenticated gh for your disposable account;
record actual version.
|
3. Predict state before running
Write your predictions before dispatching. At minimum predict these four changes: (1) a new workflow run/attempt will exist for one immutable source SHA; (2) the runner will create a new artifact digest from that source; (3) two attestation records with different predicate types will be associated with that digest; and (4) no deployment/package/release target will change.
Also predict two negative outcomes: changing one byte should break subject verification, and requiring an unrelated signer workflow should reject the original bytes.
4. Create the checkpoint source
Create the following source and build script. The artifact embeds the source SHA at build time so the evidence packet can correlate visible content with run metadata, while SHA-256 remains the authoritative byte identity.
mkdir -p src scripts .github/workflows
printf 'academy-provenance-checkpoint
' > src/app.txt
cat > scripts/build.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
rm -rf dist
mkdir -p dist
{
cat src/app.txt
printf 'source_sha=%s
' "${GITHUB_SHA:-local}"
} > dist/checkpoint.txt
sha256sum dist/checkpoint.txt > dist/checkpoint.txt.sha256
EOF
chmod +x scripts/build.sh
5. Exact workflow
This workflow builds, creates a standards-based toy SBOM and signs both provenance and SBOM claims. It records only non-secret outputs and never prints a raw OIDC token.
name: Provenance checkpoint
on:
workflow_dispatch:
permissions:
contents: read
id-token: write
attestations: write
artifact-metadata: write
jobs:
release-evidence:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Build exact subject
run: |
./scripts/build.sh
cat dist/checkpoint.txt.sha256
printf 'run=%s attempt=%s sha=%s ref=%s\n' \
"$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" "$GITHUB_SHA" "$GITHUB_REF"
python3 --version
gh --version | head -1
- name: Create SPDX 2.3 SBOM
run: |
python3 - <<'PY'
import hashlib, json, os, datetime
p='dist/checkpoint.txt'
d=hashlib.sha256(open(p,'rb').read()).hexdigest()
sbom={
'spdxVersion':'SPDX-2.3','dataLicense':'CC0-1.0',
'SPDXID':'SPDXRef-DOCUMENT','name':'gha-provenance-checkpoint',
'documentNamespace':f'https://example.invalid/spdx/{os.environ["GITHUB_SHA"]}',
'creationInfo':{'created':datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ'),
'creators':['Tool: gha-course-checkpoint']},
'files':[{'fileName':'dist/checkpoint.txt','SPDXID':'SPDXRef-File-checkpoint',
'checksums':[{'algorithm':'SHA256','checksumValue':d}],
'licenseConcluded':'NOASSERTION','copyrightText':'NOASSERTION'}]
}
open('dist/checkpoint.spdx.json','w').write(json.dumps(sbom,indent=2)+'\n')
PY
- name: Sign provenance claim
id: prov
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-path: dist/checkpoint.txt
- name: Sign SBOM claim
id: sbom
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-path: dist/checkpoint.txt
sbom-path: dist/checkpoint.spdx.json
- name: Evidence summary
env:
PROV_ID: ${{ steps.prov.outputs.attestation-id }}
PROV_URL: ${{ steps.prov.outputs.attestation-url }}
SBOM_ID: ${{ steps.sbom.outputs.attestation-id }}
SBOM_URL: ${{ steps.sbom.outputs.attestation-url }}
run: |
{
echo '## Checkpoint evidence'
echo "- run: $GITHUB_RUN_ID / attempt $GITHUB_RUN_ATTEMPT"
echo "- source: $GITHUB_SHA"
echo "- provenance attestation: $PROV_ID"
echo "- provenance URL: $PROV_URL"
echo "- SBOM attestation: $SBOM_ID"
echo "- SBOM URL: $SBOM_URL"
} >> "$GITHUB_STEP_SUMMARY"
6. Dispatch and preserve Run A
Commit the files, push to the disposable public repository and dispatch Run A. Do not rerun immediately if a step fails. First save the failure output, run ID, attempt, SHA, runner image metadata and action references. If the action fails because the repository is private or the feature is unavailable, document that prerequisite mismatch and use the simulation path rather than weakening permissions.
7. Recreate the exact artifact locally
Check out Run A's exact source SHA locally, export
GITHUB_SHA to the same SHA and run the deterministic
build. Compare its checksum with Run A. If they differ, stop: you do
not yet have the same subject and should not attempt verification.
git checkout RUN_A_SOURCE_SHA
export GITHUB_SHA=RUN_A_SOURCE_SHA
./scripts/build.sh
sha256sum dist/checkpoint.txt
cat dist/checkpoint.txt.sha256
8. Positive verification: bytes + expected builder identity
Record the CLI version, then verify the original subject against the expected repository and exact signer workflow path.
gh --version
gh attestation verify dist/checkpoint.txt --repo YOUR_OWNER/gha-provenance-checkpoint --signer-workflow YOUR_OWNER/gha-provenance-checkpoint/.github/workflows/provenance-checkpoint.yml
Save the complete verifier output as
verify-provenance-success.txt.
9. Verify the SBOM predicate separately
gh attestation verify dist/checkpoint.txt --repo YOUR_OWNER/gha-provenance-checkpoint --predicate-type https://spdx.dev/Document/v2.3
Record that this verifies the signed inventory claim for the subject; it does not certify vulnerability-free software.
10. Negative case 1 — tampered bytes
Preserve the valid file, create a tampered copy and capture the expected rejection without deleting the valid subject.
cp dist/checkpoint.txt dist/checkpoint.valid.txt
cp dist/checkpoint.txt dist/checkpoint.tampered.txt
printf 'tamper
' >> dist/checkpoint.tampered.txt
sha256sum dist/checkpoint.valid.txt dist/checkpoint.tampered.txt
gh attestation verify dist/checkpoint.tampered.txt --repo YOUR_OWNER/gha-provenance-checkpoint > verify-tamper.txt 2>&1 || true
cat verify-tamper.txt
11. Negative case 2 — wrong signer policy
Use the untouched valid subject but require a nonexistent/unapproved workflow identity.
gh attestation verify dist/checkpoint.valid.txt --repo YOUR_OWNER/gha-provenance-checkpoint --signer-workflow YOUR_OWNER/gha-provenance-checkpoint/.github/workflows/unapproved.yml > verify-wrong-signer.txt 2>&1 || true
cat verify-wrong-signer.txt
12. Required evidence packet
| Category | Required evidence |
|---|---|
| Source/event | workflow_dispatch, repository, ref, source SHA. |
| Run/job | Run ID, attempt, job/step conclusions. |
| Runner/tool | Hosted image metadata, Python version, actual gh CLI version. |
| Dependencies | Checkout/attest exact SHAs and mapped releases. |
| Permissions | Exact job/workflow permission block. |
| Subject | Original SHA-256 and filename. |
| Provenance | Attestation ID/URL + successful identity-constrained verification. |
| SBOM | SPDX JSON + attestation ID/URL + predicate verification. |
| Negative tests | Tampered digest/failure + wrong-signer failure. |
| External state | Statement that no package/release/deployment/cloud target changed. |
| Assumptions | Public disposable repo, current GitHub.com service, toy artifact/SBOM. |
13. Residual supply-chain risk statement
Finish with a short written risk statement. It must say that successful verification confirms the artifact digest is covered by attestations from the expected GitHub Actions identity under the tested policy. It must also state what remains unproven: source correctness, dependency safety, test adequacy, absence of vulnerabilities, runner/service compromise beyond the attestation model, SBOM completeness, and deployment/runtime health.
14. Faithful free/local fallback
If live artifact attestations cannot be used, run the same deterministic build and SBOM generation, then create an UNSIGNED SIMULATION provenance JSON with repository/workflow/ref/digest and test a local policy checker against valid, tampered and wrong-identity cases. Keep the language explicit: this demonstrates subject/policy behavior but does not create a GitHub/Sigstore signature or attestation ID.
15. Cleanup / rollback
After grading your evidence packet, delete the disposable repository if desired. If you experiment with attestation lifecycle deletion APIs, that is a separate destructive exercise: preserve the attestation IDs/bundles and first-failure evidence, guard the exact disposable repository and subject digest, and never delete evidence from production systems as routine troubleshooting.
16. Production contract and bridge to Chapter 24
Chapter 23 adds a release-integrity contract: exact source + immutable workflow dependencies → exact build bytes → digest-bound signed provenance/SBOM → identity-constrained consumer verification → policy decision. Chapter 24 now uses that disciplined evidence model to compose fast CI stages for build, test, lint and coverage without collapsing all quality signals into one opaque job.
Knowledge check
What two negative tests are required by this checkpoint?
Tamper the artifact bytes and require a wrong signer identity; both must be rejected while the original success evidence is preserved.
Why does the checkpoint not publish a release package?
The chapter isolates provenance/verification from distribution and deployment side effects. Those are independent states.
A valid provenance and SBOM both verify. Can the team claim the artifact has no vulnerabilities?
No. Provenance and SBOM authenticate origin/inventory claims; vulnerability/security analysis remains separate.
Why record the exact actions/attest commit
SHA?
To make the attestation implementation dependency immutable and auditable instead of relying on a movable tag.
What should happen before a consumer promotes the artifact?
Verify the exact digest plus expected signer/repository/predicate policy, then apply separate quality/security/deployment authorization controls.
Official references and version notes
- Artifact attestations concept — GitHub model for Sigstore-backed attestations, public/private signing roots and verification policy.
- Use artifact attestations — Current generation, permissions, binary/container and verification guidance.
- actions/attest v4.2.2 — Current consolidated GitHub action for provenance, SBOM and custom attestations.
- GitHub CLI attestation verify — Current identity, predicate, source-ref and signer-workflow verification controls.
- Verify attestations offline — Bundle and trusted-root workflow for disconnected verification.
- SLSA-oriented GitHub guidance — GitHub guidance connecting attestations and reusable workflows to stronger SLSA-oriented build controls.
- Workflow permissions — Current id-token, attestations, artifact-metadata and contents permission syntax.
- Manage attestation lifecycle — GitHub entry point for attestation generation, verification, offline use and lifecycle management.
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.