Software Supply Chain Security: SBOM, SLSA, and Provenance
How to secure your software supply chain with SBOMs, SLSA build integrity levels, sigstore/cosign artifact signing, and verifying provenance in CI/CD.
Software Supply Chain Security: SBOM, SLSA, and Provenance
The SolarWinds, Log4Shell, and XZ Utils incidents made software supply chain security a board-level concern. In each case, the attack vector was not the target's code — it was a trusted dependency, build tool, or update mechanism. Modern applications are 80-90% open-source software by volume. The security of that software — and the integrity of the build pipeline that assembles it — is now as important as the security of the code you write yourself.
This guide covers the three pillars of supply chain security: knowing what is in your software (SBOM), ensuring your build pipeline cannot be tampered with (SLSA), and verifying that artifacts are what they claim to be (provenance signing).
What Is a Software Supply Chain Attack?
A supply chain attack compromises software before it reaches the end user by targeting:
- Open-source dependencies — Malicious packages published to npm, PyPI, or RubyGems (typosquatting, account takeover, malicious updates)
- Build infrastructure — Compromising CI/CD systems to inject malicious code at build time (SolarWinds pattern)
- Update mechanisms — Distributing malicious updates through legitimate channels
- Transitive dependencies — The dependency of a dependency of a dependency contains malware
The attack surface is massive. The average Node.js application has 1,000+ transitive dependencies. A vulnerability or compromise in any one of them affects every application that depends on it.
SBOM: Software Bill of Materials
An SBOM is a machine-readable inventory of all components in a software artifact — packages, libraries, versions, licenses, and relationships. It is the software equivalent of a nutrition label.
Why SBOMs matter:
When a new vulnerability is announced (Log4Shell, Spring4Shell, OpenSSL), an SBOM lets you instantly determine which applications are affected. Without an SBOM, answering "do we use Log4j?" requires auditing dozens of repositories manually.
US Executive Order 14028 (2021) mandates SBOMs for software sold to the federal government. CISA has published guidance that SBOMs are a baseline for critical infrastructure software.
SBOM formats:
- SPDX (Software Package Data Exchange) — Linux Foundation standard, ISO/IEC 5962
- CycloneDX — OWASP standard, richer security metadata, supports VEX (Vulnerability Exploitability eXchange)
- SWID — ISO/IEC 19770-2, used in enterprise software licensing
Generating SBOMs:
For containers:
# Syft (Anchore) — generates SPDX or CycloneDX SBOM from container images
syft myapp:latest -o cyclonedx-json > sbom.json
# Trivy — generates SBOM as part of vulnerability scan
trivy image --format cyclonedx myapp:latest > sbom.json
For Node.js projects:
# CycloneDX Node.js module
npx @cyclonedx/cyclonedx-npm --output-format JSON --output-file sbom.json
For Python:
# pip-audit with SBOM output
cyclonedx-py --output sbom.json
Integrating SBOM into CI/CD:
- name: Generate SBOM
run: syft ${{ env.IMAGE_NAME }}:${{ github.sha }} -o cyclonedx-json > sbom.json
- name: Scan SBOM for vulnerabilities
run: grype sbom:./sbom.json --fail-on high
- name: Attach SBOM to release
uses: actions/upload-artifact@v4
with:
name: sbom-${{ github.sha }}
path: sbom.json
VEX (Vulnerability Exploitability eXchange):
VEX is a companion to SBOM. Where SBOM says "this software contains component X," VEX says "component X has CVE-Y, but it is not exploitable in this product because the vulnerable function is never called." This dramatically reduces false positives from SBOM-based vulnerability scanning.
SLSA: Supply-chain Levels for Software Artifacts
SLSA (pronounced "salsa") is a framework of security requirements for the build pipeline itself. It ensures that the artifact you deploy was produced by the build process you think it was, with the code you think it contains.
SLSA defines four levels of increasing assurance:
| Level | Requirements | Protection Against |
|---|---|---|
| SLSA 1 | Documented build process, provenance generated | Accident, ad-hoc builds |
| SLSA 2 | Hosted build service (GitHub Actions, Cloud Build), signed provenance | Tampering post-build |
| SLSA 3 | Source + build both auditable, builds run in isolated, ephemeral environments | Compromised build system |
| SLSA 4 | Two-party code review, hermetic reproducible builds | Insider threats, insider compromise |
Most organizations should target SLSA Level 2 as a practical baseline — it requires a hosted build service and signed provenance, both of which are achievable with standard CI/CD tooling.
Generating SLSA provenance with GitHub Actions:
name: Build and Attest
on:
push:
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v4
- name: Build container
id: build
run: |
docker build -t myapp:${{ github.ref_name }} .
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' myapp:${{ github.ref_name }})
echo "digest=${DIGEST}" >> $GITHUB_OUTPUT
provenance:
needs: [build]
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
with:
image: myregistry/myapp
digest: ${{ needs.build.outputs.digest }}
secrets:
registry-username: ${{ secrets.REGISTRY_USERNAME }}
registry-password: ${{ secrets.REGISTRY_PASSWORD }}
This uses the SLSA GitHub Generator project to produce SLSA Level 3 provenance automatically.
Sigstore: Keyless Signing with cosign
Sigstore is a set of open-source tools for signing and verifying software artifacts without managing long-lived private keys. It uses short-lived certificates tied to OIDC identity (GitHub Actions, Google, Microsoft) and records signatures in a public transparency log (Rekor).
cosign is the Sigstore tool for signing container images and other OCI artifacts.
Signing a container image (keyless, in CI):
- name: Install cosign
uses: sigstore/cosign-installer@v3.5.0
- name: Sign the image
run: |
cosign sign --yes ${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
env:
COSIGN_EXPERIMENTAL: 1 # Keyless signing via GitHub OIDC
The signature is stored in the container registry alongside the image. The transparency log entry in Rekor provides a public audit trail — anyone can verify that the signature existed at a specific time.
Verifying an image before deployment:
cosign verify \
--certificate-identity-regexp "https://github.com/my-org/my-repo" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
myregistry/myapp@sha256:abc123...
Using cosign in a Kubernetes admission controller:
Policy Controller (from Sigstore) enforces image signing at admission time:
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: require-signed-images
spec:
images:
- glob: "myregistry/**"
authorities:
- keyless:
url: https://fulcio.sigstore.dev
identities:
- issuer: https://token.actions.githubusercontent.com
subjectRegExp: "https://github.com/my-org/.*"
Any pod attempting to use an unsigned image from myregistry will be rejected by the admission controller.
Dependency Management Best Practices
Supply chain security starts with your dependency choices:
-
Pin exact versions in lockfiles (
package-lock.json,poetry.lock,go.sum) and commit them. Floating versions (^1.2.0) allow automatic upgrades to potentially malicious versions. -
Use digest-pinned images in container builds:
node:20-alpine@sha256:abc...instead ofnode:20-alpine. Tags are mutable; digests are not. -
Enable Dependabot or Renovate for automated dependency updates. Security patches for dependencies are worthless if they are never applied.
-
Monitor for malicious packages with tools like Socket Security (real-time npm/PyPI analysis), Phylum, and Snyk Open Source.
-
Private package registries with an allow list of approved packages reduce the risk of typosquatting and namespace confusion attacks.
Supply chain security is not a single control — it is a collection of overlapping defenses. SBOMs give you visibility. SLSA gives you build integrity. Sigstore gives you verification. Together, they make it dramatically harder for an attacker to compromise your software without detection.