The TLDR

When you run npm install or pip install, you’re executing code written by someone you’ve never met, hosted on infrastructure you don’t control, signed by keys you’ve never verified. Modern software is built on dependency trees hundreds of levels deep. A supply chain attack compromises one package and inherits access to every application that depends on it. SolarWinds compromised 18,000 organizations through a single build system. The event-stream npm attack reached millions of installations through one maintainer handoff. Your node_modules folder is the largest trust surface in your application.

The Reality

The average JavaScript project has over 1,000 transitive dependencies. The average Python project has hundreds. Each dependency is a piece of code that runs with the same permissions as your application — reading files, making network requests, accessing environment variables, executing system commands.

You audit your own code. You review your team’s pull requests. But the code in node_modules/ or site-packages/? That’s trusted implicitly. And that’s where supply chain attacks live.

The Scale

How It Works

Dependency Confusion

Discovered by Alex Birsan in 2021, dependency confusion exploits how package managers resolve names between private (internal) and public registries.

Many companies have internal packages with names like company-utils hosted on private registries. If an attacker publishes a package with the same name on the public registry (npm, PyPI) with a higher version number, some package managers will prefer the public version.

The attacker’s payload runs during installation — preinstall scripts in npm, setup.py execution in pip. Birsan demonstrated this against Apple, Microsoft, and PayPal, gaining code execution on internal build systems.

Typosquatting

Register a package with a name close to a popular one:

Developers who mistype a package name or copy a wrong string install the malicious package instead. The malicious package often includes the legitimate package’s functionality (to avoid immediate detection) plus a payload.

PyPI and npm both regularly discover and remove typosquatting packages — but the window between publication and removal can be hours to days, during which thousands of installations occur.

Maintainer Compromise

The event-stream attack (2018): A popular npm package (event-stream, ~2 million weekly downloads) had a single maintainer who was burnt out. A new contributor offered to help. The original maintainer transferred ownership. The new maintainer added a dependency (flatmap-stream) that contained an encrypted payload targeting the Copay Bitcoin wallet, attempting to steal cryptocurrency private keys.

The attack was in production for two months before detection. It demonstrates the fundamental problem: open-source packages are maintained by volunteers with no security obligations, and ownership transfer has no verification process.

Build System Compromise

SolarWinds (2020): Attackers compromised SolarWinds’ build system and injected malicious code into the Orion software update. The update was digitally signed by SolarWinds (because it was built in their compromised build pipeline), so it passed all integrity checks. 18,000 organizations installed the compromised update, including the US Treasury, Department of Homeland Security, and Fortune 500 companies.

This is the nuclear option of supply chain attacks — compromise the build pipeline itself, and every artifact it produces is weaponized.

Codecov (2021): Attackers modified Codecov’s Bash Uploader script (used in CI/CD pipelines) to export environment variables — including CI secrets, API keys, and tokens — to an attacker-controlled server. The compromised script was active for two months.

Malicious Packages

Direct publication of malicious packages on public registries:

MITRE ATT&CK T1195.002 (Supply Chain Compromise: Compromise Software Supply Chain) documents this technique class.

How It Gets Exploited

The CI/CD Vector

Build pipelines (GitHub Actions, Jenkins, GitLab CI) install dependencies from public registries. A compromised dependency gains access to:

The pipeline trusts its dependencies because it has to — the build won’t work without them.

The Transitive Trust Problem

You audit your direct dependencies. But each of those depends on other packages, which depend on other packages. A vulnerability or compromise five levels deep in the dependency tree affects your application, and you probably don’t even know the package exists.

The Log4Shell vulnerability (CVE-2021-44228) demonstrated this: Log4j was a transitive dependency in thousands of applications whose developers had never directly referenced it. Finding and patching every affected system took months across the industry.

What You Can Do

Dependency Management

  1. Lock your dependencies — use lock files (package-lock.json, Pipfile.lock, Cargo.lock) to pin exact versions. Don’t allow ranges in production.
  2. Audit your dependency treenpm audit, pip-audit, cargo audit. Run these in CI.
  3. Minimize dependencies — before adding a package, ask: can I write this in 20 lines instead of adding a dependency with its own dependency tree?
  4. Review new dependencies — check the package’s maintainers, download count, last update, and GitHub activity before adding it.
  5. Use private registries with proxy/caching — Artifactory, Verdaccio, or similar. This prevents dependency confusion and gives you a chokepoint for scanning.

Build Pipeline Security

  1. Pin actions and tools in CI/CD to specific commit SHAs, not tags or branches
  2. Isolate build environments — don’t share secrets across jobs that don’t need them
  3. Use SLSA (Supply-chain Levels for Software Artifacts) framework to improve build provenance
  4. Verify signatures on dependencies where available (Sigstore, npm provenance)
  5. Rotate secrets regularly and audit access

Monitoring

  1. Dependabot / Renovate — automated dependency update PRs with security alerts
  2. Socket.dev — detects supply chain attacks in npm packages by analyzing behavior
  3. Snyk / Grype — vulnerability scanning for container images and dependencies
  4. SBOM (Software Bill of Materials) — maintain and publish an SBOM so downstream consumers know what’s in your software

The Cultural Fix

The open-source maintainer burnout problem is structural. The event-stream attack happened because a single exhausted volunteer transferred ownership to a stranger. If your business depends on open-source packages:

Sources & Further Reading