~/opinions $ npm install // 847 packages from 312 strangers

VENDOR
EVERYTHING

why package managers suck for code, why you're wrong if you disagree, and why they are objectively a security nightmare

11 lines of left-pad broke the internet 2.6B weekly downloads hijacked by one phish 19.7% of AI-suggested packages don't exist

[01] THE CLAIM

You didn't add a dependency. You subscribed to a stream of future code from someone you have never met, and you gave it write access to production.

Let's be precise about the target. Your distro's package manager is fine: curated repos, signing keys, maintainers with names and reputations. The indictment is language package managers: npm, pip, and yes, cargo too. Registries where anyone publishes anything, where installing one library means trusting its author, its author's authors, and whoever phishes any of them next Tuesday. Using other people's code is good engineering; nobody is telling you to rewrite SHA-256 from the spec for fun (I did, it was great). The problem is the subscription: version ranges, postinstall hooks and transitive resolution mean the code in your build tonight is not the code you reviewed, ever.

[02] THE RAP SHEET

a partial incident log. every one of these actually happened, and notice how the entries accelerate:

2016 npm left-pad A maintainer unpublishes his 273 packages over a trademark dispute; one of them is 11 lines that pad a string. Babel, React and half the internet stop building within minutes, and npm has to invent "un-unpublishing" to make it stop.
2018 npm eslint-scope One maintainer's reused password, no 2FA. The hijacked version pulls code from Pastebin and harvests the npm tokens of everyone who installs it; npm ends up revoking every token on the registry issued before that morning.
2018 npm event-stream A burned-out maintainer hands the keys to a helpful stranger. The stranger ships a bitcoin-wallet stealer in a transitive dep; the poisoned version is downloaded eight million times, and the payload politely activates only for wallets holding over 100 BTC.
2019 rubygems ruby season bootstrap-sass grows a backdoor that evals the contents of a cookie named to impersonate Cloudflare's. rest-client starts exfiltrating env vars, but only in environments named production, so developers never see it.
2021 npm·pypi·gems dependency confusion Alex Birsan publishes packages matching companies' internal names, at version 9.9.9, across three registries. Build systems at Apple, Microsoft, Tesla and 32 other companies auto-pull his code and run it. $130,000 in bounties for the politest possible version of this attack.
2021 npm ua-parser-js Eight million downloads a week, hijacked to ship a Monero miner and a password stealer. Malicious for roughly four hours, still earned a federal CISA alert. The same actor came back two weeks later for coa and rc: another 23 million weekly downloads.
2022 npm colors & faker The author sabotages his own packages with an infinite loop, on purpose, to make a point about unpaid labor. 19,000 dependent packages, AWS's own CDK among the casualties. Your build trusts his mood.
2022 npm node-ipc A maintainer ships code that geolocates your IP and, if you're in the wrong country, overwrites your files with heart emoji. CVSS 9.8 protestware, pulled in by the Vue CLI; an NGO's monitoring server in Belarus got wiped.
2022 pypi ctx & phpass A package dormant for eight years gets taken over because its maintainer's email domain expired. Buy the domain for a few dollars, request a password reset, own 22,000 weekly downloads. No exploit anywhere; the registry worked as designed.
2022 pypi torchtriton Someone registers the name of PyTorch's internal nightly dependency on public PyPI; pip's resolution rules prefer it. Every Linux install of PyTorch-nightly over Christmas exfiltrates ssh keys and the first thousand files in $HOME, hidden inside DNS queries.
2023 npm ledger connect-kit A former employee gets phished; the stolen session token still publishes. The hardware-wallet company's own connector ships a drainer straight into every dApp loading it from CDN. $610,000 gone in about two hours.
2023+ pypi typosquat season Malware waves so relentless PyPI repeatedly suspends new user registration just to catch its breath: one March 2024 wave was 566 packages from 566 throwaway accounts, freezing the world's main Python registry for ten hours.
2024 trust chain xz-utils Three years of patient social engineering to become co-maintainer, then a backdoor in the compression library under half the internet's ssh. Caught by one engineer noticing 500 milliseconds of latency. You trust people, and people get got.
2024 cdn polyfill.io The domain serving a polyfill script to 100,000+ sites quietly changes owners, then starts injecting redirects to scam sites, but only for mobile visitors and never for admins. The dependency didn't update; its landlord did.
2024 npm drainer season lottie-player, the animation widget, pops fake wallet-connect prompts on every site auto-loading "latest" from CDN. Five weeks later @solana/web3.js itself ships a backdoor exfiltrating private keys. Both via one phished publisher each.
2025 npm chalk & debug One maintainer falls for a fake 2FA-reset email from npmjs.help, and 18 packages totaling roughly 2.6 billion weekly downloads ship a crypto-clipper. The largest blast radius in npm history nets the attacker under a thousand dollars, which somehow makes it worse.
2025 npm shai-hulud worm A self-replicating worm rides install scripts, harvests tokens with TruffleHog, then uses victims' own npm credentials to republish itself into 500+ packages. The November sequel hits 796 packages, 20 million weekly downloads, and wipes your home directory if it can't find anything to steal.
2025 npm·ai nx "s1ngularity" Malicious nx versions invoke the victim's own AI tools: Claude Code, Gemini CLI, Amazon Q, run with permission checks disabled and prompted to hunt the filesystem for wallets and secrets. 2,349 credentials leaked. Your assistant, conscripted in one prompt.
2025-26 vs code glassworm A self-propagating worm in VS Code extension marketplaces, hidden in invisible Unicode characters so the code literally cannot be seen in an editor, with command-and-control written into Solana transactions so it cannot be taken down. 35,800 installs in the first wave; still resurfacing.
2026 vs code github itself A trojaned Nx Console extension sits on the official marketplace for eighteen minutes. That's long enough: one GitHub employee installs it, and attackers clone roughly 3,800 internal GitHub repositories. The registry operator got owned by a registry.
2026 npm·pypi the teampcp spring One group's season: Trivy and Checkmarx (the security scanners), LiteLLM (the AI proxy), the entire TanStack family, Mistral's official SDKs and Red Hat's official npm namespace, 170+ packages poisoned through one corrupted CI cache. The tools that were supposed to catch this were the delivery vehicle.
2026 crates.io rust, too In February the Rust team announces that malicious crates are now too routine to blog about individually; they'll just file advisories. By June a legitimate crate with 18,000 downloads ships a build.rs that exfiltrates your project's source. The safest registry, normalized.

