Sachin Chaurasiya

DevSecOps Part 1 of 5 · Software Supply Chain Security

Understanding the Software Supply Chain

The delivery chain from source through dependencies, build, artifact, image, registry and deployment; where each link can be compromised; and the control for each, from lockfiles and SBOMs to signing by digest.

Author
Sachin Chaurasiya
Sachin Chaurasiya
Published
Reading time
9 min read
Difficulty
intermediate

Reviewed Tested with Syft 1.51.1, Grype 0.118.0, Cosign 3.1.3, Docker 29.1

On this page

Overview

A vulnerability scanner answers one question: does this artifact contain a package with a known CVE? It does not answer the questions that supply-chain attacks actually raise. Is this the artifact the build produced? Did the build use the dependencies the lockfile named? Is the image the cluster pulled the one that passed the scan yesterday, or has the tag moved? Each of those is a different link in the chain, and each has its own control.

This part maps the chain and the controls. The four parts that follow put the controls on one real image: an SBOM with Syft, a scan and fix cycle with Grype, a signature with Cosign, and an SBOM attestation bound to the image digest. The tool versions in the header are the ones those parts were run with; this part runs nothing itself.

Diagram · The software supply chain and its controls
The software supply chain and its controlsSource and its dependencies are resolved by a build into an artifact, which becomes a container image pushed to a registry and finally deployed. Each step has a control: a lockfile and secret scanning for source, pinned and checked dependencies, a pinned build environment, an SBOM and vulnerability scan for the image, a signature and attestation bound to the image digest in the registry, and verification of that digest before deployment.resolveinstallpackagepushpullrejectedSourcelockfile · gitleaks1Dependenciespinned · checksums2Buildpinned image · CI3Container imagesyft · grype4Deploymentverify digest6Registrycosign sign · attest5Tamperedartifactmoved tag

Source and its dependencies are resolved by a build into an artifact, which becomes a container image pushed to a registry and finally deployed. Each step has a control: a lockfile and secret scanning for source, pinned and checked dependencies, a pinned build environment, an SBOM and vulnerability scan for the image, a signature and attestation bound to the image digest in the registry, and verification of that digest before deployment.

  1. Source. The repository and its history. The controls are review (a protected branch, one approver), secret detection before and after the push (Gitleaks), and a lockfile committed with the code so the next link is pinned.
  2. Dependencies. Everything the lockfile resolves: the direct dependencies you chose and the transitive ones you did not. The control is an install that refuses to deviate from the lockfile (npm ci, --frozen-lockfile) and verifies checksums.
  3. Build. The environment that turns source and dependencies into an artifact. The control is a pinned, reproducible build image and a pipeline whose definition is itself reviewed (CI/CD Engineering, part 2).
  4. Container image. The artifact plus a base image plus whatever the base image carried. The controls are an inventory (the SBOM, part 2) and a scan of that inventory (part 3).
  5. Registry. Where the image waits between build and deploy, addressable by tag or by digest. The controls are a signature over the digest and attestations attached to it (parts 4 and 5).
  6. Deployment. The point where the image becomes a running process. The control is verification: pull by digest, check the signature and the attestations, refuse anything else.

The chain is a sequence of trust decisions, most of them implicit. Making them explicit is the point of the exercise.

Dependency trust

A package.json with one dependency produced, in the sample application used across this path, a node_modules with exactly one package. The base image it runs on contributed 233 more (part 2 shows the count and where they came from). Every one of those is code you run with your application’s privileges, written by someone you have not met, published to a registry that authenticates the publisher’s account, not the publisher.

The practical controls, in the order they matter:

  • Pin and lock. A lockfile records exact versions and integrity hashes; the install verifies them. This does not make a dependency safe, it makes it the same dependency every time, which is the precondition for everything else.
  • Know what you have. An SBOM turns “we use Node” into a list of 234 named, versioned packages. When a CVE is published you grep the list instead of rebuilding to find out.
  • Reduce what you have. The fastest way to have fewer vulnerable packages is to have fewer packages. Part 3 removes more than two hundred from the sample image by deleting a package manager the runtime never uses.
  • Watch for change. A dependency that changes maintainer, adds an install script or gains a network call is worth a look before it is upgraded. Tooling for this (Socket, OpenSSF Scorecard) is beyond this path; the habit is not.

