DevOps15 min readJun 2026 Last verified Jun 2026

Securing the Software Supply Chain (SLSA, SBOM, Signing)

Most of your production code isn't yours, it's dependencies, base images, and build tools you didn't write. The supply chain is now the attack surface. Here's how to shift security left: SBOMs, artifact signing and provenance with Sigstore, the SLSA levels, and least-privilege CI.

Supply ChainSLSASBOMDevSecOpsSecurity
SB

Sri Balaji

Founder ยท TheSimplifiedTech

On this page

TL;DR

Most of your production code is dependencies you didn't write, so the supply chain is the attack surface. Shift security left with SBOMs, signing and provenance via Sigstore, the SLSA levels, and least-privilege CI.

You didn't write most of your production code

Open your package.json or go.mod and count the direct dependencies. Now count the *transitive* ones, the dependencies of your dependencies. It's usually hundreds, often over a thousand. Add your base container image, your build tools, your CI runner, and the plugins in your pipeline. The uncomfortable truth: most of the code running in your production environment was written by strangers, pulled in automatically, and trusted by default.

That's the software supply chain, and it's now the favoured attack surface. SolarWinds, Codecov, the event-stream and xz backdoors, none of these broke in through your code. They came in through something you trusted. Securing the supply chain means treating every input to your build as untrusted until proven otherwise: knowing exactly what's in your artifacts (SBOMs), proving they're unmodified (signing and provenance), and hardening the build itself (SLSA). This article covers all three.

Who this is for

Engineers who own a CI/CD pipeline and want to make it defensible. No security background needed, we define SBOM, provenance, SLSA, and signing from scratch. Familiarity with a pipeline (build โ†’ test โ†’ publish) is assumed.

Shift left: security in the pipeline, not after it

Shifting left means moving security checks as early in the pipeline as possible, catching a vulnerable dependency at pull-request time, not in a pen-test six months after it shipped.

The later you find a problem, the more it costs. A vulnerable library caught by a scanner on the PR is a one-line bump. The same library found in production after an incident is an outage, a disclosure, and a postmortem. So the supply-chain discipline is to push every check as far upstream as it'll go, into the build, the PR, the merge.

๐Ÿ“‹ Know every ingredient in every dishSBOM, inventory of what's in your artifact
๐Ÿšš Verify the supplier sealed the deliverySigning, prove the artifact is unmodified
๐Ÿ“น Cameras on the whole prep processProvenance, how the artifact was built
โญ A health-inspection grade on the doorSLSA level, how trustworthy the build is
Securing a restaurant kitchen.

SBOM: an ingredients label for your software

A Software Bill of Materials is a complete, machine-readable list of every component in an artifact, every library, version, and license, transitive ones included. You can't secure what you can't see, and when the next log4shell drops, the difference between "are we affected?" taking five minutes versus five days is whether you have SBOMs. Generate one for every build and store it as an artifact.

sbom.sh
bash
# Generate an SBOM for a built container image (Syft)
syft registry.example.com/api:v2 -o spdx-json > sbom.spdx.json

# Later: instantly answer 'are we exposed to CVE-2025-XXXX?'
grep -i "log4j" sbom.spdx.json

# Or scan the SBOM itself against the vuln database (Grype)
grype sbom:sbom.spdx.json --fail-on high

Pro tip

SPDX and CycloneDX are the two standard SBOM formats, pick either; both are widely supported. The win isn't the format, it's the habit: an SBOM per build, stored and queryable, so 'what's affected?' is a grep and not an archaeology project.

Signing and provenance: prove it's really yours

An SBOM tells you what's *inside* an artifact. Signing tells you the artifact is the one you built and hasn't been tampered with since. Without a signature, an attacker who compromises your registry can swap your image for theirs and your cluster will happily run it. Sigstore (via the cosign tool) made signing practical: keyless signing tied to your CI's OIDC identity, with the signature published to a public transparency log.

Provenance goes one step further: a signed statement of *how* the artifact was built, which source commit, which builder, which steps. Together, signature + provenance let a deployer verify "this image came from our repo, built by our CI, from this exact commit" before it ever runs.

.github/workflows/release.yml
yaml
# A scan + sign + provenance stage in CI
jobs:
  publish:
    permissions:
      id-token: write      # OIDC identity for keyless signing
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4

      - name: Build image
        run: docker build -t $IMAGE .

      - name: Scan before publishing
        run: grype $IMAGE --fail-on high   # block on high/critical CVEs

      - name: Generate SBOM
        run: syft $IMAGE -o spdx-json > sbom.spdx.json

      - name: Push image
        run: docker push $IMAGE

      - name: Sign (keyless, via OIDC)
        run: cosign sign --yes $IMAGE

      - name: Attach SBOM as a signed attestation
        run: cosign attest --yes --predicate sbom.spdx.json \
               --type spdxjson $IMAGE
verify-before-deploy.sh
bash
# Deployers verify the signature and the source repo BEFORE running it
cosign verify $IMAGE \
  --certificate-identity-regexp 'https://github.com/acme/.*' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com'

# In a cluster, enforce this automatically with an admission policy
# (e.g. Sigstore policy-controller / Kyverno) so unsigned images are rejected.

Signing without verification is theatre