[03] THE MATH

trust multiplies, it never divides

You vet one library. It resolves to dozens of transitive packages, each with its own maintainers, each one phishing email away from shipping code into your build. Security composes with AND, and registries compose it with OR.

install scripts run code

postinstall hooks execute on your dev box at install time, with your shell, your env vars, your ssh agent and your cloud credentials. Before your app even starts, the dependency already had a better session on your machine than you did.

lockfiles lock code you never read

A lockfile pins a hash of something nobody on your team has looked at. That's not security, that's notarized ignorance. And the next update re-rolls the dice across the whole tree.

semver is a promise, not a property

"Patch releases are safe" is a social convention enforced by nothing. Every incident on the rap sheet shipped inside a version number that looked harmless.

the registry is a single point of pwn

One compromised maintainer account, one hijacked CI pipeline at the registry, one malicious mirror: the blast radius is everyone who installs, which is everyone.

deps rot, vendored code doesn't

Packages get unpublished, abandoned, renamed, acquired. A folder of source in your tree compiles the same way in ten years, with or without the internet, with or without npm inc.

[04] THE AI ERA

AI is making this worse and better at the same time. The trick is noticing which side each effect lands on.

Look at the rap sheet again: the entries from 2025 onward aren't just more frequent, they're a different species. Malware that conscripts your coding agent. Packages with documentation written for an LLM's eyes. Registries flooded at content-farm speed. And on the other side, the one thing that always made vendoring "impractical", the cost of actually reading code, just collapsed.

// worse

  • SLOPSQUATTINGabout one in five packages that LLMs recommend doesn't exist (19.7% in the USENIX study, 205,000+ unique hallucinated names), and the hallucinations repeat across sessions. So attackers register them and wait. One researcher squatted the hallucinated huggingface-cli: 30,000 downloads, and it ended up in the install docs of Alibaba's own research repo.
  • YOUR AGENT, CONSCRIPTEDthe nx attack ran victims' own Claude Code and Gemini CLIs with permission checks off and told them to find the wallets. The Amazon Q extension shipped a wiper written in plain English to a million installs: malware that executes on the LLM, not the CPU.
  • THE README IS EXECUTABLE NOWprompt injection through docs hits every major coding agent at 85%+ success rates in adaptive tests. The first malicious MCP server shipped 15 clean versions, then added one line BCC-ing every email your agent sent.
  • MALWARE AT CONTENT-FARM SCALENorth Korea alone pushed 1,700+ malicious packages across four registries in seven months, some with documentation deliberately engineered so AI agents would choose them. The 2026 Hades campaign explicitly harvests Anthropic API keys and Claude/MCP config files: the agents are now the loot too.
  • VIBE VELOCITYcode now ships faster than anyone reads it, and agents install dependencies on their own. Every "just npm install it" suggestion compounds a tree nobody is looking at, at a rate nobody ever looked at before.

