What npm Supply Chain Attacks Made Me Realize
The JavaScript ecosystem scaled trust faster than verification.
Over the past few months, npm supply chain attacks stopped feeling like isolated incidents.
A compromised package here. A malicious dependency there. Another ecosystem-wide security advisory is being discussed every time I open Twitter.
But recently, a few incidents have started to feel qualitatively different.
In March, malicious versions of Axios briefly appeared on npm, carrying hidden dependencies designed to steal credentials and establish remote access on developer machines. Microsoft later linked the campaign to a North Korean threat actor. The compromise window lasted only minutes, but Axios sits in millions of dependency trees. For many developers, a routine npm install was enough exposure.
Then came Shai-Hulud, a self-replicating npm worm that spread through compromised packages inside the registry itself. Shortly after, variants targeted SAP ecosystems, while other campaigns compromised tools like Trivy, KICS, and LiteLLM. Attackers were no longer just abusing application dependencies. They were targeting the software supply chain itself.
Around the same time, even mainstream ecosystem tooling like TanStack started dealing with supply chain-related security concerns. That was the moment it stopped feeling like a problem affecting obscure packages at the edges of npm.
What surprised me was not just the attacks themselves. What surprised me was how normal the response felt.
Pin the version. Wait for the patch. Update dependencies. Move on.
Nobody really stopped to question the underlying trust model that made these incidents possible in the first place.
What I realized about the ecosystem I depend on
Every time I run npm install, I am potentially executing third-party code on my machine through lifecycle scripts, native binaries, transitive dependencies, and dynamically downloaded artifacts.
That sounds obvious when stated directly, but I do not think most JavaScript developers emotionally process what that actually means at an ecosystem scale.
The JavaScript ecosystem optimized heavily for convenience, automation, and composability.
That is a large part of why npm became so successful.
The same low-friction publishing model that accelerated innovation also created one of the largest software trust surfaces in the industry.
None of these risks are unknown. Lifecycle scripts, typosquatting, maintainer compromise, and dependency confusion have been discussed in security circles for years. The issue is not a lack of awareness. The issue is that modern JavaScript development still prioritizes developer velocity over strict trust boundaries in many places.
npm does provide important security mechanisms. Lockfiles, integrity hashes, provenance support, script controls, and audit tooling all reduce risk. But they solve different problems than many developers assume.
A lockfile guarantees reproducibility, not trustworthiness.
A checksum verifies artifact integrity, not safety.
An audit helps with known vulnerabilities, not newly compromised packages.
The uncomfortable reality is that most production applications execute enormous amounts of third-party code that nobody on the team has fully reviewed. That tradeoff is economically rational, but it also creates an enormous attack surface.
What keeps bothering me is how normalized this has become.
I deploy dependency trees containing hundreds of packages I have never read, and I accept that as normal because the alternative sounds impractical. We casually say “just review your dependencies” as if anyone realistically audits the full transitive dependency graph of a modern Node.js application by hand.
Security is no longer just about the code we write. It is increasingly about the code our tooling executes on our behalf.
Not every ecosystem problem is a supply chain attack
One thing I also realized while thinking about this is that not every issue belongs in the same category.
Some of the recent Next.js vulnerabilities were fundamentally different from malicious npm packages. Framework vulnerabilities, insecure defaults, rushed releases, and ecosystem-wide architectural assumptions are separate problems from supply chain compromise.
But they still point toward the same broader pattern.
The JavaScript ecosystem moves extremely fast. New runtimes, frameworks, AI SDKs, bundlers, and infrastructure tooling get production adoption almost immediately, often before their operational and security characteristics are fully understood.
I have become much more conservative about that.
I no longer assume that something being technically impressive or widely hyped automatically makes it production-ready. I want to understand:
how it executes code
what permissions it expects
how updates are distributed
whether install-time scripts are involved
how quickly vulnerabilities get patched
whether the architecture itself introduces new trust assumptions
Technical innovation moves faster than operational hardening. That gap matters more than most developers realize.
The ecosystem is slowly correcting itself
One thing that gives me some optimism is that newer tooling is starting to treat dependency trust as a first-class engineering concern instead of an afterthought.
Package managers like pnpm and Bun introduce stricter dependency isolation models than the traditional flat node_modules approach. pnpm’s content-addressable store and stricter linking behavior reduce entire classes of phantom dependency and dependency confusion problems that npm historically tolerated.
Features like minimum-release-age policies are also interesting because they acknowledge a simple reality: many malicious packages are discovered shortly after publication. Delaying adoption of freshly published packages by even a few hours meaningfully reduces exposure to fast-moving attacks.
Provenance and package signing are improving as well. npm now supports provenance attestations through trusted publishing flows, which helps verify where a package came from and how it was built. It is not a perfect solution, but it moves the ecosystem closer to verifiable supply chains instead of blind trust.
The part that embarrassed me as a JavaScript developer
The JavaScript ecosystem markets itself on accessibility and developer experience. I say the same things myself when people ask why I choose Node.js.
Those strengths are real.
They are also part of the attack surface.
The same low barrier to publishing that made npm extraordinarily successful is also what makes large-scale trust abuse possible. Anyone can publish packages. Millions of projects automatically consume them. CI systems execute them. Production systems depend on them.
For a long time, I treated that level of implicit trust as normal.
I am less comfortable with that assumption now.
What I actually do now
I pin dependencies aggressively and review lockfile diffs much more carefully during merges.
I pay far more attention to package.json files, especially lifecycle scripts like postinstall, binary downloads, GitHub-based dependencies, and sudden expansion in transitive dependencies. Most malicious packages do not initially look malicious. They look ordinary.
I use --ignore-scripts when evaluating unfamiliar packages. I use tools like Socket to inspect dependency behavior at install time. I try to minimize unnecessary dependencies entirely because every package introduces another trust relationship into the system.
I also prefer tooling with stricter dependency isolation and configure minimum-release-age policies so freshly published packages are not immediately trusted in production environments.
These habits probably sound paranoid today.
I suspect they will feel standard within a few years.
The JavaScript ecosystem is not fundamentally broken. It was designed for speed, accessibility, and composability, not adversarial environments. For years, that tradeoff mostly worked because the ecosystem operated on a strong social contract.
Now the ecosystem is large enough, automated enough, and economically important enough that attackers actively optimize against those assumptions.
The problem is not that open source failed.
The problem is that the industry scaled trust much faster than it scaled verification.
If you found this interesting, you can subscribe with your email below. I’ll be sharing more deep dives and breakdowns on infrastructure, AI systems, cloud architecture, and the kind of tech that usually stays behind the scenes.

