Cybersecurity & Compliance

Supply Chain Security: SLSA, SBOMs, Sigstore, and Dependency Pinning in CI/CD

MatterAI
MatterAI
10 min read·

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

AttackVectorControl
Malicious dependency (typosquatting, account takeover)Package registryPinning, lockfiles, private proxies
Compromised build systemCI runner, poisoned pipelineSLSA provenance, isolated builds
Tampered artifact in transit or registryRegistry, CDNSigning and verification (Sigstore)
Known-vulnerable dependency shippedAny dependencySBOM + vulnerability scanning
Malicious base imageContainer registryDigest 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:

  1. Commit lockfiles (package-lock.json, poetry.lock, go.sum) and enforce them in CI with npm ci, pip install --require-hashes, or go mod verify.
  2. Pin GitHub Actions by SHA. Tags are mutable; a compromised action maintainer can retag malicious code to a version you already trust.
  3. 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.
  4. Review dependency diffs. Tools like npm diff or 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:

LevelRequirementWhat It Defeats
L1Provenance existsNothing by itself; documentation only
L2Hosted build platform, signed provenanceArtifact tampering after build
L3Hardened, isolated buildsBuild-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:

  1. Build: pinned dependencies and lockfiles, SHA-pinned actions, digest-pinned base images
  2. Inventory: SBOM generated from the final image, scanned for vulnerabilities, build fails on high severity
  3. Sign: Cosign keyless signing of the image and the SBOM
  4. Provenance: SLSA L3 provenance generated by an isolated reusable workflow
  5. 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

  1. Pin all dependencies, actions, and base images; enforce lockfiles in CI
  2. Generate an SBOM for every release artifact and scan it for vulnerabilities
  3. Sign images with Cosign keyless signing; verify signatures at deploy time
  4. Add SLSA provenance via reusable workflows; verify before promotion
  5. Run a private registry proxy to buffer upstream package registry incidents
  6. 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

Get started free - https://app.matterai.so


Follow us on X · LinkedIn · GitHub

Share this Guide:

Ship Faster. Ship Safer.

Join thousands of engineering teams using MatterAI to autonomously build, review, and deploy code with enterprise-grade precision.

No credit card requiredSOC 2 Type IISetup in 2 min