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.
Continue learning
Go deeper on security
As an Amazon Associate I earn from qualifying purchases. Codelit may receive a commission at no extra cost to you.
Real-World Cryptography
David Wong · 2021
Which primitive to pick for TLS, JWTs or end-to-end messaging, and what breaks when you pick wrong.
4.7 (100)#14 in Client-Server Networking SystemsZero Trust Networks
Razi Rais, Christina Morillo, Evan Gilman, Doug Barth · 2024
How to migrate off the perimeter, with rollout case studies and the trust-engine design in detail.
4.4 (16)Designing Data-Intensive Applications
Martin Kleppmann, Chris Riccomini · 2026
Traces replication, consistency and consensus end to end, with the failure modes behind each choice.
3.6 (196)#1 in Data MiningBuilding Microservices
Sam Newman · 2021
600 pages on splitting a monolith: service boundaries, data decomposition, migration order.