Imperva has shown that crafted Brotli data can run attacker-supplied code on an official, unmodified Node.js build, by abusing CVE-2026-111497 in the runtime’s bundled decoder. Fixes for the 22.x, 24.x and 26.x lines shipped on 21 October, and any Node.js service that decompresses untrusted Brotli should be updated now.
What happened
On 21 October the Node.js project put out security releases for all three supported lines: 22.23.4, 24.22.1 and 26.12.1. The headline fix covers CVE-2026-111497, one of four rated high: an out-of-bounds write in the Brotli decompression code Node.js ships with. The advisory says an attacker who can feed Brotli data into an application’s node:zlib decompression can corrupt process memory. The result could be a crash, a leak of memory contents, or remote code execution. Node.js credits Khai Tran and Sylvie Mayer of OpenAI Codex Security with the report.
When the patch landed, the code execution outcome was still largely theoretical. An earlier public proof of concept managed code execution only against an intentionally hobbled build; on a standard one it produced a crash and a small memory read.
On 22 October, Imperva’s Red Team published what it calls Node2Shell. Using the stock 24.22.0 image on Linux arm64, without a debugger or instrumented binary, its proof of concept made a test application run code supplied inside Brotli data. On 24.22.1 the same input was refused and nothing ran. Imperva says the attack needed several attempts in its setup, and that it did not test other architectures or the 22.x and 26.x lines. As of 22 October, no exploitation in the wild had been reported.
The technical picture
Brotli is a compression format now commonly used for HTTP content, alongside gzip and deflate. As it is decoded, a Brotli stream announces which block types follow. According to the Node.js advisory, the vulnerable decoder misread one impossible block switch: rather than failing with a format error, it assumed it simply needed more data. Decoding therefore carried on in an invalid state and kept adding bytes to a small, eight-byte internal buffer. In a separate sanitiser run, Imperva saw that state produce a write beyond the intended buffer. The fix, taken from the upstream Brotli project, makes the decoder reject that condition outright.
Exposure depends on how an application uses Brotli, and this is where defenders need to look carefully. Node.js does not decompress Brotli just because it arrives. A plain HTTP server that receives a compressed body leaves it alone. Imperva tested two routes into the decoder that matter in practice:
- Inbound request bodies. Express 5’s body parsers, including json(), urlencoded(), text() and raw(), decode Brotli-compressed bodies on any route where they are applied. Imperva reproduced code execution at pinned versions within Express 5.0.0 to 5.3.0 and body-parser 2.0.0 to 2.3.0, without testing every release in between.
- Outbound responses. A service that calls another server with the built-in fetch() and receives a Brotli-encoded response also passes that data through the vulnerable decoder.
The second route is easy to overlook. Any internal service that fetches content from a partner API, a webhook source, a scraped page or a compromised third party is potentially exposed, even if it accepts no inbound traffic from the internet at all.
The advisory offers no workaround inside Node.js. Vercel added WAF rules on 21 October that block the inbound vector for projects on its platform, and Imperva has added its own rules. Vercel points out that its WAF cannot see responses an application fetches for itself, and both say only the runtime update closes the hole. Node.js 20.x and earlier are end of life and will not be fixed.

Why it matters for UK organisations
Node.js sits underneath a large share of UK digital estates, from retail checkouts and banking front ends to SaaS back ends, internal tooling, serverless functions and CI pipelines. Plenty of organisations that are not “Node shops” still run it inside vendor containers, build agents or self-hosted Next.js. Vercel tells self-hosted Next.js users to upgrade Node.js, noting that image optimisation does not block Brotli responses.
The bigger issue is that the vulnerable code lives in the runtime, not in an npm package. Many software composition analysis tools and dependency scanners report on package manifests, not on the Node binary baked into a container base image months ago. An organisation can show a clean dependency report and still be running 24.22.0 in production. Imperva and Vercel both stress that container images and other artefacts bundling a Node binary need rebuilding.
For organisations working towards Cyber Essentials, the scheme’s security update management control expects high or critical fixes to be applied within 14 days of release. That puts the deadline in early November for anyone running affected Node.js on in-scope systems. For suppliers to UK public sector bodies and regulated firms, it is reasonable to expect customers to ask for evidence of the patched runtime version.
Expert view
In my experience, runtime bugs like this get patched late, not because teams ignore them but because nobody owns the runtime. Application teams own the code and its dependencies, platform teams own the clusters, and the Node binary inside an image falls between the two. When we review container estates, it is common to find several Node.js versions in use across one organisation, some of them long out of support.
I would not wait for in-the-wild reports before acting. Imperva’s demonstration needed several attempts and covered one architecture, but it shows the bug can be turned into code execution on a stock build. Exploit reliability tends to improve quickly once that is established. The fetch() route also means that the threat model is not limited to whoever can reach your API. Anyone able to influence content your services pull in may be able to reach the decoder.
The fix is clean and the exposure conditions are well described, so this is a manageable patch, provided you can find every Node.js runtime you run.
What to do now
- Upgrade the runtime. Move to 22.23.4, 24.22.1 or 26.12.1 or later. Include hosts, serverless settings, CI runners and developer machines.
- Rebuild container images. Rebase on patched Node.js images and redeploy. Check the Node version inside running images directly, not just in manifests or lockfiles.
- Retire Node.js 20.x and older. These lines get no fix. Move them to 22.x or 24.x.
- Map Brotli paths. Identify routes using Express 5 or body-parser 2 parsers, and services that fetch() content from outside your control. Patch those first.
- Use WAF rules as a stopgap only. Edge filtering can reduce inbound risk while you roll out the fix. It does nothing for responses your services fetch.
- Watch for anomalies. Imperva suggests that unexpected process exits or decoder errors during decompression can help prioritise investigation, though neither proves exploitation on its own.
- Ask your suppliers. Request confirmation of patched Node.js versions from vendors whose appliances or hosted services run on it.
Bottom line
Node2Shell turns a high-severity decoder bug into a demonstrated code execution path on an official Node.js build. There is no runtime workaround and WAF coverage is partial, so the only real fix is updating Node.js everywhere it runs, including the images and build systems that are easy to forget.