// better

  • THE MODEL JUST WRITES ITseriously: most of the time you don't need the package at all. Ask for the function and you get good, dependency-free code (in Odin, of course) that you read once, test, and own forever. No registry, no maintainer, no postinstall. left-pad would never need to exist in 2026, and neither would half of any package.json. It is going to be fine. Better than fine.
  • READING CODE IS CHEAP NOWand when you do want someone else's library, an LLM reads the 40,000-line dependency tonight, flags the weird postinstall and the obfuscated blob, and writes you a summary. The strongest objection to vendoring, "nobody can review all that", just stopped being true.
  • MACHINE-SPEED REGISTRY DEFENSELLM-based scanners now read every package the moment it's published, with 99% precision in benchmarks, catching on the order of a hundred attacks a week. Most of the 2026 incidents on the rap sheet were caught by AI scanners, not by the registries themselves.
  • AGENTS PATCHING THE COMMONSautonomous security researchers are finding real CVEs and upstreaming fixes to under-maintained projects by the dozen: the exact neglected layer the xz attack exploited is finally getting free labor on the defense side.
  • STRUCTURE, FINALLYpost-worm, the registries are deprecating long-lived tokens, pushing trusted publishing, and shipping install cooldowns by default. A one-day cooldown would have blunted Shai-Hulud almost entirely. Progress, even if it took a worm named after a sandworm.

Now notice the asymmetry. Every "worse" is leverage for the publisher side of the registry: more packages, more trust, more velocity, less reading. Every "better" is leverage for the reader: cheaper review, cheaper audit, cheaper replacement. AI didn't rescue the registry model; it armed both sides and the reader's weapon is vendoring. The folder you can read just became the folder your agent already read.

[05] "BUT..."

the standard objections, handled:

  • "I'm not rewriting crypto myself." Correct, don't. Take the code: read it once, vendor it, own it. The objection confuses using other people's code (great) with subscribing to their future changes (the nightmare). fastr vendors libsecp256k1 at a pinned tag and builds it static. Bitcoin Core's actual crypto, zero registry involved.
  • "Lockfiles and npm audit have this covered." audit is a noise machine trained on a CVE feed, and the lockfile pins code you never read. event-stream was locked. ua-parser-js was locked. The attack arrives in the next bump of a transitive dep you don't know you have.
  • "Nobody can review all that vendored code." You aren't reviewing the registry stream either; you're just not admitting it. The vendored folder at least holds still: it cannot change without a diff in your repo, reviewed like all your other code, by the process you already trust. And it's 2026: an LLM will read the entire folder tonight and flag the weird parts. This objection is dead.
  • "But security patches arrive automatically." So do the backdoors. Same pipe, same speed, same version number. An update you pull deliberately, read, and commit beats an update that happens to you overnight in CI.
  • "This doesn't scale to real teams." SQLite vendors its dependencies. The Linux kernel vendors. Google runs a monorepo where third-party code is checked in and reviewed. The most serious software on earth already works this way; the registry model is the one that doesn't scale, it just fails quietly until it doesn't.
  • "You're just nostalgic." I run a relay that outpaces the industry standard 62 to 1 with zero registry dependencies. Nostalgia doesn't ship that; a threat model does. The burden of proof is on the side that runs strangers' postinstall scripts as a lifestyle.

[06] THE WAY

Take the code, not the subscription. Copy the source into your tree, read it, pin it, own it.

Updates become deliberate: you fetch a new version when you decide, you read the diff, you commit it like any other change. Dependencies become assets instead of liabilities. Your build works on an airplane, in ten years, after the registry is acquired and enshittified. This is how Sakura ships with zero dependencies, how fastr builds libsecp256k1 from a pinned tag, and how Odin treats the whole problem: core: and vendor: come with the compiler, everything else is a folder you can read.

A dependency you've read is just code. A dependency you haven't is a hostage situation.

The rule fits in one line: every byte that ships in your build should have a human on your side of the trust boundary who has seen it. Vendoring is not the inconvenient option, it's the honest one; the registry just hides the cost until invoice day.

// my node_modules had more authors than the bible, and i had met none of them.