Supply Chain Security: SLSA, SBOMs, Sigstore, and Dependency Pinning in CI/CD
Supply Chain Security: SLSA, SBOMs, Sigstore, and Dependency Pinning in CI/CD
Your application is not just your code. It is the hundreds of dependencies you pull in, the base images you build on, the CI runners that compile it, and the registries that distribute it. SolarWinds, Log4Shell, and the xz backdoor all exploited different links in this chain. This guide covers the four controls that address them: SLSA for build integrity, SBOMs for visibility, Sigstore for artifact signing, and dependency pinning for reproducibility.
The Threat Surface
| Attack | Vector | Control |
|---|---|---|
| Malicious dependency (typosquatting, account takeover) | Package registry | Pinning, lockfiles, private proxies |
| Compromised build system | CI runner, poisoned pipeline | SLSA provenance, isolated builds |
| Tampered artifact in transit or registry | Registry, CDN | Signing and verification (Sigstore) |
| Known-vulnerable dependency shipped | Any dependency | SBOM + vulnerability scanning |
| Malicious base image | Container registry | Digest pinning, signature verification |
Dependency Pinning and Lockfiles
Floating versions (^1.2.3, latest) mean every build can resolve to different code. Pin everything:
// package.json: exact versions, no ranges
{
"dependencies": {
"express": "4.21.2"
}
}
# Dockerfile: pin base images by digest, not tag
FROM node:22-alpine@sha256:51eff88af6dff26f59316b6e356188ffa2c422bd3c3b76f2556a2e7e89d080bd
# GitHub Actions: pin actions by commit SHA, not tag
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
Rules that matter:
- Commit lockfiles (
package-lock.json,poetry.lock,go.sum) and enforce them in CI withnpm ci,pip install --require-hashes, orgo mod verify. - Pin GitHub Actions by SHA. Tags are mutable; a compromised action maintainer can retag malicious code to a version you already trust.
- Use a private registry proxy (Artifactory, Nexus, or a pull-through cache) so a package being yanked or poisoned upstream does not immediately hit your builds.
- Review dependency diffs. Tools like
npm diffor Socket flag new install scripts, network calls, and obfuscated code in updated packages.
SBOMs: Know What You Ship
A Software Bill of Materials is a machine-readable inventory of every component in your artifact. When the next Log4Shell drops, an SBOM is the difference between querying a database and grepping a thousand repos.
Generate one at build time with Syft (SPDX or CycloneDX format):
# From a container image
syft myapp:1.4.2 -o cyclonedx-json=sbom.cdx.json
# From a source directory
syft dir:. -o spdx-json=sbom.spdx.json
In GitHub Actions:
- name: Generate SBOM
uses: anchore/sbom-action@f8ec1d277b33c5c1391d3447c4d4e0df4b16f8a8 # v0.17.2
with:
image: ${{ env.IMAGE }}
format: cyclonedx-json
output-file: sbom.cdx.json
- name: Scan SBOM for vulnerabilities
uses: anchore/scan-action@3887ce3b8f8b72410a2648a1b4f4bca4f8f1b8e0 # v6.1.0
with:
sbom: sbom.cdx.json
fail-build: true
severity-cutoff: high
Scan the SBOM, not just the repo: Grype or Trivy against the SBOM catches vulnerabilities in transitive dependencies and OS packages inside your image that source-level scanners miss. Attach the SBOM to the release artifact so consumers can verify what they are running.
Sigstore: Keyless Signing and Verification
Traditional signing fails on key management: private keys leak, get lost, or never get rotated. Sigstore removes the key problem entirely:
- Cosign signs containers and blobs
- Fulcio issues short-lived certificates bound to an OIDC identity (your CI workflow)
- Rekor records signatures in a transparency log
Keyless signing in GitHub Actions uses the workflow's OIDC token, so there is no private key to manage:
permissions:
id-token: write # OIDC token for keyless signing
contents: read
steps:
- name: Build and push image
run: |
docker build -t ghcr.io/example/myapp:${{ github.sha }} .
docker push ghcr.io/example/myapp:${{ github.sha }}
- name: Sign image
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.8.1
- run: |
cosign sign --yes ghcr.io/example/myapp:${{ github.sha }}
Consumers verify before deploying:
cosign verify ghcr.io/example/myapp@sha256:abc123... \
--certificate-identity-regexp "https://github.com/example/myapp/.github/workflows/release.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
Verification proves the image was built by your specific workflow on your specific repo, not just by someone with a stolen key. Enforce it at deploy time with a Kubernetes admission controller (Kyverno, or Sigstore's policy-controller) that rejects unsigned images.
SLSA: Provenance for Build Integrity
SLSA (Supply-chain Levels for Software Artifacts) defines how much you can trust that an artifact came from the claimed source via a tamper-resistant build:
| Level | Requirement | What It Defeats |
|---|---|---|
| L1 | Provenance exists | Nothing by itself; documentation only |
| L2 | Hosted build platform, signed provenance | Artifact tampering after build |
| L3 | Hardened, isolated builds | Build-time tampering, cross-build contamination |
Provenance is a signed statement recording the source repo, commit, build steps, and builder identity. The easiest path to L3 is GitHub's reusable SLSA generator workflow:
# .github/workflows/release.yml
jobs:
build:
outputs:
digests: ${{ steps.build.outputs.digests }}
steps:
- name: Build and push
id: build
run: |
docker build -t ghcr.io/example/myapp:${{ github.sha }} .
docker push ghcr.io/example/myapp:${{ github.sha }}
echo "digests=$(docker inspect --format='{{index .RepoDigests 0}}' ghcr.io/example/myapp:${{ github.sha }} | cut -d'@' -f2)" >> "$GITHUB_OUTPUT"
provenance:
needs: [build]
permissions:
actions: read
id-token: write
contents: write
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
with:
image: ghcr.io/example/myapp
digest: ${{ needs.build.outputs.digests }}
Verify provenance before deployment:
slsa-verifier verify-image ghcr.io/example/myapp@sha256:abc123... \
--source-uri github.com/example/myapp \
--source-tag v1.4.2
Putting It Together: A Hardened Pipeline
The controls compose into a single pipeline:
- Build: pinned dependencies and lockfiles, SHA-pinned actions, digest-pinned base images
- Inventory: SBOM generated from the final image, scanned for vulnerabilities, build fails on high severity
- Sign: Cosign keyless signing of the image and the SBOM
- Provenance: SLSA L3 provenance generated by an isolated reusable workflow
- Deploy gate: admission controller verifies signature and provenance before any pod starts
# Kyverno policy: only allow signed images from our repo
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signatures
spec:
validationFailureAction: Enforce
rules:
- name: require-cosign-signature
match:
any:
- resources:
kinds: [Pod]
verifyImages:
- imageReferences: ["ghcr.io/example/*"]
attestors:
- entries:
- keyless:
subject: "https://github.com/example/*"
issuer: "https://token.actions.githubusercontent.com"
Adoption Checklist
- Pin all dependencies, actions, and base images; enforce lockfiles in CI
- Generate an SBOM for every release artifact and scan it for vulnerabilities
- Sign images with Cosign keyless signing; verify signatures at deploy time
- Add SLSA provenance via reusable workflows; verify before promotion
- Run a private registry proxy to buffer upstream package registry incidents
- Alert on new CVEs against stored SBOMs, not just at build time
Start with pinning and SBOMs (hours of work, immediate visibility), then add signing, then provenance. Each layer assumes the previous one exists.
MatterAI builds frontier AI infrastructure for engineering teams — from inference-optimized models to autonomous coding agents and agentic code reviews.
Explore what we're building:
- Orbital IDE — Autonomous AI coding agent with background agents and deep codebase memory
- AI Code Reviews — Agentic pre-commit reviews across GitHub, GitLab, and Bitbucket
- Axon Models — Frontier-grade reasoning models at 70% lower inference cost
Share this Guide:
More Guides
Grafana Stack vs SigNoz: Choosing an OpenTelemetry-Native Observability Platform
Compare the Grafana LGTM stack (Loki, Tempo, Mimir) with SigNoz for OpenTelemetry observability: architecture, storage engines, operational overhead, cost at scale, and migration paths.
10 min readPrompt Injection Defense: Securing LLM Apps Against Jailbreaks, RAG Poisoning, and Tool-Use Exploits
Defend production LLM applications against direct jailbreaks, indirect injection via RAG pipelines, and tool-use exploits with layered defenses, input filtering, and least-privilege tool design.
10 min readRate Limiting and Backpressure: Token Bucket vs Leaky Bucket, Distributed Rate Limiters with Redis
Implement rate limiting and backpressure for high-traffic APIs with token bucket and leaky bucket algorithms, sliding window counters, and atomic distributed rate limiters using Redis and Lua.
9 min readSecrets Management at Scale: Vault vs AWS Secrets Manager vs SOPS, Rotation and Dynamic Secrets
Compare HashiCorp Vault, AWS Secrets Manager, and SOPS for secrets management at scale: static vs dynamic secrets, rotation strategies, Kubernetes integration, and access control models.
11 min readLLM Integration for AI Agents: A Complete Engineering FAQ
Everything engineers need to know about integrating, testing, and productionizing LLMs in AI agents: model selection, tool calling, structured outputs, error handling, observability, and cost optimization.
22 min readContinue Reading
Grafana Stack vs SigNoz: Choosing an OpenTelemetry-Native Observability Platform
Compare the Grafana LGTM stack (Loki, Tempo, Mimir) with SigNoz for OpenTelemetry observability: architecture, storage engines, operational overhead, cost at scale, and migration paths.
10 min readPrompt Injection Defense: Securing LLM Apps Against Jailbreaks, RAG Poisoning, and Tool-Use Exploits
Defend production LLM applications against direct jailbreaks, indirect injection via RAG pipelines, and tool-use exploits with layered defenses, input filtering, and least-privilege tool design.
10 min readRate Limiting and Backpressure: Token Bucket vs Leaky Bucket, Distributed Rate Limiters with Redis
Implement rate limiting and backpressure for high-traffic APIs with token bucket and leaky bucket algorithms, sliding window counters, and atomic distributed rate limiters using Redis and Lua.
9 min readShip Faster. Ship Safer.
Join thousands of engineering teams using MatterAI to autonomously build, review, and deploy code with enterprise-grade precision.