Build-system trust

The build is where source and dependencies meet, and where a compromise is hardest to see afterwards: a build that injects one line into the output leaves no trace in the repository. Two controls make a build trustworthy.

The build environment is pinned and disposable: a specific image, by digest, with nothing carried over between runs. The CI/CD Engineering path covers this in detail. And the build’s inputs and outputs are recorded, ideally by the build system itself rather than by a script the attacker also controls. That record is provenance, and part 5 returns to it.

Artifact integrity

An artifact is trustworthy when you can prove it is the one the build produced. A checksum next to the archive (app-4e205ab8.sha256 in the CI/CD path) proves the bytes were not corrupted in transit. It does not prove who produced them: anyone who can replace the archive can replace the checksum. Proving origin needs a signature with a key the replacer does not have, which is what part 4 adds.

Registries and the tag problem

A container registry stores manifests, layers and the tags that point at manifests. Two facts about it decide most of this path:

  • A digest (sha256:5743684a…) is the hash of the manifest. It identifies exactly one image forever. If a single byte of a layer changes, the digest changes.
  • A tag (ci-demo:1.4.2) is a mutable pointer. Anyone with push access can point it at a different manifest, and the registry will serve the new one under the old name.

Part 4 does exactly that: pushes a known-vulnerable image over the tag of a signed one, and shows that verification by tag fails while verification by digest still passes. The lesson generalises: every control in this path binds to the digest, and a deployment that pulls by tag has opted out of all of them.

Deployment

The deploy step is the last chance to say no, and the only place where the answer prevents a compromised artifact from running rather than reporting it afterwards. Verification here means resolving the tag to a digest, checking a signature over that digest against a trusted key or identity, optionally checking that the required attestations (an SBOM, a scan result, provenance) exist and say the right thing, and pulling by that digest.

On Kubernetes this is an admission policy (Kyverno’s verifyImages, Sigstore’s policy-controller) or a deploy job that runs cosign verify before kubectl set image. The Secure Kubernetes path covers admission control; part 4 shows what the verify step checks.

Why scanning alone is not enough

A vulnerability scan is necessary and it is where most teams start, so it is worth being precise about its limits.

  • It finds known vulnerabilities in identified packages. A malicious package with no CVE, a package the scanner could not identify, or a backdoor in your own code all pass.
  • It scans what you gave it. If the image you scan is not the image you deploy (tag moved, rebuilt, pulled from a different mirror), the result describes something else.
  • It is point-in-time. The SBOM lab on this site scans a database from one day and notes the count will drift; a clean scan on Monday says nothing about Friday.

Signing addresses the second problem, attestations the third (the scan result travels with the image and says when it was made), and an SBOM makes re-checking cheap. None of them replaces the scan; they make its result mean something.

SBOMs, signatures and attestations in one paragraph

An SBOM is an inventory: the packages in an artifact, with versions and, ideally, where each came from. A signature is a statement by a key holder that a specific digest is theirs. An attestation is a signed statement about a digest: “this SBOM describes it”, “this scan was run on it”, “this build produced it”. The three compose. The SBOM says what is inside, the signature says who vouches for it, the attestation binds the SBOM to the exact image so the inventory cannot be swapped either.

Where SLSA fits

SLSA (Supply-chain Levels for Software Artifacts) is a framework that grades how much a build’s provenance can be trusted, from “there is provenance” (level 1) to “the build platform generated it and it could not have been forged by the project’s own maintainers” (level 3). It is useful as a vocabulary for the build-system link above and as a target when choosing a CI platform’s provenance feature. It is not a tool, and this path does not chase a level; it puts the controls in place that a level would measure.

Security Considerations

  • The controls are only as trustworthy as the keys and identities behind them. A signing key on a developer laptop, or in a CI variable any job can read, signs whatever an attacker wants signed. Part 4 discusses key handling and keyless signing.
  • Secret detection and dependency locking are already on this site in the DevSecOps Pipeline path; this path assumes them and starts from the artifact.

Conclusion

The chain has six links and each one has a control that is cheap to add and expensive to skip. The next four parts add them to one image, in order: inventory it (part 2), scan and fix it (part 3), sign it by digest (part 4), and attach the inventory as an attestation (part 5).

References

Keep reading