SLSA Framework: Achieving Supply Chain Integrity Levels

SLSA Framework: Achieving Supply Chain Integrity Levels

SLSA (Supply-chain Levels for Software Artifacts, pronounced "salsa") is a security framework from Google that provides a common vocabulary and checklist for supply chain integrity. Rather than describing a single policy, SLSA defines four progressively stricter levels — allowing organizations to incrementally improve their supply chain security posture.

Why SLSA Exists

Software supply chain attacks have increased dramatically. SolarWinds, CodeCov, and the XZ Utils backdoor all exploited the same weakness: the gap between "we trust this software" and "we have proof this software is what it claims to be."

SLSA addresses this by asking: can you prove how your software was built?

The framework was developed by Google based on internal practices (their Binary Authorization system), then open-sourced and standardized by the Open Source Security Foundation (OpenSSF).

The Four SLSA Levels

SLSA 1: Documented Build Process

Goal: Basic build provenance exists.

Requirements:

  • The build process is scripted (not manual)
  • Provenance is generated (but not necessarily verified)
  • Provenance exists as a document that consumers can review

SLSA 1 is achievable by most teams using any CI system. The key artifact is provenance: a document stating what inputs went into the build, what build steps ran, and what outputs were produced.

// Minimal SLSA provenance (simplified)
{
  "_type": "https://in-toto.io/Statement/v0.1",
  "predicateType": "https://slsa.dev/provenance/v0.2",
  "subject": [{
    "name": "myapp",
    "digest": {"sha256": "abc123..."}
  }],
  "predicate": {
    "builder": {"id": "https://github.com/actions/runner"},
    "buildType": "https://github.com/slsa-framework/slsa-github-generator/...",
    "invocation": {
      "configSource": {
        "uri": "git+https://github.com/myorg/myrepo",
        "digest": {"sha1": "def456..."},
        "entryPoint": ".github/workflows/release.yaml"
      }
    },
    "buildConfig": {},
    "materials": [{
      "uri": "git+https://github.com/myorg/myrepo",
      "digest": {"sha1": "def456..."}
    }]
  }
}

SLSA 2: Hosted Build Service

Goal: Tamper resistance in the build process.

Requirements (everything from SLSA 1, plus):

  • Build runs on a hosted CI service (not developer machines)
  • Provenance is generated by the CI service itself
  • Provenance is signed by the build service

SLSA 2 prevents someone from claiming a build was done by CI when it was actually done on a compromised developer laptop. The CI service's signature on the provenance provides that assurance.

Most GitHub Actions, GitLab CI, and Google Cloud Build pipelines can reach SLSA 2 with relatively minor changes.

SLSA 3: Hardened Builds

Goal: Resist insider threats and compromised CI.

Requirements (everything from SLSA 2, plus):

  • Build runs in an isolated environment (each build starts fresh)
  • Build inputs are fully declared in advance
  • No network access after dependencies are resolved (ideally)
  • Provenance cannot be falsified even by the build service operator
  • Source is version controlled and the specific commit is captured in provenance

SLSA 3 is where most organizations should aim for critical software. The key distinction from SLSA 2: even if someone compromised the build service, they couldn't generate valid provenance for a tampered build.

SLSA 4: Two-Party Review + Hermetic Builds

Goal: High assurance against insider attacks.

Requirements (everything from SLSA 3, plus):

  • All code changes require review by a different person (no solo merges)
  • Build is fully hermetic (completely offline, all dependencies pre-fetched)
  • Build is fully reproducible (same inputs always produce same outputs)
  • Provenance includes all build parameters

SLSA 4 is the highest level and currently difficult to achieve in practice. Full hermeticity is hard when builds pull from the internet. It's appropriate for highly sensitive software — cryptographic libraries, security tools, CI infrastructure itself.

Achieving SLSA Levels in Practice

GitHub Actions: SLSA 3 with slsa-github-generator

The slsa-github-generator project provides reusable workflows that produce SLSA 3 provenance:

