48,015 karma · joined September 1, 2009
Stanford visiting lecturer, CS 253 Web Security <https://cs253.stanford.edu> – Principles of web security, attacks and countermeasures, and more...
Open source maintainer – 100+ open source packages on npm, including WebTorrent <https://webtorrent.io>, StandardJS <https://standardjs.com>, BitMidi <https://bitmidi.com>, simple-peer <https://github.com/feross/simple-peer>, and more <https://socket.dev/npm/user/feross>.
You can reach me at {my username}@feross.org, or find out more on my website: https://feross.org/resume
[ my public key: https://keybase.io/feross; my proof: https://keybase.io/feross/sigs/gO6pVIJ1DXdy9Y21yil6nlyk_by5BE_GaaWOOQJ5PvQ ]
If you have a suggestion for how to scope down the permissions of our app, we’re all ears. But based on our research, it’s not possible.
More info documenting each permission: https://docs.socket.dev/docs/permissions
Disclosure: I’m the founder of Socket.
If you don't follow this space closely, here's the bigger picture: these are part of an organized campaign that's hitting popular packages and slipping in malware.
What makes this campaign different is how aggressive it is: the payload doesn’t just run locally -- it actively hunts for developer and CI/CD credentials, spins up rogue GitHub Actions, and uses those to keep propagating. That’s a step beyond the usual crypto miner or info stealer.
npm and other package registries have become the weakest link in modern software. Every developer depends on them, yet a single compromised dependency can cascade into thousands of downstream apps and companies.
At this point (given we just published research about this) we've upgraded this threat to Known malware.
So in short:
- “AI detected potential malware” = automated system found something suspicious
- “Known malware” = human confirmed it’s real
The wording is intentional because not every automated hit ends up being true malware. It’s better to give developers early visibility into possible threats, even if they turn out to be benign, than to miss a real attack.
A week waiting period would not be enough. On average, npm malware lingers on the registry for 209 days before it's finally reported and removed.
Source: https://arxiv.org/abs/2005.09535
npm stats lag. We observed installs while the malicious versions were live for hours before removal. Affected releases we saw: duckdb@1.3.3, @duckdb/duckdb-wasm@1.29.2, @duckdb/node-api@1.3.3, @duckdb/node-bindings@1.3.3. Same payload as yesterday’s Qix compromise. Recommend pinning and avoiding those versions, reviewing diffs, and considering a temporary policy not to auto-adopt fresh patch releases on critical packages until they age.
Strongly agree on artifact signing, but it has to be real end-to-end. If the attacker can trigger your CI to sign with a hot key, you still lose. What helps: 1) require offline or HSM-backed keys with human approval for release signing, 2) enforce that published npm artifacts match a signed Git tag from approved maintainers, 3) block publishes after auth changes until a second maintainer re-authorizes keys. In today’s incident the account was phished and a new token was used to publish a browser-side wallet-drainer. Proper signing plus release approvals would have raised several hard gates.
We analyzed this DuckDB incident today. The attacker phished a maintainer on npmjs.help, proxied the real npm, reset 2FA, then immediately created a new API token and published four malicious versions. A short publish freeze after 2FA or token changes would have broken that chain. Signed emails help, but passkeys plus a publish freeze on auth changes is what would have stopped this specific attack.
There was a similar npm phishing attack back in July (https://socket.dev/blog/npm-phishing-email-targets-developer...). In that case, signed emails would not have helped. The phish used npmjs.org — a domain npm actually owns — but they never set DMARC there. DMARC is only set on npmjs.com, the domain they send email from. This is an example of the “lack of an affirmative indicator” problem. Humans are bad at noticing something missing. Browsers learned this years ago: instead of showing a lock icon to indicate safety, they flipped it to show warnings only when unsafe. Signed emails have the same issue — users often won’t notice the absence of the right signal. Passkeys and publish freezes solve this by removing the human from the decision point.
A few concrete datapoints from our analysis of this incident that may help cut through the hand-waving:
1. This is the same campaign that hit Qix yesterday (https://socket.dev/blog/npm-author-qix-compromised-in-major-...). The injected payload is byte-for-byte behaviorally identical. It hooks fetch, XMLHttpRequest, and common wallet provider APIs and live-rewrites transaction payloads to attacker addresses across ETH, BTC, SOL, TRX, LTC, BCH. One tell: a bundle of very distinctive regexes for chain address formats, including multiple Solana and Litecoin variants.
2. Affected versions and timing (UTC) that we verified:
- duckdb@1.3.3 at 01:13
- @duckdb/duckdb-wasm@1.29.2 at 01:11
- @duckdb/node-api@1.3.3 at 01:12
- @duckdb/node-bindings@1.3.3 at 01:11
Plus low-reach test shots: prebid@10.9.1, 10.9.2 and @coveops/abi@2.0.1
3. Payout so far looks small. Tracked wallets sum to roughly $600 across chains. That suggests speed of discovery contained damage, not that the approach is harmless.
What would actually move the needle:
=== Registry controls ===
- Make passkeys or FIDO2 mandatory for high-impact publisher accounts. Kill TOTP for those tiers.
- Block publishing for 24 hours after 2FA reset or factor changes. Also block after adding a new automation token unless it is bound by OIDC provenance.
- Require signed provenance on upload for popular packages. Verify via Sigstore-style attestations. Reject if there is no matching VCS tag.
- Quarantine new versions from being treated as “latest” for automation for N hours. Exact-version installs still work. This alone cuts the blast radius of a hijack.
=== Team controls ===
- Do not copy-paste secrets or 2FA. Use autofill and origin-bound WebAuthn.
- Require maker-checker on publish for org-owned high-reach packages. CI must only build from a signed tag by an allowed releaser.
- Pin and lock. Use `npm ci`. Consider an internal proxy that quarantines new upstream versions for review.
=== Detection ===
- Static heuristics catch this family fast. Wallet address regex clusters and network shims inside non-crypto packages are a huge tell. If your tooling sees that in a data engine or UI lib, fail the build.
Lastly, yes, training helps, but the durable fix is making the easy path the safe path.
Running AI coding setups in containers (or even just VMs) seems like a solid default, and I’d love to see tooling move in that direction by default—less as a hard security perimeter, more as a safety net for people trying to move fast.
Re: the article’s conclusion—I get the skepticism. For what it’s worth, the product came after years of trying to solve the problem of package security and maintainer funding in the open. At some point, it felt like the best way to make a dent was to build something dedicated to it.
Totally understand the skepticism. It’s easy to assume commercial motives are always front and center. But in this case, the company actually came after the problem. I’ve been deep in this space for a long time, and eventually it felt like the best way to make progress was to build something focused on it full-time.
– The JavaScript ecosystem moves faster — way more packages, more frequent updates, and more transitive dependencies (avg 79 per package).
– npm has lower barriers to publishing, so it’s easier for malicious actors to get in.
– Java developers often use internal mirrors and have stricter review processes, while npm devs tend to install straight from the registry.
– But to be clear, supply chain attacks do happen in other ecosystems — they’re just underreported. We’ve seen similar issues in PyPI, RubyGems, and even Maven.
JavaScript just happens to be the canary in the coal mine.
- They cache packages but don’t analyze what’s inside.
- They scan or review the first version, then auto-approve every update after that.
- They skip transitive deps — and in npm, that’s 79 on average per package.
- They rely on scanners that claim to detect supply chain attacks but just check for known CVEs. The CVE system doesn’t track malware or supply chain attacks (except rarely), so it misses 99%+ of real threats.
Almost everything on the market today gives a false sense of security.
One exception is Socket — we analyze the actual package behavior to detect risks in real time, even in transitive deps. https://socket.dev (Disclosure: I’m the founder.)
(Disclosure: I’m the founder. https://socket.dev)
You can set a custom security policy to block or warn on file system, network, shell, or environment variable access.
Disclosure: I’m the founder.
Videos for CS253 are online here: https://www.youtube.com/playlist?list=PL1y1iaEtjSYiiSGVlL1cH...
Engineers are using AI to write code. Other engineers are using AI to review that code.
Same thing will happen in many other areas of life.
Now that we’ve shifted our company’s focus to https://socket.dev, I’d love to open source Wormhole. I’m quite proud of the code - I’ve worked on P2P and file transfer systems for so so long that I think this might be some of the best code I’ve worked on.
It’s just a matter of finding the time, but I expect this will be open source eventually.
Longest npm package name? 214 characters
Package with the most maintainers? 554 maintainers