Skip to main content

The software supply chain

Summary: most of the code that runs in your application does not come from you. It comes from libraries, themselves built on other libraries, in a chain whose extent you generally do not know. This lesson explains the risks of that chain — indirect dependencies, typosquatting, maintainer compromise — and the answers: SBOM, version pinning, signing and the SLSA framework.


1. The scale of the problem

This is the observation on which this entire lesson rests. The question is not "is my code secure?" but "is everything that runs in my environment secure?". And that whole, you neither wrote, nor read, nor entirely chose.

A telling number: in the JavaScript ecosystem, installing a common development tool can bring in more than a thousand packages. Each one is a point of trust, and each one can publish a new version tomorrow morning.


2. The five ways to be attacked through the chain

An important hierarchy to keep in mind. Vector 1 accounts for the immense majority of real incidents and is entirely avoidable: you just have to track your dependencies and apply patches. Vectors 4 and 5 are spectacular and make headlines, but they are rare.

The practical advice that follows: put your energy into vector 1 first. A team that applies its patches eliminates more real risk than a team that sets up sophisticated signature verification without updating its libraries.


3. Typosquatting and dependency confusion

Dependency confusion is particularly formidable because it exploits a default configuration rather than a human error. It was publicly demonstrated in 2021 against several very large technology companies, with a success that surprised everyone.


4. Version pinning

This is the most cost-effective measure in this whole lesson, and it is free. Depending on your ecosystem, the file is called package-lock.json, yarn.lock, poetry.lock, Gemfile.lock, go.sum or Cargo.lock. Always version it.

The essential complement in continuous integration: use the command that strictly follows the lockfile and fails if it is inconsistent — npm ci rather than npm install, for example. Otherwise your pinning can be silently bypassed.


5. The SBOM — the inventory of your components

The best argument in favor of the SBOM is the lived experience of December 2021. When Log4Shell appeared, the question asked in every company in the world was: "do we use Log4j, and where?". Organizations with an inventory answered in minutes. The others mobilized entire teams for days — with no certainty of having found everything.

Regulatory point worth knowing: the SBOM is progressively becoming a contractual requirement, notably for suppliers to the American public sector since a 2021 presidential executive order, and increasingly in European calls for tenders.

How to produce one: tools like Syft, Trivy or Docker's built-in functions generate an SBOM in one command. It is not a project, it is a pipeline step.


6. Artifact signing

The innovation brought by Sigstore deserves to be highlighted. Historically, signing meant managing private keys — storing them, protecting them, rotating them — which discouraged most teams. Sigstore allows signing with an ephemeral identity provided by the CI system, without any key to keep. That is what made signing genuinely accessible.


7. What to do in practice

Step 6 is the one no one talks about, and yet it is the most effective in the long run. Before adding a dependency, ask yourself three questions: "do I really need it?", "can I write these twenty lines myself?", "is this project actively maintained?". A dependency not added is a future vulnerability that will never concern you.

How to evaluate a dependency before adopting it: look at the date of the last commit, the number of active maintainers (a single maintainer is a risk), the number of dependencies it brings in itself, and the existence of a declared security policy.


Remember in 30 seconds

  • You write a tiny fraction of the code that runs. 25 direct dependencies often bring in several hundred indirect ones.
  • Five attack vectors: unpatched vulnerability, typosquatting, maintainer compromise, patient malicious contributor, build chain compromise.
  • Vector 1 accounts for the immense majority of real incidents and is entirely avoidable. Start there.
  • Dependency confusion exploits a default configuration, not a human error. Reserve a prefix for your internal packages.
  • Always version your lockfile, and use the strict command in CI (npm ci, not npm install).
  • An SBOM is the complete inventory of your components. Formats: CycloneDX and SPDX.
  • The value of the SBOM shows during an incident: answers in seconds rather than days. Remember Log4Shell.
  • Artifact signing closes the substitution scenario. Sigstore and Cosign made it accessible, with no key to manage.
  • SLSA is the reference framework, with progressive levels of build guarantees.
  • The most lasting gain: reducing the number of dependencies. A library not added is a risk permanently eliminated.

Next: Secrets and identities: where to store your keys →