AI Agents · Security

Supply Chain Security — A Field Guide

A practical look at software supply chain security: how dependency compromise happens, what SLSA provenance actually proves, signing artifacts with Sigstore/cosign, and verifying lockfile integrity in CI.

John Kihiu12 min read

Most supply chain attacks don't target your code — they target the dozens of transitive dependencies your code pulls in without anyone reviewing them. A compromised maintainer account, a typosquatted package name, a build step that fetches an unpinned script from the internet: any of these can smuggle malicious code into your production build without touching a single line you wrote. The response to this isn't "audit every dependency," which doesn't scale — it's building a chain of verifiable evidence from source to running artifact, so a compromise anywhere in that chain is detectable rather than invisible.

How dependency compromise actually happens

The realistic attack patterns are not exotic. A maintainer's npm token gets phished and a backdoored patch version gets published. A popular package gets abandoned and someone squats the name with a near-identical one (typosquatting: corss-env instead of cross-env). A CI pipeline pulls a build script over plain HTTP with no integrity check, and a man-in-the-middle swaps it. In every case, the code that runs in production was never reviewed by anyone on your team — it was trusted by default because it came through a package manager.

Lockfile integrity in CI

The cheapest defense against a compromised or accidentally-upgraded dependency is enforcing that CI installs exactly what the lockfile says, and fails loudly if `package.json` and the lockfile have drifted apart.

BASH · CI LOCKFILE INTEGRITY CHECK
# npm: fails if package.json and package-lock.json are out of sync,
# and refuses to silently resolve a newer version of any dependency.
npm ci --ignore-scripts

# yarn (classic): same intent — install exactly what's locked.
yarn install --frozen-lockfile --ignore-scripts

# --ignore-scripts blocks postinstall scripts from running during CI
# install. Most supply-chain payloads execute via postinstall hooks,
# so this alone closes a large fraction of real-world attacks.
npm audit is not a supply chain control

`npm audit` checks known CVEs against a database — it says nothing about whether the package you're about to install is the one the maintainer actually published, or whether it was tampered with in transit. Integrity comes from the lockfile's recorded hashes, not from vulnerability scanning.

SLSA: what provenance actually proves

SLSA (Supply-chain Levels for Software Artifacts) is a framework for grading how verifiable the path from source code to built artifact is. The core idea is provenance: a signed, tamper-evident statement saying "this exact artifact was built from this exact source commit, by this build system, using these exact steps" — generated automatically by the build platform, not by a human attesting after the fact. At SLSA's higher levels, the build has to run in an isolated, non-interactive environment so that no one — including someone with source repo access — can slip an unreviewed change into the artifact without it showing up in the provenance.

YAML · GITHUB ACTIONS: BUILD PROVENANCE ATTESTATION
name: build-and-attest
on: push

permissions:
  id-token: write     # required to mint the signing identity
  contents: read
  attestations: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci --ignore-scripts && npm run build

      # Generates and attaches SLSA provenance for the build artifact,
      # signed via GitHub's OIDC-backed Sigstore integration.
      - uses: actions/attest-build-provenance@v1
        with:
          subject-path: 'dist/**'

Signing artifacts with Sigstore and cosign

Sigstore solves the "who signed this and can I trust the key" problem without requiring you to manage long-lived signing keys yourself. Instead of a private key you store and rotate, Sigstore issues a short-lived certificate tied to an OIDC identity (your CI provider's token, or a GitHub Actions workflow identity) at signing time, and records the signature in a public transparency log. `cosign` is the CLI most teams use to sign and verify container images and other artifacts against Sigstore.

BASH · COSIGN SIGN AND VERIFY
# Sign a container image keylessly, using the CI job's OIDC identity
cosign sign ghcr.io/acme/api:1.4.2

# Verify the image was signed by a specific identity (e.g. your CI
# workflow) before pulling it in a deploy pipeline
cosign verify ghcr.io/acme/api:1.4.2 \
  --certificate-identity "https://github.com/acme/api/.github/workflows/release.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com"
Verification belongs in the deploy gate, not just CI

Signing an image is only useful if something refuses to deploy an unsigned or wrongly-signed one. Add a cosign verify step (or a Kubernetes admission controller like Kyverno/Sigstore Policy Controller) at the point of deployment, not just as a CI checkbox nobody enforces downstream.

Wrapping up

None of these controls — lockfile pinning, SLSA provenance, cosign signatures — prevent a determined attacker on their own. What they do is convert an invisible compromise into a detectable one: a tampered dependency fails an integrity check, an unsigned artifact fails a deploy gate, a build missing provenance can't claim to be the one your CI actually produced. Supply chain security is less about any single tool and more about closing the gaps where trust was previously implicit — the postinstall script nobody read, the image nobody verified, the artifact that could have come from anywhere.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.