Software Supply Chain Security: SBOMs, SLSA, and Provenance Attestation
Modern software is assembled, not written from scratch. The average application pulls in hundreds of open-source dependencies, each of which pulls in more. A single compromised package — or a tampered build pipeline — can cascade into thousands of production systems. Supply chain security addresses this risk surface.
The Attack Surface#
Supply chain attacks target the path between source code and production:
Developer ──▶ Source Repo ──▶ Build System ──▶ Artifact Registry ──▶ Deployment
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
Typosquat Compromised Injected build Tampered image Dependency
packages maintainer steps / secrets replacement confusion
High-profile incidents illustrate each vector:
- SolarWinds — Attackers injected malicious code during the build process.
- event-stream — A maintainer handoff introduced a cryptocurrency-stealing payload.
- Codecov — A compromised CI script exfiltrated secrets from customer builds.
- Log4Shell — A vulnerability in a transitive dependency affected millions of applications.
Software Bill of Materials (SBOM)#
An SBOM is a machine-readable inventory of every component in a software artifact: direct dependencies, transitive dependencies, versions, licenses, and suppliers.
Formats#
| Format | Maintainer | Strength |
|---|---|---|
| SPDX | Linux Foundation | License compliance, broad adoption |
| CycloneDX | OWASP | Security focus, VEX support |
| SWID | ISO/IEC | Software identification standard |
Generating SBOMs#
SBOMs can be generated at different stages:
- Source — Analyze manifest files (
package.json,go.mod,Cargo.lock). - Build — Capture resolved dependencies during compilation.
- Binary / Container — Scan the final artifact with tools like Syft or Trivy.
Build-time SBOMs are the most accurate because they reflect what actually shipped, not what the manifest declared.
# Generate an SBOM for a container image using Syft
syft packages registry.example.com/app:v2.1.0 -o spdx-json > sbom.spdx.json
# Scan the SBOM for known vulnerabilities using Grype
grype sbom:sbom.spdx.json
Dependency Scanning#
Dependency scanners match your dependency tree against vulnerability databases (NVD, OSV, GitHub Advisory Database) and flag known CVEs.
GitHub Dependabot#
Dependabot runs in three modes:
- Alerts — Notifies when a dependency has a known vulnerability.
- Security updates — Automatically opens pull requests to bump vulnerable packages.
- Version updates — Keeps dependencies current on a schedule, reducing the delta when a security patch drops.
Configuration lives in .github/dependabot.yml:
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
Snyk#
Snyk scans dependencies, container images, IaC templates, and source code. It integrates into the IDE, CI pipeline, and container registry. Snyk differentiates itself with reachability analysis — it checks whether your code actually calls the vulnerable function, reducing noise from theoretical-only vulnerabilities.
Other Scanners#
- Trivy — Open-source scanner for containers, filesystems, and git repositories.
- Grype — Vulnerability scanner from Anchore that pairs with Syft for SBOM-based scanning.
- OSV-Scanner — Google-backed scanner using the Open Source Vulnerabilities database.
SLSA Framework#
Supply-chain Levels for Software Artifacts (SLSA, pronounced "salsa") is a framework for increasing the integrity of your build pipeline. It defines four levels:
| Level | Requirements |
|---|---|
| SLSA 1 | Build process is documented and produces provenance. |
| SLSA 2 | Build runs on a hosted service with authenticated provenance. |
| SLSA 3 | Source and build platforms meet hardening requirements. |
| SLSA 4 | Hermetic, reproducible builds with two-party review. |
Each level adds guarantees against different threat categories: SLSA 1 prevents the "no provenance" problem. SLSA 3 prevents a compromised build from injecting code without detection.
Build Provenance#
Provenance is metadata that answers three questions:
- What was built? (artifact hash)
- How was it built? (build configuration, builder identity)
- From what source? (repository, commit, branch)
GitHub Actions can generate SLSA provenance automatically:
- uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
with:
base64-subjects: "${{ steps.hash.outputs.hashes }}"
The resulting provenance file is a signed JSON document that any consumer can verify before deploying the artifact.
Sigstore — Keyless Signing#
Sigstore provides free, open-source tooling for signing, verifying, and protecting software artifacts. Its key innovation is keyless signing: instead of managing long-lived GPG or PEM keys, developers authenticate through OIDC (GitHub, Google, Microsoft) and receive a short-lived certificate.
Components of the Sigstore ecosystem:
- Cosign — Signs and verifies container images and other OCI artifacts.
- Fulcio — Certificate authority that issues short-lived certificates tied to OIDC identity.
- Rekor — Transparency log that records signing events so they cannot be tampered with after the fact.
# Sign a container image with Cosign (keyless)
cosign sign registry.example.com/app:v2.1.0
# Verify the signature
cosign verify registry.example.com/app:v2.1.0 \
--certificate-identity=ci@example.com \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com
Container Image Signing#
Signing container images ensures that what you deploy is what you built. A typical pipeline:
Build ──▶ Push to Registry ──▶ Sign with Cosign ──▶ Attach SBOM ──▶ Attach Provenance
│
▼
Admission Controller
verifies signature
before allowing deploy
Kubernetes admission controllers like Kyverno and Sigstore Policy Controller reject unsigned or untrusted images at deploy time:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
rules:
- name: check-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "registry.example.com/*"
attestors:
- entries:
- keyless:
subject: "ci@example.com"
issuer: "https://token.actions.githubusercontent.com"
Lock Files and Reproducibility#
Lock files pin every dependency — direct and transitive — to an exact version and integrity hash. They are the first line of defense against supply chain drift.
| Ecosystem | Lock File | Integrity Hash |
|---|---|---|
| npm | package-lock.json | SHA-512 |
| Yarn | yarn.lock | SHA-512 |
| pnpm | pnpm-lock.yaml | SHA-512 |
| Go | go.sum | SHA-256 |
| Rust | Cargo.lock | SHA-256 |
| Python | poetry.lock | SHA-256 |
Rules for lock file hygiene:
- Always commit lock files to version control.
- Use
--frozen-lockfile(or equivalent) in CI to fail if the lock file is out of date. - Review lock file diffs — large, unexplained changes deserve scrutiny.
- Pin registries — Configure
.npmrcor equivalent to prevent dependency confusion attacks from public registries overriding private packages.
Provenance Attestation#
Attestation goes beyond signing. It binds structured claims — provenance, SBOM, vulnerability scan results — to an artifact using the in-toto attestation framework.
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [
{
"name": "registry.example.com/app",
"digest": { "sha256": "a1b2c3..." }
}
],
"predicateType": "https://slsa.dev/provenance/v1",
"predicate": {
"buildDefinition": {
"buildType": "https://github.com/slsa-framework/slsa-github-generator/generic@v2"
},
"runDetails": {
"builder": { "id": "https://github.com/slsa-framework/slsa-github-generator" }
}
}
}
npm, PyPI, and GitHub Container Registry now support publishing and verifying provenance attestations natively.
Putting It All Together#
A mature supply chain security posture layers multiple controls:
┌────────────────────────────────────────────────────────┐
│ Development │ Build / CI │ Deploy │
├───────────────────────┼─────────────────────┼──────────┤
│ Lock files │ SLSA provenance │ Image │
│ Dependabot / Snyk │ Hermetic builds │ signing │
│ Code review │ SBOM generation │ Admission│
│ Private registries │ Artifact signing │ policy │
│ Manifest pinning │ Reproducibility │ Runtime │
│ │ checks │ scanning│
└───────────────────────┴─────────────────────┴──────────┘
No single tool solves supply chain security. The goal is to make every link in the chain auditable, verifiable, and resistant to tampering — from the developer's workstation to the production cluster.
Article #344 in the Codelit engineering series. Explore our full library of system design, infrastructure, and security guides at codelit.io.
Try it on Codelit
GitHub Integration
Paste any repo URL to generate an interactive architecture diagram from real code
Related articles
Try these templates
Build this architecture
Generate an interactive architecture for Software Supply Chain Security in seconds.
Try it in Codelit →
Comments