Software Supply Chain Attacks: How Open Source Dependencies Become Weapons


When people think about hacking, they picture someone breaking through a firewall or brute-forcing a password. Modern software supply chain attacks are more insidious: instead of breaking in, the attacker becomes part of the supply chain. They contribute to an open source project, gain trust, and eventually inject malicious code that gets distributed to millions of downstream users.

The XZ Utils incident in 2024 remains the most instructive example. An attacker spent over two years building credibility in the XZ compression library project—submitting legitimate patches, helping with maintenance, and gradually gaining commit access. Once trusted, they inserted a sophisticated backdoor that would have compromised SSH authentication on millions of Linux systems. The backdoor was discovered by accident when a developer noticed unusual SSH latency.

This wasn’t a smash-and-grab. It was a long-term operation that required patience, social engineering, and technical sophistication. The attacker even used sockpuppet accounts to pressure the existing maintainer into sharing responsibilities, creating the opening needed to gain access.

The XZ incident was caught. How many similar operations weren’t? That’s the question that keeps supply chain security researchers up at night.

The attack surface is enormous. Modern software applications depend on hundreds or thousands of open source packages. A typical Node.js application might pull in 500+ dependencies when you count transitive dependencies (dependencies of dependencies). Each of those packages is maintained by someone—often a single volunteer working in their spare time. Each one is a potential insertion point for malicious code.

The npm registry hosts over 2 million packages. PyPI has over 500,000. Most are legitimate, but the sheer volume makes comprehensive security review impossible. Automated scanning catches known malicious patterns but misses novel techniques, obfuscated code, and supply chain attacks that look like legitimate contributions.

Common supply chain attack techniques include:

Typosquatting: Publishing packages with names similar to popular ones. “pythonn-requests” instead of “python-requests.” Developers who mistype a package name in their dependency list unknowingly install the malicious version. Studies have found hundreds of typosquatting packages on major registries.

Dependency confusion: Exploiting how package managers resolve names. If a company uses internal packages with common names, an attacker can publish a higher-version public package with the same name. Some package managers will prefer the public version, pulling in the attacker’s code. This technique was publicly disclosed by Alex Birsan and successfully tested against Apple, Microsoft, and other major companies.

Maintainer account compromise: Attacking the accounts of legitimate package maintainers. If a maintainer’s npm or PyPI account is compromised, the attacker can publish malicious versions of trusted packages. The eslint-scope incident in 2018 followed this pattern—a compromised maintainer account was used to publish a version that stole npm tokens from users.

Social engineering of maintainers: Offering to help maintain a project, gradually gaining trust, then inserting malicious code. This is the XZ model. It’s harder to detect because the attacker’s contributions are initially legitimate.

Build system compromise: Attacking the CI/CD infrastructure used to build and publish packages rather than the source code itself. If an attacker compromises a project’s GitHub Actions workflow, they can inject code during the build process that doesn’t appear in the source repository.

Defensive measures exist but require effort:

Dependency pinning locks your dependencies to specific versions rather than version ranges. This prevents automatic upgrades that might include compromised versions. It also means you need to manually review and update versions, which creates a maintenance burden.

Lock files (package-lock.json, Pipfile.lock, Cargo.lock) record the exact versions and integrity hashes of all dependencies. They should be committed to version control and treated as security-relevant files. Any unexpected change to a lock file deserves scrutiny.

Software Composition Analysis (SCA) tools scan your dependency tree for known vulnerabilities and suspicious patterns. Snyk, Dependabot, and Socket.dev are popular options. They can’t catch zero-day supply chain attacks, but they catch known bad packages quickly.

Reproducible builds ensure that the binary you run was built from the source code you reviewed. If a published package can’t be reproduced from its source, something was added during the build process. This is technically challenging but increasingly supported by major package registries.

Vendor dependencies by copying them into your repository rather than fetching them from registries at build time. This makes dependency updates a conscious decision and prevents registry compromises from affecting your builds. Go’s module system encourages this approach.

Review new dependencies carefully. Before adding a package, check its maintenance activity, contributor count, download numbers, and source code. A single-maintainer package with sporadic updates and minimal downloads is higher risk than a well-maintained project with active community oversight. This isn’t foolproof—XZ had an active community—but it raises the bar.

The broader problem is structural. Open source infrastructure that billions of dollars of commercial software depends on is maintained by volunteers with no security budget. Initiatives like the Open Source Security Foundation are working on funding and tooling, but the fundamental imbalance between the critical importance of open source and the resources dedicated to securing it remains.

For individual developers and organisations, the practical takeaway is to treat dependencies as attack surface. Every package you add is code you’re trusting. Minimise unnecessary dependencies, pin versions, use lock files, and monitor for anomalies. It won’t prevent every supply chain attack, but it significantly reduces your exposure.