Signing your images does nothing if nothing checks the signature. The value only materialises when an admission controller (policy-controller, Kyverno, or a Connaisseur-style gate) rejects unsigned or wrongly-signed images at deploy time. Sign in CI, then enforce verification in the cluster, both halves, or neither.

SLSA: a maturity ladder for your build

SLSA (Supply-chain Levels for Software Artifacts, said "salsa") is a framework that grades how trustworthy your *build process* is. It's a ladder, each level is a concrete, achievable step, not an all-or-nothing certification. You don't need the top rung; you need to know which rung you're on and climb one.

LevelWhat it requiresWhat it stops
L1Build is scripted; provenance existsNothing built by hand / undocumented
L2Hosted build service; signed provenanceTampering with provenance after the fact
L3Hardened, isolated builds; non-falsifiable provenanceA compromised build job forging its own provenance
Climb one level at a time. Most teams should target L2 โ†’ L3 as a realistic, high-value goal.

The jump from L1 to L2 is mostly "use a real CI service and sign the provenance", achievable in an afternoon. L3 (isolated, ephemeral, hardened build runners) is where you stop a compromised build step from lying about what it produced. Pick a target, measure where you are, and treat the gap as a backlog.

Least-privilege CI and dependency hygiene

Your pipeline is itself a high-value target, it has registry credentials, signing identity, and often cluster access. Treat it like production. And the dependencies it pulls deserve the same suspicion as external input, because that's exactly what they are.

  • Pin dependencies by hash, not just version. A version tag can be re-pointed; a hash can't. Use lockfiles with integrity hashes and pin GitHub Actions to a commit SHA, never a moving tag like @v4.
  • Scope CI tokens to the minimum. Per-job, least-privilege, short-lived. A workflow that builds shouldn't hold deploy credentials. Prefer OIDC over long-lived secrets.
  • Scan dependencies and images on every PR, and fail the build on high/critical CVEs. Make the safe path the default path.
  • Use a private proxy/registry for dependencies so a deleted or hijacked upstream package can't break or poison your builds.
  • Review what you add. A new dependency is new code from a stranger running with your privileges. Weigh whether you need it at all.

The moving-tag trap

Pinning a GitHub Action to @v4 means you run whatever the maintainer (or whoever compromises their account) points v4 at, automatically, on your next build, with your secrets. Pin to a full commit SHA and update deliberately. This single change closes one of the most common CI attack paths.

Common mistakes that cost hours

  1. 1No SBOM, so every new CVE is an archaeology project. When the next log4shell drops, 'are we affected?' should be a grep, not a week of spelunking.
  2. 2Signing images but never verifying them. A signature nothing checks is decoration. Enforce verification at deploy time with an admission policy.
  3. 3Pinning Actions and base images to moving tags. @v4 and :latest mean you silently run whatever someone repoints them to. Pin by SHA / digest.
  4. 4Over-privileged CI. A single broad token turns a compromised build into a compromised registry, signing key, and cluster. Scope tokens per job; prefer OIDC.
  5. 5Scanning only in production / on a schedule. By then it's an incident. Scan on the PR and fail the build on high-severity findings.
  6. 6Treating SLSA as pass/fail. It's a ladder. Not knowing your level is the real failure, measure, then climb one rung.

Takeaways

Supply-chain security in six lines

  • Most of your production code is other people's, the supply chain is the attack surface.
  • Shift left: catch vulnerable dependencies on the PR, not in a post-incident pen-test.
  • SBOM = an ingredients label per build, so 'are we affected?' is a grep.
  • Sign artifacts (cosign/Sigstore) AND enforce verification at deploy, both halves or neither.
  • Provenance proves how an artifact was built; SLSA grades how trustworthy that build is.
  • Pin by hash, scope CI tokens tightly, scan every PR, your pipeline is production.

Where to go next

Supply-chain security lives inside your pipeline and is one expression of a broader 'trust nothing by default' mindset:

Check your understanding

1. What does an SBOM (Software Bill of Materials) give you?

2. According to the article, what does signing an artifact prove that an SBOM does not?

Frequently asked questions

What does 'shift left' mean for supply chain security?

Shifting left means moving security checks as early in the pipeline as possible, catching a vulnerable dependency at pull-request time rather than in a pen-test months later. The earlier you find a problem, the cheaper it is: a one-line version bump on a PR versus an outage and postmortem in production.

What is an SBOM and why do I need one?

A Software Bill of Materials is a complete, machine-readable list of every component in an artifact, including every library, version, license, and transitive dependency. When the next log4shell drops, having an SBOM per build is the difference between answering 'are we affected?' in five minutes versus five days.

What does signing an artifact prove that an SBOM does not?

An SBOM tells you what is inside an artifact, while signing proves the artifact is the one you built and has not been tampered with since. Without a signature, an attacker who compromises your registry could swap in a malicious build and you would have no way to detect it.

Which SBOM format should I use, SPDX or CycloneDX?

Either works, since both are standard formats and widely supported. The real win is not the format but the habit of generating an SBOM per build, storing it, and keeping it queryable so 'what's affected?' becomes a grep rather than an archaeology project.

Was this article helpful?

Want to go deeper?

This article covers concepts taught hands-on in the Cloud Engineer and DevOps career paths, with real terminal labs, production scenarios, and structured lessons.