Shai-Hulud Returns: Over 300 NPM Packages Infected
Scope and behavior of the attack
- Worm infects npm packages by adding a
preinstallscript (node setup_bun.js) and a huge obfuscatedbun_environment.js(~10MB). - On install, it runs TruffleHog‑style secret scanning on the machine and exfiltrates npm tokens, cloud creds, env vars to GitHub repos; then uses stolen npm tokens to republish compromised versions of any packages it can access.
- Propagation is “worm‑like”: each infected maintainer’s environment can in turn infect more packages on publish.
- High‑profile SDKs were briefly affected (e.g. PostHog, Zapier, Postman, ENS, AsyncAPI). Those vendors rotated keys, unpublished bad versions and re‑released clean ones; impact appears time‑limited but still under investigation.
Is Node/npm uniquely bad?
- One camp: this is fundamentally a package‑manager problem, not a Node problem; any ecosystem with easy 3rd‑party publishing (PyPI, Cargo, RubyGems, Go modules) is vulnerable.
- Opposing view: npm is worse in practice due to culture (micro‑packages, “update constantly”), semantics (version ranges instead of strict pinning), and npm’s willingness to run arbitrary lifecycle scripts on install.
Ecosystem and packaging model criticisms
- JS/Node widely criticized for:
- Extremely deep dependency trees for trivial tasks.
- Automatic or frequent dependency updates, including transitive ones.
- Postinstall/preinstall scripts executing with full user privileges by default.
- Some argue that languages without convenient central package managers (C/C++, Odin) are more secure because they discourage huge dependency graphs, at the cost of more in‑house code.
- Others note this just shifts risk to hand‑rolled “Utils” code and outdated libraries.
Comparisons to other ecosystems
- Go and Rust praised for tooling (e.g. cargo vendor, MVS in Go) but criticized for also trending toward large graphs and missing “batteries” in the stdlib.
- Maven/.NET highlighted as relatively safer:
- Namespaced coordinates tied to domains.
- Signing, domain verification, and fewer transitive deps thanks to rich standard/first‑party libs.
- Linux distros (Debian, etc.) cited as a better model: curated maintainers, delayed/staged releases; but expensive to scale to npm’s volume.
Proposed mitigations in the thread
- Pin exact versions; use lockfiles; avoid auto‑updating; adopt “dependency cooldowns” (e.g. pnpm/bun minimumReleaseAge, uv’s
--exclude-newer). - Disable or whitelist install scripts (
ignore-scripts, pnpm/bun defaults). - Use alternative CLIs (pnpm, bun) that are stricter by default.
- Vendor or mirror dependencies; use internal npm registries with review and delayed promotion.
- Run npm in containers/VMs or sandboxes (bubblewrap, Podman), and keep sensitive credentials out of dev environments.
- Move to OIDC/“trusted publishing” instead of long‑lived npm tokens; alert on publishes not tied to CI.
Deeper fixes and culture
- Calls for:
- Richer standard libraries to reduce dependency sprawl.
- Tiered ecosystems (true stdlib, vetted “blessed” libs, then free‑for‑all).
- Package‑level capability restrictions (FS/network permissions) rather than full host access.
- Many argue the core problem is social: over‑trust of random packages and lack of review, not just npm’s mechanics; others counter that insecure defaults make that behavior inevitable at scale.