This repo creates a Codespaces Environment for the follow along workshop for Practical Software Bill of Materials: From Generation to Distribution Workshop at VulnCon 2025
To help you get started, we have created a Gitpod environment that has all the tools you need to complete the workshop. To get started, click the link below to start the Gitpod environment.
- Start Codespaces Environment <-- Start Here
Alternatively you can install all of these tools and follow along with this README.md file.
No need to execute this in your Codespaces environment, install.sh will be called for you that contains these commands.
# Install Trivy for SBOM generation
brew install trivy
# Install sbomasm and sbomqs for SBOM augmentation and quality scoring
brew tap interlynk-io/interlynk
brew install sbomasm sbomqs
# Install parlay for SBOM enrichment
brew install parlay
# Install cosign for SBOM signing
brew install cosign
# To attest and convert the SBOM, fetch binaries for bnd and unpack
# for your local architecture (these are prereleases so they are not on
# homebrew yet, sorry!)
# https://github.com/carabiner-dev/bnd/releases/latest
# https://github.com/carabiner-dev/unpack/releases/tag/v0.1.0-pre5
# Install bomctl for SBOM sharing and distribution
brew tap bomctl/bomctl
brew install bomctl
# Install duckdb
brew install duckdb
# Install osv-scanner
brew install osv-scannerGenerate an SBOM for kubectl using Trivy.
curl -L -o /tmp/kubectl.tgz \
"https://github.com/kubernetes/kubectl/archive/refs/tags/v0.31.1.tar.gz"
tar xvf /tmp/kubectl.tgz -C .
trivy fs \
--format cyclonedx \
--skip-db-update \
--offline-scan \
--output generated.cdx.json \
kubectl-0.31.1This step should generate the following file:
generated.cdx.json
Add missing metadata to the generated SBOM using sbomasm. This step adds
metadata to the document and primary component.
# Augment the Generated CycloneDX with updated document information
sbomasm edit --subject Document \
--author 'VulnCon SBOM Generation Workshop' \
--supplier 'kubernetes (https://kubernetes.io/kubectl)' \
--lifecycle 'pre-build' \
--repository 'https://github.com/kubernetes/kubectl' \
--license 'Apache-2.0 (https://raw.githubusercontent.com/kubernetes/kubectl/refs/heads/master/LICENSE)' \
generated.cdx.json > augmented-docs-sbom.cdx.json
# Augment the Generated CycloneDX with updated primary component information
sbomasm edit --subject primary-component \
--author 'VulnCon SBOM Generation Workshop' \
--supplier 'kubernetes (https://kubernetes.io/kubectl)' \
--repository 'https://github.com/kubernetes/kubectl' \
--license 'Apache-2.0 (https://raw.githubusercontent.com/kubernetes/kubectl/refs/heads/master/LICENSE)' \
augmented-docs-sbom.cdx.json > augmented-sbom.cdx.jsonThe following files were created in this step:
augmented-docs-sbom.cdx.json- SBOM with updated document metadataaugmented-sbom.cdx.json- SBOM with updated document and primary component metadata
You can view a diff of the changes made to the SBOM by running the following command:
code --diff generated.cdx.json augmented-sbom.cdx.jsonEnrich the SBOM with additional metadata using parlay. This step adds
metadata to components.
parlay ecosystems enrich \
augmented-sbom.cdx.json > enriched-sbom.cdx.json
# Since parlay doesn't pretty its generated json, use jq to format it.
jq . enriched-sbom.cdx.json > enriched-sbom.pretty.cdx.jsonThe following files were created in this step:
enriched-sbom.cdx.json- the component metadata enriched SBOMenriched-sbom.pretty.cdx.json- the component metadata enriched SBOM, pretty printed
You can view a diff of the changes made to the SBOM by running the following command:
code --diff augmented-sbom.cdx.json enriched-sbom.pretty.cdx.jsonUse sbomqs to validate the SBOM. This step checks the SBOM for common issues.
sbomqs score generated.cdx.json
sbomqs score enriched-sbom.cdx.jsonAt this point we want to rename the SBOM to meet the OpenSSF naming recomendation schema. This is to make it easy to relate the SBOM with its released artifacts:
mv enriched-sbom.cdx.json kubectl-v0.31.1.cdx.jsonFor completeness and maximum compatibility, create the SPDX variant of our enriched SBOM:
unpack sbom kubectl-v0.31.1.cdx.json --format=spdx > kubectl-v0.31.1.spdx.jsonNext up, we'll sign the enriched software bill of materials. This step will create a detached certificate and signature fiel pair you can distribute to verify the SBOM.
Note
The following command will open the cosign signing flow, authenticate with your OIDC provider (Google/GitHub/Microsoft) and you are done. If you are following in the GitPod tutorial, you will need to copy + paste the URL in your browser and then put the resulting code in the terminal.
# Sign the CycloneDX variant
cosign sign-blob kubectl-v0.31.1.cdx.json \
--output-certificate=kubectl-v0.31.1.cdx.json.pem \
--output-signature=kubectl-v0.31.1.cdx.json.sig
# Sign the SPDX variant
cosign sign-blob kubectl-v0.31.1.spdx.json \
--output-certificate=kubectl-v0.31.1.spdx.json.pem \
--output-signature=kubectl-v0.31.1.spdx.json.sigVerify the signed SBOM with cosign verify:
cosign verify-blob kubectl-v0.31.1.cdx.json \
--certificate=kubectl-v0.31.1.cdx.json.pem \
--signature=kubectl-v0.31.1.cdx.json.sig Creating an attestation cryptographically binds the SBOM to the artifact it describes. In addition, attestations wrapped in bundles can be distributed with all their verification material in a single file which is often more convenient than the artifact+cert+signature trio.
# Create the attestation bundle, it will use as its subject the hash of the
# downloaded source tarball:
bnd predicate kubectl-v0.31.1.cdx.json \
--type=https://cyclonedx.org/bom \
--subject-file=/tmp/kubectl.tgz \
--out=kubectl-v0.31.1.cdx.bundle.json
# Same with the SPDX variant:
bnd predicate kubectl-v0.31.1.spdx.json \
--type=https://spdx.dev/Document \
--subject-file=/tmp/kubectl.tgz \
--out=kubectl-v0.31.1.spdx.bundle.jsonNow finaly, we should pack all attestations into a single distributable file:
# This packs both attestations into a linear json bundle
bnd pack -o kubectl-v0.31.1.bundle.jsonl \
kubectl-v0.31.1.cdx.bundle.json \
kubectl-v0.31.1.spdx.bundle.jsonAfter this step you will have a single file kubectl-v0.31.1.bundle.jsonl containing
both signed SBOMs wrapped in their attestations together with all the verification
material required to verify the documents.
Recepients of signed SBOMs should verify the authenticity and integrity of the documents. Throughout this tutorial we've produced two kinds of signed SBOMs. Let's verify them.
To verify the signature of an SBOM with a detached signature and its certificate invoke cosign:
cosign verify-blob kubectl-v0.31.1.cdx.json \
--certificate kubectl-v0.31.1.cdx.json.pem \
--signature kubectl-v0.31.1.cdx.json.sig \
--certificate-identity-regexp='.*' \
--certificate-oidc-issuer-regexp='.*'To verify a bundled attestation, you only need the bundle file:
# Verify the attestation with bnd:
bnd verify --skip-identity kubectl-v0.31.1.cdx.bundle.json
# Expected:
# ✅ Bundle Verification OK!
# Inspect the bundle's metadata
bnd inspect kubectl-v0.31.1.cdx.bundle.json
# Extract the unsigned attestation wrapping the SBOM:
bnd extract statement kubectl-v0.31.1.cdx.bundle.json
# > Note the subject data, binding the sbom to the file
Warning
Note that both examples, in cosign's case allowing any string with the regular
expression '.*' and in the second with --skip-identity we are not checking
the signer identity. The expected identity must be checked when verifying as
part of the policy.
This will recursively fetch the SBOM at this URL, and any internally referenced SBOMs (for this example it will fetch two SBOMs)
bomctl fetch https://raw.githubusercontent.com/bomctl/bomctl-playground/main/examples/bomctl-container-image/bomctl_bomctl_v0.3.0.cdx.jsonAfter fetching the SBOMs, you can list the SBOMs that have been fetched:
bomctl listView the SBOMs that have been fetched:
bomctl export https://anchore.com/syft/file/bomctl_0.3.0_linux_amd64.tar.gz-1b838d44-9d3c-47d0-9f7f-846397e701fa#DOCUMENTConvert between SBOM formats:
bomctl export -f cyclonedx https://anchore.com/syft/file/bomctl_0.3.0_linux_amd64.tar.gz-1b838d44-9d3c-47d0-9f7f-846397e701fa#DOCUMENTIf you want to run some GUAC demos, see: https://docs.guac.sh/setup-install/
Collect SBOMs:
# Fetch an SBOM
curl -LO https://raw.githubusercontent.com/SBOM-Community/VulnCon-SBOM-Workshop/refs/heads/main/examples/scalibr.spdx.json
# Look at the SBOM
cat scalibr.spdx.jsonCorrelate an SBOM with vulnerability data:
# OSV Scanner correlates data from the OSV vulnerability database
osv-scanner scan --sbom scalibr.spdx.json --format verticalUse DuckDB to understand all unique packages across multiple SBOMs:
duckdb
D CREATE TABLE sbom AS
· SELECT * FROM read_json_auto('*.json', auto_detect=true, maximum_object_size="333554428");
D SELECT DISTINCT package.unnest.name AS package_name
· FROM
· sbom,
‣ UNNEST(packages) AS package;Use OSV Scanner to fix any vulnerabilities in a project:
git clone https://github.com/whiskeytastingfoundation/wtf-frontend
cd wtf-frontend
osv-scanner fix \
--max-depth=3 \
--min-severity=5 \
--ignore-dev \
--strategy=in-place \
-L package-lock.json