Skip to main content
  1. Blog/

npm Supply Chain Under Siege — The coa and rc Package Compromises

Osmond van Hemert
Author
Osmond van Hemert
Table of Contents
Supply Chain Security - This article is part of a series.
Part : This Article

If you’re a JavaScript developer and your CI/CD pipeline started failing around November 4th, you’re not alone. Two widely-used npm packages — coa (a command-line option parser with 9 million weekly downloads) and rc (a configuration loader with 14 million weekly downloads) — were compromised. Malicious versions were published that attempted to steal passwords and install cryptominers.

This comes barely two weeks after the ua-parser-js incident that I wrote about recently. The npm ecosystem is having a very bad autumn, and the pattern is becoming impossible to ignore.

What Happened
#

The attack followed a depressingly familiar pattern. The npm accounts of the package maintainers were compromised — likely through credential reuse or phishing — and the attackers published new versions containing malicious code.

For coa, versions 2.0.3, 2.0.4, 2.1.1, 2.1.3, and 3.0.1 were published on November 4th. For rc, version 1.2.9, 1.3.9, and 2.3.9 appeared shortly after. The malicious code was obfuscated but, when analysed, was found to:

  1. Detect the operating system
  2. Download a platform-specific binary from a remote server
  3. Execute the binary, which functioned as a password stealer (specifically targeting Chrome and Firefox stored credentials)
  4. On some variants, install a cryptocurrency miner

The npm security team acted relatively quickly, unpublishing the compromised versions within hours. But “within hours” in npm-land means potentially millions of installations.

Why This Keeps Happening
#

The fundamental issue hasn’t changed since I first started writing about npm supply chain risks: the JavaScript ecosystem has a deep dependency problem, and the security model hasn’t kept pace.

Account security is the weakest link. npm has offered two-factor authentication since 2017, but it’s not mandatory — even for packages with millions of dependents. After the ua-parser-js incident, npm announced plans to require 2FA for maintainers of high-impact packages. But that’s not implemented yet, and coa and rc were compromised in the gap.

Dependency depth amplifies blast radius. Both coa and rc are transitive dependencies pulled in by popular tools. coa is a dependency of css-loader and svgo, which are dependencies of create-react-app. If you ran npx create-react-app during the window of compromise, you were affected. The average npm project has hundreds of transitive dependencies, and most developers couldn’t name half of them.

Version ranges make it worse. Many package.json files use semver ranges like ^1.2.8, which automatically install newer minor and patch versions. The attackers exploited this by publishing versions that fell within common semver ranges. If your lockfile wasn’t committed (and you’d be surprised how many projects don’t commit lockfiles), npm install would happily pull the malicious version.

Practical Defences
#

I’ve been hardening Node.js build pipelines for years, and here’s what I recommend today:

Always commit your lockfile. Whether it’s package-lock.json or yarn.lock, it belongs in version control. This pins your transitive dependencies to exact versions and prevents surprise upgrades.

Use npm ci instead of npm install in CI/CD. The ci command installs exactly what’s in the lockfile and fails if the lockfile is out of sync with package.json. It’s faster and more deterministic.

Audit regularly, but don’t rely on it. npm audit catches known vulnerabilities in published advisories, but these supply chain attacks are zero-days — there’s no advisory until after the compromise is discovered. Audit is necessary but not sufficient.

Consider package pinning for critical dependencies. Tools like Socket and Snyk are beginning to offer supply chain detection that goes beyond known vulnerability databases. They analyse package behaviour changes between versions, which would have flagged the coa and rc attacks.

Evaluate alternatives to deep dependency trees. This is the hardest advice to follow, but it’s the most effective. Every dependency you add is a trust relationship. Every transitive dependency is a trust relationship you didn’t explicitly choose. Consider whether you really need that utility library, or whether a few lines of your own code would serve better.

My Take
#

Three major npm supply chain attacks in the space of a month (ua-parser-js, coa, rc) isn’t a coincidence — it’s a campaign. The JavaScript ecosystem’s popularity and its dependency model make it the highest-value target for supply chain attacks in the software world.

GitHub and npm are starting to take this seriously. The upcoming 2FA requirements for top packages are a good step. But we need more fundamental changes: better provenance tracking, package signing, and reproducible builds. The Sigstore project and npm’s own plans for package signing give me some hope.

In the meantime, treat your node_modules directory like what it is: a collection of code from thousands of strangers that runs with full access to your system. Lock your dependencies, monitor for anomalies, and think twice before adding that next npm install.

The npm ecosystem’s greatest strength — its vast package registry and frictionless dependency management — is also its greatest vulnerability. Until the security model catches up with the scale, these attacks will continue.

Supply Chain Security - This article is part of a series.
Part : This Article

Related