TL;DR
npm audit went red on a lot of projects between 28 and 30 September 2026 because GitHub added 12 axios and 11 undici advisories to its database in three days, not because anything new was attacked. Most were disclosed weeks earlier and the fixes were already on npm. npm audit fix clears the node_modules copies. One undici bug, a process-killing WebSocket crash, also ships inside Node itself, and npm audit can't see that copy. Upgrade Node too.
Picture it: you ran npm audit on a Monday and got a clean report. You ran it again on Thursday and axios had a dozen advisories, undici had ten more, and nothing in your repo had changed.
That's what happened to a lot of Node projects at the end of September. We pulled the advisory records to see why, and then tested the one bug in the batch we thought most readers would actually hit.
What actually changed on 28-30 September
npm audit sends your dependency tree to the npm registry's bulk advisory endpoint and prints whatever comes back. In practice that list tracks the GitHub Advisory Database: these entries started showing up in audit output once GitHub had reviewed them. When the list gains an entry, every project with a matching version goes red, whether or not that project changed.
According to the advisory records in OSV, the database added, between 25 September and 3 October:
| Package | Advisories added | Database date |
|---|---|---|
axios | 12 | 2026-09-30 |
undici | 11 | 2026-09-28 and 09-29 |
nodemailer | 5 | late Sep |
fastify | 5 | late Sep |
fast-uri | 4 | late Sep |
@grpc/grpc-js | 2 | late Sep |
dompurify, joi, serialize-javascript, engine.io, moment | 1 each | late Sep |
The date that matters is a different one. Each GitHub advisory page carries several dates, and the one labelled Published is usually not the one labelled Published to the GitHub Advisory Database.
The fixes came first
Take the two biggest batches.
undici. GHSA-rfgv-xxqx-mfg5 shows "Published Sep 4, 2026" and "Published to the GitHub Advisory Database Sep 29, 2026". The fixed releases, undici 6.28.1, 7.29.1 and 8.10.2, all went up on the npm registry on 4 September. So the patch existed for more than three weeks before npm audit could tell you about it.
axios. GHSA-3pq3-5fj3-cg6v, the HTTP/2 proxy bypass, shows "Published Sep 16, 2026" and "Published to the GitHub Advisory Database Sep 30, 2026". The fix, axios 1.20.0, was on npm from 26 August, three weeks before the advisory itself. All 12 axios advisories in the batch carry that same 16 September publication date. (Two of them also cover the old 0.x line, whose fix, 0.34.0, only landed on 16 September.)
Fix first, advisory second, database entry last. The gap between disclosure and database was about two weeks for axios and three and a half for undici. What made this batch feel like an outbreak was that the last step landed for dozens of advisories in the same three days.
If you upgraded axios 1.x or undici in your project at any point in September, you may never have been on a vulnerable version there at all. For those copies, the red you saw at the end of the month was the warning catching up with a fix you already had. Node's own copy of undici is a separate story, below.
Most of these need a feature you probably don't use
We went through the axios and undici advisories in the batch. A lot of them only fire when you've turned something on:
- axios HTTP/2 proxy bypass needs
httpVersion: 2. In axios 1.19.0 the HTTP adapter setshttpVersion = 1when you don't pass one. - axios
data:URL ReDoS needs you to pass untrusted URL strings straight into axios. - axios
maxRedirects: 0bypass only matters if you rely on that option as an SSRF guard while using the fetch adapter. - undici TLS bypass needs
BalancedPoolwith a function-valuedconnectortlsoption. - undici cookie disclosure needs
interceptors.cache(), in its default shared mode, in front of an untrusted or multi-user upstream.
That doesn't mean ignore them. It means npm audit fix is the whole job for most apps, and you don't need to spend an evening proving reachability for each line.
The exception is the one that needs nothing turned on.
The undici bug that kills the process
CVE-2026-19534 (High, 7.5) is a crash in undici's WebSocket client. If a server answers the handshake with a Sec-WebSocket-Protocol header the client never asked for, undici throws a TypeError inside a microtask with no try/catch. That's an uncaught exception, and the Node process exits. The advisory says it hits the default new WebSocket(url), with no subprotocol requested.
It covers undici from 6.7.0 up to the fixed releases. And this is where npm audit stops helping.
Node has its own copy
Node's global WebSocket and fetch don't load undici from your node_modules. They run on a copy compiled into Node itself. You can see its version:
node -p "process.versions.node + ' bundles undici ' + process.versions.undici"
npm audit only reads your lockfile, so it never sees that copy. A project with zero dependencies can pass npm audit and still be running a vulnerable undici.
We crashed it
We wrote a 10-line TCP server that completes a WebSocket handshake and adds Sec-WebSocket-Protocol: evil, then pointed Node's built-in WebSocket at it. No npm packages involved.
| Runtime | Bundled undici | Result |
|---|---|---|
| Node 22.22.0 | 6.23.0 | TypeError: Cannot read properties of null (reading 'includes'), process exits with code 1 |
| Node 22.23.3 | 6.28.1 | onerror fires ("Protocol was not set in the opening handshake."), process keeps running |
npm undici@7.29.0 | n/a | Same crash as Node 22.22.0 |
npm undici@7.29.1 | n/a | onerror, close code 1006, process keeps running |
So the fix works, and on old Node the crash is real. Your onerror handler doesn't save you either: the throw happens before undici gets as far as calling it.
Going by the bundled copy in each Node release tag, Node 22.23.3 (released 23 September) ships undici 6.28.1, Node 24.21.0 and later ship 7.29.1, and Node 26.12.0 ships 8.11.2. Node 22.23.2 still ships 6.28.0, which is inside the affected range.
Who is actually exposed: a server-side app that opens a WebSocket out to a server it doesn't control (a third-party feed, a user-supplied URL, anything over plain ws://), on a Node build older than those versions. A browser talking to your server isn't this bug. Your server dialling someone else's is.
The five-minute check
Run npm ls axios undici. axios should be 1.20.0 or later. undici should be 6.28.1, 7.29.1, 8.10.2 or later on its line.
Run npm audit fix. On our test project with axios 1.19.0 and undici 7.29.0 (caret ranges), npm audit showed 22 advisories rolled up as "2 high severity vulnerabilities", and after the fix it reported "found 0 vulnerabilities". If your package.json pins an exact version, npm audit fix won't move it; bump the version yourself rather than reaching for --force.
Run node -p process.versions.undici on the machine or image that runs production, not just your laptop. If it's below 6.28.1 on Node 22, or below 7.29.1 on Node 24, upgrade Node.
Pin the Node version so the fix sticks: an engines.node entry in package.json, a .nvmrc, or the FROM node: tag in your Dockerfile, depending on how you deploy.
Why AI-built apps sit right in the blast radius
axios is the HTTP client AI assistants reach for by default, and undici arrives through other packages even when you never asked for it. Neither is in a founder's head as "a dependency I chose". So the red report reads like something broke, the natural reaction is to ask the assistant to "fix the vulnerabilities", and the assistant runs npm audit fix and stops.
That handles node_modules. It doesn't touch the Node runtime, and an agent working from the audit output has no reason to look there, because the audit output never mentions it.
A dependency report describes what's in the lockfile, not what's answering requests. Most of the time those line up. This is one of the times they don't.
Why did npm audit suddenly show new axios and undici vulnerabilities?
Because GitHub added them to its Advisory Database between 2026-09-28 and 2026-09-30, and npm audit reads that database. The bugs were mostly disclosed earlier (undici on 2026-09-04, the axios HTTP/2 one on 2026-09-16) and the fixed versions were already on npm. Your code didn't change. The list it's checked against did.
Was I exposed the whole time?
Only if you were running an old version and using the affected feature. The undici fixes shipped on npm the same day the advisories were published, and axios 1.20.0 was on npm from 2026-08-26. Many of these bugs also need an option most apps never set, such as axios httpVersion 2 or undici BalancedPool.
Does npm audit fix clear it?
For the copies in node_modules, usually. On a test project with axios 1.19.0 and undici 7.29.0 in caret ranges, npm audit listed 22 advisories as 2 high severity vulnerabilities, and npm audit fix moved to axios 1.20.0 and undici 7.30.1 with 0 left. Exact-pinned versions won't move, so bump those by hand.
Is Node's built-in WebSocket affected by the undici crash?
Yes, on older Node builds. Node's global WebSocket and fetch run on a copy of undici bundled inside Node, which npm audit never scans. We crashed Node 22.22.0 with one bad handshake. Node 22.23.3 and 24.21.0 bundle the fixed undici. Check with node -p process.versions.undici.
Check what's actually running
CheckYourVibe scans your deployed app, not just your lockfile, so you see what a stranger can reach today.