Skip to content
Learni
View all tutorials
Sécurité DevOps

How to Secure the Software Supply Chain in 2026

Lire en français

Introduction

Software supply chain security has become critical in 2026, following major incidents like Log4Shell and SolarWinds attacks. It protects the entire software lifecycle, from open-source dependencies to deployed artifacts. A breach in the supply chain can compromise millions of users, with costs estimated in billions.

This advanced tutorial guides you step-by-step to implement defense-in-depth: static and dynamic vulnerability scans (SCA/SBOM), cryptographic container signing (Cosign/Sigstore), SLSA frameworks for pipeline integrity, and continuous monitoring with Dependency-Track. We use mature open-source tools like Trivy, Cosign, and GitHub Actions.

By the end, you'll have a CI/CD pipeline attesting SLSA Level 2, protecting against malicious injections and upstream compromises. Ideal for DevSecOps teams seeking a production-ready implementation (128 words).

Prerequisites

  • Docker and Docker Compose installed (version 27+).
  • GitHub account with a private repository.
  • Cosign tool installed via brew install cosign (macOS) or equivalent.
  • Node.js 20+ and npm/yarn for a sample project.
  • Access to a container registry (Docker Hub or GHCR).
  • Advanced knowledge of CI/CD and cryptography.

Install Trivy and Scan Dependencies

scan-deps.sh
#!/bin/bash

# Install Trivy (open-source vulnerability scanner)
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.55.0

# Create a sample Node.js project
mkdir supply-chain-demo && cd supply-chain-demo
npm init -y
npm install lodash express

# Generate SBOM and scan
trivy fs . --format json --output trivy-deps.json --scanners vuln,license,malware
trivy sbom trivy-deps.json --format table

# Example output if critical vulnerabilities found
# Display summary
trivy fs . --format sarif --output results.sarif

This script installs Trivy, creates a vulnerable Node.js project, generates an SBOM, and scans dependencies for vulnerabilities, licenses, and malware. It produces a GitHub-compatible SARIF report. Pitfall: Always scan in vuln,license mode for a complete view; don't ignore false positives without manual triage.

Interpreting Trivy Scans

Trivy detects CVEs in dependencies like lodash (e.g., CVE-2021-23337). The SBOM (Software Bill of Materials) lists all components, essential for traceability. Integrate SARIF reports into GitHub for automatic alerts.

Analogy: Think of your supply chain like a food supply chain; Trivy is the health inspection spotting contaminants before distribution.

Set Up GitHub Actions for Automatic SCA

.github/workflows/sca-trivy.yml
name: SCA with Trivy

on: [push, pull_request]

permissions:
  contents: read
  id-token: write
  security-events: write

jobs:
  sca:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
      - name: Upload Trivy scan
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-results.sarif'

This GitHub Actions workflow automatically scans on every push/PR with Trivy and uploads SARIF results for security alerts. Minimal permissions (id-token: write) for SLSA compliance. Pitfall: Limit severity to avoid blocking false positives; test on branches before main.

Secure Dockerfile with Minimal Base Image

Dockerfile
FROM node:20-alpine AS base

# Install deps without cache for reproducibility
WORKDIR /app
COPY package*.json .
RUN --mount=type=cache,target=/root/.npm npm ci --only=production --no-optional

# Copy source
COPY . .

# Multi-stage for minimal runtime
FROM base AS runner
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]

# SLSA labels
LABEL org.opencontainers.image.source=https://github.com/user/supply-chain-demo
LABEL org.opencontainers.image.licenses=MIT

Multi-stage Dockerfile using Node Alpine, non-root user, npm cache, and OCI labels for traceability. Avoids unnecessary layers to reduce attack surface. Pitfall: Use --mount=type=cache in CI to speed up builds without compromising reproducibility.

Build and Push the Image

Build with docker build -t ghcr.io/user/supply-chain-demo:v1 . and push to GHCR. The labels enable downstream verification.

Sign the Image with Cosign

sign-image.sh
#!/bin/bash

# Authenticate to GitHub Container Registry
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin

# Build and tag
IMAGE=ghcr.io/USERNAME/supply-chain-demo:v1
docker build -t $IMAGE .
docker push $IMAGE

# Generate Cosign key pair (or use GitHub OIDC)
cosign generate-key-pair

# Sign with private key
cosign sign -key cosign.key $IMAGE

# SLSA provenance attestation
cosign attest --predicate-type https://slsa.dev/provenance/v1 $IMAGE

# Verify
cosign verify $IMAGE

Complete script to build, push, sign, and attest an image with Cosign using GitHub OIDC (implicit token). Creates Fulcio/Rekor signatures for keyless verification. Pitfall: Use OIDC (permissions: id-token) in CI to avoid static secrets.

GitHub Actions Workflow for SLSA Level 2 Build/Sign

.github/workflows/build-sign.yml
name: Build and Sign SLSA L2

on: [push]

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

jobs:
  build:
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/project-everest/kstool:slsa2-debian11
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: slsa-framework/slsa-github-generator/.github/actions/slsa-build-provenance@main
        with:
          provenance-name: 'supply-chain-demo'
          base64-provenance: false
      - name: Sign
        run: |
          cosign sign --yes ghcr.io/USERNAME/supply-chain-demo@${{ steps.slsa.outputs.artifact-digest }}

SLSA Level 2 workflow using kstool and slsa-github-generator for reproducible container builds and provenance attestations. Cosign signs immutable digest. Pitfall: fetch-depth: 0 for full Git history; validate SLSA compliance with slsa-verifier.

Deploy Dependency-Track for SBOM Monitoring

docker-compose.yml
version: '3.8'
services:
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: dtrack
      POSTGRES_USER: dtrack
      POSTGRES_PASSWORD: changeit
    volumes:
      - postgres:/var/lib/postgresql/data

  dependency-track:
    image: owasp/dependency-track:4.14.0
    ports:
      - "8081:8080"
    environment:
      ALPINE_CDN_MIRROR: http://dl-cdn.alpinelinux.org/alpine
    depends_on:
      - postgres
    volumes:
      - dtrack:/data

volumes:
  postgres:
  dtrack:

# Upload SBOM: curl -X POST http://localhost:8081/api/v1/bom -F autoCreate=true -F projectUuid=UUID -F bom=@trivy-deps.json

Docker Compose setup for Dependency-Track, an SBOM monitoring tool that tracks vulnerabilities continuously. Upload SBOMs via API. Pitfall: Change default credentials; integrate into CI for automated uploads with a fixed projectUuid.

Best Practices

  • SLSA at minimum Level 2: Ensure non-falsifiable build provenance.
  • Systematic SBOMs: Generate in CycloneDX/SPDX for all artifacts.
  • Hierarchical signatures: Cosign + Rekor for chain of trust.
  • Zero-trust pipelines: OIDC permissions, no long-lived secrets.
  • Proactive alerting: Integrate SARIF with GitHub Advanced Security.

Common Mistakes to Avoid

  • Scanning only in dev: Always in CI/CD and pre-prod to catch upstream issues.
  • Ignoring signatures: Always verify with cosign verify --certificate-identity.
  • Unpinned dependencies: Use lockfiles (package-lock.json) for reproducibility.
  • Root builds: Non-root users and multi-stage to reduce blast radius.

Next Steps

  • SLSA documentation: slsa.dev.
  • Sigstore: sigstore.dev.
  • Advanced DevSecOps training: Learni Trainings.
  • Tools: in-toto, Chainguard Images.
  • Book: 'Secure Software Supply Chain' by Dan Lorenc.