Skip to content

Art deco train station with infinite branching tracks

The Software Supply Chain

Every piece of software you run is built on other software. Libraries, frameworks, tools, runtimes—layers upon layers of code written by people you've never met. This is your software supply chain, and understanding it is the first step to securing it.

Trust Decisions

Every dependency is a trust decision. Every transitive dependency is a trust decision you didn't know you were making. When someone asks "is this dependency secure?" the honest answer is almost always "I don't know, and neither do you." That's not paranoia. That's reality. The question isn't whether to trust—it's how to trust thoughtfully.


What "Supply Chain" Means

In manufacturing, a supply chain is the network of suppliers, processes, and logistics that turn raw materials into finished products. A car manufacturer doesn't mine iron ore—they buy steel from suppliers, who buy from mills, who buy from mines. Each link in the chain adds value and introduces risk.

Software works the same way:

  • You write application code
  • Your code imports libraries
  • Those libraries import other libraries
  • Those run on frameworks and runtimes
  • Those run on operating systems
  • Those run on hardware with firmware

Each layer is a dependency. Each dependency is code you didn't write, maintained by people you don't know, with security practices you can't verify.

The Dependency Graph

When you install a package, you're not just installing that package. You're installing everything it depends on, and everything those depend on.

your-application
├── web-framework (you chose this)
│   ├── http-parser (you didn't choose this)
│   ├── template-engine (you didn't choose this)
│   │   └── string-utils (you definitely didn't choose this)
│   └── logging-library (you didn't choose this)
│       └── date-formatter (you didn't choose this)
└── database-client (you chose this)
    ├── connection-pool (you didn't choose this)
    └── query-builder (you didn't choose this)

This is a dependency graph. The packages you explicitly install are direct dependencies. Everything else is transitive dependencies—dependencies of your dependencies.

The Numbers

Modern applications have a lot of transitive dependencies:

Ecosystem Typical Direct Typical Transitive
npm (Node.js) 20-50 500-2,000
pip (Python) 10-30 50-200
Maven (Java) 15-40 100-500
Cargo (Rust) 20-50 200-800

A typical React application has over 1,000 transitive dependencies.1 A typical Python data science project has 100-300.2 These aren't edge cases—they're normal.

Why This Matters

Every transitive dependency is:

  • Code you're running — It executes with the same privileges as your code
  • Attack surface — A vulnerability anywhere affects you
  • Maintenance burden — Someone has to keep it updated
  • Trust decision — You're trusting maintainers you've never evaluated

When log4shell dropped, organizations discovered they had dozens of copies of log4j buried in their dependency trees. They'd never installed it directly—it came in through other packages.

Trust Is Transitive

When you install a package, you're not just trusting its maintainers. You're trusting:

  • The package maintainers — They write and publish the code
  • Their dependencies' maintainers — Recursively, all the way down
  • The package registry — npm, PyPI, crates.io, etc.
  • The build system — If builds are compromised, signed packages can be malicious (SolarWinds)
  • The distribution infrastructure — CDNs, mirrors, proxies

A compromise anywhere in this chain can affect you. The xz utils backdoor was inserted by someone who became a trusted maintainer. The event-stream attack happened through a maintainer handoff. SolarWinds was a build system compromise.

When AI suggests dependencies, the trust chain extends further: you're also trusting the AI model's training data, the patterns it learned, and whatever code it was trained on. The AI might suggest a package based on patterns from tutorials, Stack Overflow answers, or codebases that are years out of date—or that were compromised before the training cutoff. See Vibe Coding for more on AI-introduced supply chain risk.

You can't verify all of this yourself. But you can make informed decisions about what you trust and why.

Attack Vectors

Supply chain attacks target different parts of the chain:

Compromised Packages

Malicious code inserted into legitimate packages:

  • Account takeover — Attacker gains access to maintainer's credentials (ua-parser-js, keyv / ChainDrop)
  • Social engineering — Attacker becomes a trusted maintainer (xz utils, event-stream) or manipulates an existing one (Axios)
  • Hostile maintainer — Maintainer intentionally inserts malicious code (colors.js)
  • Self-propagation — Compromised code harvests publish tokens and republishes itself into other maintainers' namespaces, faster than humans can respond (keyv / ChainDrop)

Counterfeit Packages

Packages designed to be mistaken for legitimate ones:

  • Typosquatting — Names similar to popular packages (typosquatting)
  • Dependency confusion — Internal names matching public packages (dependency confusion)
  • Namespace squatting — Claiming names before legitimate projects do

Build and Distribution Compromise

Attacks on the infrastructure itself:

  • Build system infiltration — Malicious code injected during build (SolarWinds)
  • CI action compromise — A trusted third-party build step rewritten under you (tj-actions)
  • Security vendor compromise — Your scanner, your CI, your password manager's CLI (Trivy / Cisco, Bitwarden CLI). These tools run with elevated access by design, which is exactly what makes them worth taking
  • Transit through the toolchain — The compromised tool isn't the target, it's the delivery network. A poisoned scanner harvests publish tokens from everyone who runs it, which then publish malicious packages to their users (LiteLLM / TeamPCP — Trivy to LiteLLM to ~2,500 organizations in five days)
  • Registry compromise — Malicious packages served from legitimate registries
  • Mirror poisoning — Tampered packages on mirrors or CDNs
  • Accidental disclosure — No attacker required; a build config that ships what it should have excluded (Claude Code) or a release path that rots unexercised (The Script That Ate Itself)

Vulnerability Exploitation

Not attacks per se, but exploitable weaknesses:

  • Unpatched vulnerabilities — Known CVEs in dependencies (log4shell)
  • Zero-days — Unknown vulnerabilities waiting to be discovered
  • Abandoned packages — No one to fix vulnerabilities when found

The Developer's Workstation

Every vector above assumes the malicious thing ends up in your application. This one doesn't. It never enters your dependency tree, never appears in a lock file, and never reaches production — it targets the machine that has your credentials loaded and your VPN up.

  • Editor and agent config execution — A file in the repository that your tooling runs when you open the folder: .vscode/tasks.json, .devcontainer/, .claude/settings.json, .amazonq/mcp.json. No install, no build (Amazon Q, keyv / ChainDrop)
  • Extension and plugin registries — Marketplaces nobody inventories the way they inventory npm or PyPI, installed once and auto-updated forever
  • Prose as payload — Instructions written for an AI agent rather than a CPU. No signature to match, no syntax to lint, and it reads as documentation (Amazon Q)

The reason this deserves its own heading: an SBOM will never show it to you. Your inventory covers what you ship. This is an attack on what you use to build what you ship, and the blast radius is a laptop holding production credentials — frequently a better prize than the application.

The Defender's Disadvantage

Attackers have structural advantages in supply chain attacks:

Asymmetric effort: Defenders must secure everything; attackers need to find one weakness.

Transitive trust: You can't audit every dependency. You're trusting thousands of packages based on... what, exactly?

Time advantage: Attackers can be patient. The xz utils attacker spent years building trust.

Scale leverage: One compromised package can affect millions of users simultaneously.

Detection difficulty: Malicious code can be obfuscated, dormant, or targeted at specific victims. Notepad++ is the clearest case: hijacked update infrastructure that served the legitimate binary to almost everyone and the malicious one to a chosen few, for six months. Selective targeting means the population most likely to notice never sees anything wrong.

Speed asymmetry: Publishing is automated; response is human. ChainDrop republished at roughly one package per second across eight organizations at once. Detection took hours, which was fast — and still slower than the attack by two orders of magnitude.

This doesn't mean defense is futile. It means defense requires strategy, not just tools. For what a full quarter of these looks like stacked together, see Q1 2026: When It All Converged.

Defense in Depth

No single measure secures your supply chain. Effective defense combines multiple layers:

Know What You're Running

  • Generate SBOMs for your software
  • Track direct and transitive dependencies
  • Monitor for changes

Reduce Attack Surface

  • Minimize dependency count
  • Prefer well-maintained packages
  • Remove unused dependencies

Detect Threats

Limit Blast Radius

  • Pin versions
  • Use lock files
  • Segment environments
  • Principle of least privilege

Prepare for Incidents

  • Have response plans
  • Know how to update quickly
  • Practice recovery

The remaining chapters in this section cover these defenses in detail.

Three Sentences

I remember when dependency management meant copying .h files into your project and hoping the compiler found them. We had supply chain problems then too—we just didn't call them that.

What's changed is scale. When I started, a complex project might have a dozen dependencies. Now a "hello world" web app has hundreds. The attack surface grew faster than our ability to secure it.

The tools have improved. We have SBOMs, vulnerability scanners, lock files, reproducible builds. But the fundamental problem remains: you're trusting code you haven't read, written by people you don't know, for reasons you don't understand. That's not going to change. Modern software is too complex to write everything yourself.

Know what you're running. Understand who maintains it. Have a plan for when things go wrong. That's supply chain security in three sentences. The rest is details.


Key Concepts

Term Definition
Direct dependency A package you explicitly install
Transitive dependency A dependency of your dependency
Dependency graph The tree of all dependencies
Supply chain attack Compromise via trusted software components
SBOM Software Bill of Materials—inventory of components


  1. Based on analysis of average npm package dependency trees, 2023-2024. 

  2. Based on typical requirements.txt analysis for data science projects using pandas, numpy, scikit-learn, etc.