Tutorial: SBOM as evidence
Customers and regulators increasingly ask for a Software Bill of Materials as part of attestation. Pencheff produces SPDX 2.3 and CycloneDX 1.5 from any repository using the same manifest parsers that drive SCA — so the SBOM and the findings list are consistent by construction.
Scenario
- Customer ask. Acme Corp’s vendor security questionnaire requires an SBOM “in CycloneDX 1.5 format” for every product release.
- Constraint. The SBOM has to match the production build, not a developer’s laptop.
- Goal. Auto-attached, immutable, machine-readable SBOMs on every release tag.
1. Wire SBOM generation into the release tag
Generation runs from a release-tag GitHub workflow so it’s tied to the build that actually ships:
# .github/workflows/release.yml
name: release
on:
push:
tags: [ "v*.*.*" ]
jobs:
sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Re-generate SBOM and upload as release artifact
env:
PENCHEFF_API_KEY: ${{ secrets.PENCHEFF_API_KEY }}
PENCHEFF_API_BASE: ${{ secrets.PENCHEFF_API_BASE }}
PENCHEFF_REPO_ID: ${{ secrets.PENCHEFF_REPO_ID }}
run: |
curl -X POST -H "Authorization: Bearer $PENCHEFF_API_KEY" \
"$PENCHEFF_API_BASE/repos/$PENCHEFF_REPO_ID/sbom"
curl -H "Authorization: Bearer $PENCHEFF_API_KEY" \
"$PENCHEFF_API_BASE/repos/$PENCHEFF_REPO_ID/sbom" \
-o sbom-cyclonedx.json
- uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom-cyclonedx.jsonPOST /repos/{id}/sbom regenerates the SBOM from the latest commit
on the default branch and replaces the previous one; GET pulls it
back as JSON. The SBOM also re-renders on the repo’s page in
Table + JSON views.
2. Pull the SBOM after a release
curl -H "Authorization: Bearer $PENCHEFF_API_KEY" \
"$PENCHEFF_API_BASE/repos/$REPO_ID/sbom" \
-o acme-api-sbom-cyclonedx.jsonThe latest SBOM is durable on the repo row — subsequent generations replace it. For per-release immutability, attach the artefact you uploaded in step 1 to the GitHub Release directly.
3. Use it as audit evidence
- NIST 800-53 SR-3 — “Supply Chain Controls and Processes.” The SBOM is the source-of-record for components and suppliers.
- NIST 800-53 SC-12 — “Cryptographic Key Establishment.” Crypto libs in the SBOM are the inputs the auditor checks against your approved list.
- SOC 2 CC8.1 — “Change Management.” The SBOM diff between two release tags is your change ledger.
- ISO 27001:2022 A.5.21 — “Managing Information Security in the ICT Supply Chain.” Same SBOM, framed as a supplier-management artefact.
The compliance rollup at /repos/scans/{id}/compliance cites these
controls automatically when the SCA pass produces findings.
4. Diff two SBOMs for change-management evidence
The simplest path is to keep the SBOM from each release tag as a
GitHub Release asset and diff them with jq:
# Pull both SBOMs from your release tags into a single shell.
gh release download v1.41.0 -p sbom-cyclonedx.json -O sbom-old.json
gh release download v1.42.0 -p sbom-cyclonedx.json -O sbom-new.json
# Diff component-version pairs — the result is the change ledger.
jq -r '.components[] | "\(.purl)"' sbom-old.json | sort > old.txt
jq -r '.components[] | "\(.purl)"' sbom-new.json | sort > new.txt
diff old.txt new.txt > sbom-diff.txtThe diff highlights added components, removed components, and version bumps; drop the output straight into the release PR description for the SOC 2 CC8.1 ledger.
5. SBOM ↔ SCA cross-link
Each SCA finding card links to the SBOM component row that owns it. An auditor reading a CVE finding can pivot to the exact PURL + license + supplier without leaving the report.
Deliverable
- CycloneDX + SPDX SBOMs, immutably attached to every release tag.
- A diff artefact for SOC 2 CC8.1 change-management evidence.
- A compliance rollup that cites the SBOM for SR-3 / SC-12 / A.5.21.