# .github/workflows/release.yaml
name: Release

on:
  push:
    tags: ['v*']

permissions:
  actions: read
  contents: write
  id-token: write
  packages: write

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      digest: ${{ steps.build.outputs.digest }}
    steps:
      - uses: actions/checkout@v4
      
      - name: Build and push image
        id: build
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }}

  # SLSA provenance generation (SLSA 3)
  provenance:
    needs: build
    permissions:
      actions: read
      id-token: write
      packages: write
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0
    with:
      image: ghcr.io/${{ github.repository }}
      digest: ${{ needs.build.outputs.digest }}
    secrets:
      registry-username: ${{ github.actor }}
      registry-password: ${{ secrets.GITHUB_TOKEN }}

This produces SLSA 3 provenance, signs it with Sigstore, and stores it alongside the image in the OCI registry.

Go Binary: SLSA 3

name: Release Go Binary

on:
  push:
    tags: ['v*']

permissions:
  id-token: write
  contents: write
  actions: read

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      hashes: ${{ steps.hash.outputs.hashes }}
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-go@v5
        with:
          go-version: '1.22'
      
      - name: Build
        run: |
          go build -trimpath -o myapp-linux-amd64 ./cmd/myapp
          go build -trimpath -o myapp-darwin-amd64 ./cmd/myapp
      
      - name: Generate hashes
        id: hash
        run: |
          sha256sum myapp-linux-amd64 myapp-darwin-amd64 > hashes.txt
          echo "hashes=$(base64 -w0 < hashes.txt)" >> "$GITHUB_OUTPUT"
      
      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: binaries
          path: |
            myapp-linux-amd64
            myapp-darwin-amd64

  provenance:
    needs: build
    permissions:
      actions: read
      id-token: write
      contents: write
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0
    with:
      base64-subjects: ${{ needs.build.outputs.hashes }}
      upload-assets: true

Manual SLSA 1 Provenance Generation

For non-GitHub environments or custom pipelines:

# Using slsa-verifier's provenance format
<span class="hljs-built_in">cat > provenance.json << <span class="hljs-string">EOF
{
  "_type": "https://in-toto.io/Statement/v0.1",
  "subject": [
    {
      "name": "myapp-v1.2.3.tar.gz",
      "digest": {
        "sha256": "$(sha256sum myapp-v1.2.3.tar.gz | cut -d' ' -f1)"
      }
    }
  ],
  "predicateType": "https://slsa.dev/provenance/v0.2",
  "predicate": {
    "builder": {
      "id": "https://gitlab.com/${CI_PROJECT_PATH}/-/pipelines/${CI_PIPELINE_ID}"
    },
    "buildType": "https://gitlab.com/gitlab-org/gitlab/-/jobs",
    "invocation": {
      "configSource": {
        "uri": "git+https://gitlab.com/${CI_PROJECT_PATH}.git",
        "digest": {"sha1": "${CI_COMMIT_SHA}"},
        "entryPoint": ".gitlab-ci.yml"
      }
    },
    "materials": [
      {
        "uri": "git+https://gitlab.com/${CI_PROJECT_PATH}.git",
        "digest": {"sha1": "${CI_COMMIT_SHA}"}
      }
    ]
  }
}
EOF

Verifying SLSA Provenance

slsa-verifier validates provenance against SLSA requirements:

# Install
go install github.com/slsa-framework/slsa-verifier/v2/cli/slsa-verifier@latest

<span class="hljs-comment"># Verify a Go binary's provenance
slsa-verifier verify-artifact myapp-linux-amd64 \
  --provenance-path myapp-linux-amd64.intoto.jsonl \
  --source-uri github.com/myorg/myrepo \
  --source-tag v1.2.3

<span class="hljs-comment"># Verify a container image
slsa-verifier verify-image \
  ghcr.io/myorg/myapp:v1.2.3 \
  --source-uri github.com/myorg/myapp \
  --source-tag v1.2.3

