- NPM
- Hacking
- Malware
- Web3
- Cybersecurity
NIH wasn’t the sin. Outsourcing every keystroke was.
The latest npm compromise showed how a tiny change deep in a dependency supply chain can rewrite what users see, sign, and send.

The latest npm compromise showed how a tiny change deep in a dependency supply chain can rewrite what users see, sign, and send.
A wake-up call
On September 8, 2025, attackers phished a well‑known npm maintainer and pushed malicious versions of 18 packages, including chalk and debug — together representing billions of weekly downloads. The payload hooked browser APIs ( fetch, XMLHttpRequest, window.ethereum, etc.) to intercept wallet interactions and rewrite payment destinations, hijacking funds in the process. Security firms and media traced the intrusion to a convincing look‑alike “npm support” domain and documented the drainer’s behavior.
Two points matter for defenders:
- Blast radius through transitive deps. Many apps pulled the bad code not directly but because something else depended on it.
- The target was the browser and wallets. The malware ran client‑side and focused on web3 flows — exactly where dApps can lose real money.
Why “just review your dependencies” is a fantasy in JS
Transitive depth & volume
In Node/JS, most risk hides indirectly. Snyk has reported ~86% of Node.js vulnerabilities occur in transitive dependencies, not the ones you picked yourself. That’s the part you don’t see in code review.
Multiple copies, multiple versions
Node’s resolver happily installs many versions of the same library to satisfy conflicting ranges. npm has a dedupe command and a --prefer-dedupe switch, but the default often favors newer versions — even if that increases duplication. In practice, you ship a bundle containing several ansi-* variants, each with its own update and risk profile.
Overriding sub‑deps is possible but brittle
Modern npm supports "overrides" to force a single version of a nested dependency across the graph, yet teams still report sharp edges. You can pin ansi-regex everywhere, but you are now maintaining part of the tree yourself.
Registry ≠ repository
The package you install from the registry doesn’t need to match the GitHub repo you skimmed. Attackers exploit that gap.
All of this makes “verify every dependency” closer to a research project than a build step.
Dev dependencies do matter: attackers target developers
Treat devDependencies as production‑grade risk. Attackers love them because they reach your laptop, your CI, and your tokens.
ESLint compromise (2018)
After an npm account takeover, malicious eslint-scope and eslint-config-eslint versions exfiltrated ~/.npmrc tokens during install — aimed squarely at developers.
Nx build system incident (Aug 2025)
Malicious @nx/* releases ran a post‑install script on macOS/Linux that hoovered SSH keys, npm/GitHub tokens, .env files, and wallets, then used AI CLI tools (Claude, Gemini) for recon and exfiltration — creating a “s1ngularity‑repository” in victims’ GitHub accounts. That’s dev tooling as the entry point to everything.
eslint‑config‑prettier and friends (July 2025)
Phishing led to malicious releases of popular lint/format packages — again, developer‑focused components.
When dev tools go bad, two things happen: you lose secrets, and your frontend build can be tainted — leading to user‑facing compromise.
The web3 twist: frontends sign money
Crypto users don’t just read your site; they sign transactions from it. That’s why attacks keep circling wallets:
event‑stream → Copay (2018)
a dependency hand‑off let a new maintainer inject flatmap-stream, selectively targeting the Copay Bitcoin wallet to steal keys on builds between specific versions.
ua‑parser‑js (2021)
hijacked releases shipped a cryptominer and credential stealer on install. Widely present in frontend stacks.
In the latest chalk /debug cluster, malware in the browser rewrites wallet interactions and destinations.
Mocking “NIH syndrome” pushed many teams toward tiny utility packages for everything. That made build graphs wide and intricate, increasing the places an attacker can hide.
How OKcontract adresses this challenges
- Minimize external code Our goal for dApps is minimal (ideally zero) third‑party runtime deps outside our monorepo. Where essentials are needed, we re‑implement them ourselves — github.com/okcontract/cells is an example core library designed with no external runtime dependencies. Fewer moving parts means fewer places malware can enter.
- Monorepo + reproducible builds One place to audit, one place to lock versions, one place to enforce provenance.
- **Deploy artifacts to IPFS; treat the CID as the trust root** A CID is content‑addressed: change one byte and you get a different CID. Users, partners, or reviewers can pin and verify exactly what we shipped. “Given CID ⇒ content” is the model.
A practical playbook for dApp teams
Detect & contain
- Freeze installs when news breaks: redeploy only from a known‑good lockfile
- List and explain why something is in your tree:
[npm ls](<https://docs.npmjs.com/cli/v7/commands/npm-ls/>)/npm explain - Search for compromised versions explicitly in your SBOM or lockfile; cut new builds only after you verify they’re absent.
Make version sprawl manageable
- Prefer a single copy:
npm install --prefer-dedupeand periodic[npm dedupe](<https://docs.npmjs.com/cli/v9/commands/npm-dedupe>) - Force a known‑good sub‑dep everywhere with
"overrides"in[package.json](<https://docs.npmjs.com/cli/v9/configuring-npm/package-json>)(and the equivalent in pnpm/yarn). Keep a short allowlist of overrides you audit.
Shut down install‑time surprises
- In CI and on build servers, set
NPM_CONFIG_IGNORE_SCRIPTS=trueand runnpm ci. This blockspreinstall/postinstallhooks — the vector used in several dev‑tool compromises. (Opt‑in per‑package if you truly need a script.)
Reduce registry risk
- Treat registry content as authoritative, not the GitHub repo; verify the exact tarball that will be installed
- Vendor or mirror the few runtime libraries you can’t live without. For everything else, prefer your own code.
Raise the bar for what you publish
- Enable npm Trusted Publishing / provenance so consumers can verify where your package was built and by whom (Sigstore/Rekor). If you publish any libraries, make this non‑negotiable.
Harden the developer edge
- Assume dev tooling is hostile. Rotate tokens regularly; limit token scope; block egress to pastebins and random IPs from CI; quarantine AI CLI creds if you use them. The Nx incident showed post‑install malware sweeping machines and even co‑opting AI assistants.
Ship something that can be verified
- Publish your **build artifacts to IPFS** and communicate the CID users should pin. Content addressing means anyone can check they got exactly the bits you signed off on.
“NIH” as a security control
The culture that mocked rebuilding small utilities pushed us toward sprawling graphs where a maintainer’s bad day — or a phish — can become your users’ drained wallet. The September 8th attack on chalk /debug shows how little code an adversary needs to sabotage a dApp’s frontend and wallet flow. Pulling back to fewer dependencies, authoring core building blocks ourselves (like cells), and stamping releases with verifiable artifacts (IPFS CIDs, provenance) is not purism; it’s risk management.
Originally published on Medium.