Checkpoint Lab — SBOMs, Component Provenance, Vulnerability Governance, Repository Firewall Concepts, and Supply-Chain Risk
Build three synthetic evidence packets, predict their dispositions, change only vulnerability intelligence for one component, and prove which facts are immutable. The deliverable is an auditable runbook/evidence bundle—not a screenshot of a green build.
Learning objectives
- Create three synthetic artifacts with coordinates/digests and retain their exact hashes.
- Build SBOM and provenance fixtures that distinguish first-party and third-party evidence quality.
- Evaluate time-stamped vulnerability/policy snapshots and classify allow, quarantine, and hold decisions.
- Prove which facts remain immutable when intelligence and governance decisions change later.
- Produce a verification/cleanup runbook and bridge the evidence model into Docker registry operations.
1. Checkpoint scenario
You are the artifact-platform operator for a small fictional service. Three components must be reviewed:
| Component | Origin | Initial evidence | Expected challenge |
|---|---|---|---|
| alpha-service 1.0.0 | Internal build | Artifact, SBOM, matching provenance subject, no known vulnerability in snapshot A. | Should remain allowed if evidence stays consistent. |
| bravo-lib 2.4.0 | Third party | Artifact + SBOM; no internal provenance claim; clean snapshot A. | Snapshot B introduces a synthetic high-severity vulnerability. |
| charlie-tool 3.1.0 | Internal build | Artifact + SBOM + provenance whose subject digest intentionally does not match. | Hold for identity/provenance investigation even if vulnerability snapshot is clean. |
2. Preflight and safety
-
Use Python 3 in a new local directory
chapter23-checkpoint. - Do not use real package namespaces, real CVEs, real signing keys, public registries, production Nexus, or employer SBOMs.
- Optional Nexus mapping must use disposable hosted repositories only.
- Do not delete/reclaim any real repository content.
3. Required predictions before execution
-
Alpha should be
allowbecause digest/provenance match and current synthetic risk is below threshold. -
Bravo should be
allowunder snapshot A andquarantineunder snapshot B while its artifact digest/SBOM remain unchanged. -
Charlie should be
holdbefore vulnerability policy because provenance subject mismatch breaks the claimed build-to-artifact link.
Write these predictions into predictions.md before
running the evaluator.
4. Create the complete checkpoint fixture
from pathlib import Path
import hashlib, json
root = Path('chapter23-checkpoint')
root.mkdir(exist_ok=True)
specs = {
'alpha-service-1.0.0.bin': b'alpha-service\nversion=1.0.0\nbuild=alpha-100\n',
'bravo-lib-2.4.0.bin': b'bravo-lib\nversion=2.4.0\nsource=third-party-fixture\n',
'charlie-tool-3.1.0.bin': b'charlie-tool\nversion=3.1.0\nbuild=charlie-310\n',
}
def sha(data): return hashlib.sha256(data).hexdigest()
digests = {}
for name, data in specs.items():
(root/name).write_bytes(data)
digests[name] = sha(data)
sbom = {
'bomFormat':'CycloneDX', 'specVersion':'1.6', 'version':1,
'components':[
{'type':'application','name':'alpha-service','version':'1.0.0','purl':'pkg:generic/learner-example/alpha-service@1.0.0'},
{'type':'library','name':'bravo-lib','version':'2.4.0','purl':'pkg:generic/learner-example/bravo-lib@2.4.0'},
{'type':'application','name':'charlie-tool','version':'3.1.0','purl':'pkg:generic/learner-example/charlie-tool@3.1.0'}
]
}
provenance = {
'alpha-service-1.0.0.bin': {
'_type':'https://in-toto.io/Statement/v1',
'subject':[{'name':'alpha-service-1.0.0.bin','digest':{'sha256':digests['alpha-service-1.0.0.bin']}}],
'predicateType':'https://slsa.dev/provenance/v1',
'predicate':{'runDetails':{'builder':{'id':'https://builder.example.invalid/internal-ci'}},
'buildDefinition':{'buildType':'https://builder.example.invalid/types/internal/v1','externalParameters':{'commit':'ALPHA_FAKE_COMMIT'}}}
},
'charlie-tool-3.1.0.bin': {
'_type':'https://in-toto.io/Statement/v1',
'subject':[{'name':'charlie-tool-3.1.0.bin','digest':{'sha256':'0'*64}}],
'predicateType':'https://slsa.dev/provenance/v1',
'predicate':{'runDetails':{'builder':{'id':'https://builder.example.invalid/internal-ci'}},
'buildDefinition':{'buildType':'https://builder.example.invalid/types/internal/v1','externalParameters':{'commit':'CHARLIE_FAKE_COMMIT'}}}
}
}
snapshot_a = {
'evaluatedAt':'2026-08-27T00:00:00Z',
'risk':{
'pkg:generic/learner-example/alpha-service@1.0.0':0.0,
'pkg:generic/learner-example/bravo-lib@2.4.0':0.0,
'pkg:generic/learner-example/charlie-tool@3.1.0':0.0
}
}
snapshot_b = json.loads(json.dumps(snapshot_a))
snapshot_b['evaluatedAt'] = '2026-09-26T00:00:00Z'
snapshot_b['risk']['pkg:generic/learner-example/bravo-lib@2.4.0'] = 8.8
(root/'digests.json').write_text(json.dumps(digests,indent=2)+'\n')
(root/'sbom.cdx.json').write_text(json.dumps(sbom,indent=2)+'\n')
(root/'provenance.json').write_text(json.dumps(provenance,indent=2)+'\n')
(root/'intel-a.json').write_text(json.dumps(snapshot_a,indent=2)+'\n')
(root/'intel-b.json').write_text(json.dumps(snapshot_b,indent=2)+'\n')
print(json.dumps(digests, indent=2))
5. Evaluate identity first, then mutable risk
from pathlib import Path
import hashlib, json
root = Path('chapter23-checkpoint')
digests = json.loads((root/'digests.json').read_text())
prov = json.loads((root/'provenance.json').read_text())
purls = {
'alpha-service-1.0.0.bin':'pkg:generic/learner-example/alpha-service@1.0.0',
'bravo-lib-2.4.0.bin':'pkg:generic/learner-example/bravo-lib@2.4.0',
'charlie-tool-3.1.0.bin':'pkg:generic/learner-example/charlie-tool@3.1.0',
}
def actual_digest(name):
return hashlib.sha256((root/name).read_bytes()).hexdigest()
def provenance_ok(name):
if name == 'bravo-lib-2.4.0.bin':
return None # third-party fixture: no internal provenance claim
return prov[name]['subject'][0]['digest']['sha256'] == actual_digest(name)
def evaluate(snapshot_name):
intel = json.loads((root/snapshot_name).read_text())
rows = []
for name, purl in purls.items():
identity_ok = actual_digest(name) == digests[name]
prov_ok = provenance_ok(name)
risk = intel['risk'][purl]
if not identity_ok or prov_ok is False:
decision = 'hold'
reason = 'identity/provenance mismatch'
elif risk >= 7.0:
decision = 'quarantine'
reason = 'synthetic risk threshold'
else:
decision = 'allow'
reason = 'identity checks pass; risk below threshold'
rows.append({'artifact':name,'sha256':actual_digest(name),
'provenanceOk':prov_ok,'risk':risk,
'decision':decision,'reason':reason,
'evaluatedAt':intel['evaluatedAt']})
return rows
for src, out in [('intel-a.json','decision-a.json'),('intel-b.json','decision-b.json')]:
result = evaluate(src)
(root/out).write_text(json.dumps(result,indent=2)+'\n')
print(src, [(r['artifact'], r['decision']) for r in result])
Expected:
intel-a.json [('alpha-service-1.0.0.bin', 'allow'), ('bravo-lib-2.4.0.bin', 'allow'), ('charlie-tool-3.1.0.bin', 'hold')]
intel-b.json [('alpha-service-1.0.0.bin', 'allow'), ('bravo-lib-2.4.0.bin', 'quarantine'), ('charlie-tool-3.1.0.bin', 'hold')]
6. Interpret the results correctly
- Alpha: nothing changed. Its exact bytes and evidence remain internally consistent.
- Bravo: its SHA-256 and SBOM entry are identical across both runs; only risk intelligence and policy disposition changed.
- Charlie: its vulnerability snapshot is clean, but it is still held because the claimed provenance subject does not match the bytes. “No CVE” cannot repair broken provenance.
7. Prove immutable versus mutable facts
| Fact | Alpha | Bravo | Charlie | Expected to change later? |
|---|---|---|---|---|
| Artifact SHA-256 | Fixed | Fixed | Fixed | No, unless bytes change. |
| SBOM name/version for this build | Fixed | Fixed | Fixed | No for archived build evidence. |
| Provenance subject | Fixed + matches | Not claimed internally | Fixed + intentionally mismatches | Do not rewrite historical evidence. |
| Vulnerability risk | 0 → 0 | 0 → 8.8 | 0 → 0 | Yes. |
| Disposition | allow → allow | allow → quarantine | hold → hold | Yes as evidence/policy changes. |
8. Required evidence packet
chapter23-checkpoint/
├── predictions.md
├── alpha-service-1.0.0.bin
├── bravo-lib-2.4.0.bin
├── charlie-tool-3.1.0.bin
├── digests.json
├── sbom.cdx.json
├── provenance.json
├── intel-a.json
├── intel-b.json
├── decision-a.json
├── decision-b.json
├── nexus-mapping.md
└── verification.md
In nexus-mapping.md, record where each
artifact/evidence object would live in a production design and
explicitly distinguish Nexus database/blob state from
SBOM/provenance/intelligence/policy state. In
verification.md, record the predictions, actual
decisions, hashes, and all checklist results.
9. Optional disposable Nexus mapping
If you have the disposable local Nexus instance from earlier
chapters, create a hosted repository specifically for harmless
checkpoint artifacts or use an existing disposable lab repository.
Publish the three files through the supported repository protocol,
calculate SHA-256 after downloading through Nexus, and prove the
downloaded digest matches digests.json. Store the
evidence files only if your chosen Raw/Maven evidence design is
deliberate.
Do not pretend that uploading
provenance.json to Nexus validates it. Nexus is
preserving bytes; provenance verification is a separate control.
10. Optional licensed IQ/Firewall mapping
In an authorized licensed environment only, map the fixture concepts to current Sonatype behavior: third-party component request → component intelligence → policy evaluation → repository disposition. Keep the fixture as the mandatory proof so learners do not need paid services or real risky packages. Never search for malware to create a quarantine demo.
11. Verification checklist
- All three artifact hashes were calculated from local bytes.
- Alpha provenance subject exactly matches Alpha SHA-256.
-
Charlie provenance subject intentionally mismatches and the
evaluator returns
hold. - Bravo SHA-256 is identical under both intelligence snapshots.
-
Bravo changes only from
allowtoquarantinebecause risk changed from 0.0 to 8.8. - The SBOM remains unchanged between evaluations.
- No real CVE/package/employer identifiers, credentials, public uploads, or production URLs are used.
- Nexus mapping clearly separates database/blob state from external evidence/governance state.
- Predictions were written before execution and compared with results.
12. Cleanup / rollback
Archive the evidence packet if desired, then delete only the local
chapter23-checkpoint directory. If an optional Nexus
lab was used, remove only the explicitly named disposable
repository/content through supported Nexus UI/API after inspecting
scope. Do not delete blob-store files or database rows manually. If
a licensed policy fixture was used, remove only the authorized test
configuration and restore its documented baseline.
13. Knowledge check
Why is Charlie held even though its vulnerability risk is 0?
Its provenance subject digest does not match the artifact bytes. Identity/provenance integrity is a separate gate from vulnerability status.
Which Bravo facts changed between snapshot A and B?
The risk intelligence and resulting policy disposition changed. The artifact bytes, SHA-256, name/version, and SBOM remained fixed.
Should you edit Charlie provenance to the correct digest and continue?
No. Preserve the mismatch and trace the authoritative build/evidence process. Rewriting the historical statement would hide the cause.
What would Repository Firewall add to the Bravo story in a licensed deployment?
A repository-boundary policy enforcement mechanism for supported proxy requests, potentially auditing/quarantining according to current policy/intelligence. It would not replace the SBOM or build provenance.
Why does the checkpoint use a synthetic CVE and no public package?
The learning objective is evidence and policy semantics, not handling malicious or risky software. Synthetic fixtures make the lab safe, reproducible, free, and legally/operationally isolated.
14. Chapter summary and bridge to Chapter 24
Chapter 23 completes the supply-chain evidence model around Nexus: exact artifact bytes are anchored by digests; SBOMs inventory components; provenance describes build origin/process; signatures and TLS provide different integrity/authentication properties; vulnerability intelligence evolves; and repository policy governs consumption without becoming proof of safety. The checkpoint proved that immutable and mutable facts must be stored separately but linked. Chapter 24 applies that discipline to Docker/OCI registry operations, where tags, manifests, layer digests, proxy caches, cleanup, retention, and production troubleshooting introduce another set of identity and reclamation traps.
Official references and version notes
- Sonatype: Software Bill of Materials (SBOM) — SBOM purpose, CycloneDX/SPDX support, analysis/export concepts, and point-in-time risk context.
- Sonatype: SBOM Best Practices — generate/store an SBOM for each application and do not use embedded vulnerability data as long-term risk state.
- Sonatype: CycloneDX Application Analysis — component identification by Package-URL, hashes, and coordinates plus dependency relationships.
- Sonatype: Repository Firewall — licensed repository-boundary policy enforcement and supported package ecosystems.
- Sonatype: Firewall Configuration for Nexus Repository — current repository-level enforcement model for recent Nexus versions.
- Sonatype: Nexus Repository Feature Matrix — Community/Pro entitlement boundaries; IQ/Firewall remain separate products/licensing.
- Sonatype: Nexus Repository System Requirements — current Java/database/runtime requirements.
- Sonatype nexus-public release 3.95.2-01 — dated Nexus baseline used by this chapter.
- CycloneDX: Specification Overview — current BOM object model, components, dependencies, hashes, and external references.
- CycloneDX 1.7 JSON Reference — current schema reference used to explain fields, not a claim that every Sonatype integration ingests every 1.7 feature.
- SLSA v1.2: Provenance — provenance as verifiable information describing where, when, and how software was produced.
- SLSA v1.2: Build Requirements — output identification by cryptographic digest and provenance-generation/integrity requirements.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.