Verification checks:

  1. The provenance was signed by GitHub Actions (via Sigstore)
  2. The source repository matches the claimed repository
  3. The build ran on the claimed workflow
  4. The artifact digest matches what's in the provenance

SLSA Requirements Summary Table

Requirement SLSA 1 SLSA 2 SLSA 3 SLSA 4
Scripted build
Build service
Provenance exists
Provenance authenticated
Non-falsifiable provenance
Source version controlled
Two-party review
Hermetic build
Reproducible build

Consuming SLSA Provenance

Provenance is only useful if consumers verify it. Patterns for verification:

In Kubernetes (with Kyverno)

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-slsa-provenance
spec:
  validationFailureAction: enforce
  rules:
    - name: verify-provenance
      match:
        any:
          - resources:
              kinds: [Pod]
      verifyImages:
        - imageReferences: ["ghcr.io/myorg/*"]
          attestations:
            - predicateType: "https://slsa.dev/provenance/v0.2"
              attestors:
                - entries:
                    - keyless:
                        subject: "https://github.com/myorg/myapp/.github/workflows/release.yaml@refs/tags/v*"
                        issuer: "https://token.actions.githubusercontent.com"
              conditions:
                - all:
                    - key: "{{ predicate.builder.id }}"
                      operator: Equals
                      value: "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@refs/tags/v1.10.0"

In a Verification Script

#!/bin/bash
<span class="hljs-comment"># verify-artifact.sh - verify SLSA provenance before deployment

ARTIFACT=<span class="hljs-variable">$1
PROVENANCE=<span class="hljs-variable">$2
SOURCE_URI=<span class="hljs-variable">$3
EXPECTED_TAG=<span class="hljs-variable">$4

slsa-verifier verify-artifact \
  <span class="hljs-string">"$ARTIFACT" \
  --provenance-path <span class="hljs-string">"$PROVENANCE" \
  --source-uri <span class="hljs-string">"$SOURCE_URI" \
  --source-tag <span class="hljs-string">"$EXPECTED_TAG"

<span class="hljs-keyword">if [ $? -eq 0 ]; <span class="hljs-keyword">then
  <span class="hljs-built_in">echo <span class="hljs-string">"✓ Provenance verified - artifact is authentic"
  <span class="hljs-built_in">exit 0
<span class="hljs-keyword">else
  <span class="hljs-built_in">echo <span class="hljs-string">"✗ Provenance verification FAILED"
  <span class="hljs-built_in">exit 1
<span class="hljs-keyword">fi

SLSA in the Dependency Graph

SLSA becomes most powerful when applied transitively. If your build requires dependencies, those dependencies should also have SLSA provenance.

The long-term vision: every dependency you pull has verifiable provenance, so you can answer "was this package built from the source it claims, by the CI service it claims?"

Tools like deps.dev from Google are building provenance tracking for popular open source packages.

What SLSA Doesn't Cover

SLSA focuses on the build process, not:

  • Code quality: SLSA doesn't audit for vulnerabilities or bugs
  • Runtime behavior: Provenance doesn't prove the code does what it should
  • Key compromise: If the signing key is stolen, provenance can be faked
  • Source trust: SLSA trusts that the source repository is controlled by the right people

These gaps are addressed by complementary controls: code review, vulnerability scanning (Grype/Trivy), and access control.

Practical Starting Point

Most organizations should target SLSA 2 as a baseline and SLSA 3 for production software:

  1. Start with GitHub Actions: slsa-github-generator makes SLSA 3 achievable in an afternoon
  2. Automate verification: Add slsa-verifier checks to your deployment pipeline
  3. Document your current level: Audit each repository and note its SLSA level
  4. Require SLSA 2+ for third-party dependencies you embed or vendor

SLSA is not about achieving level 4 immediately — it's about having a clear vocabulary for discussing and improving supply chain security incrementally. Level 1 today is better than level 0.

Read more