# Node2Shell: Node.js Fixes Brotli RCE Across All Supported Versions

Node2Shell exposes Node.js to Brotli-based remote code execution. Check affected versions, verified exploit paths, patched releases, and deployment fixes.

Canonical URL: https://aiidelist.com/blog/node2shell-nodejs-brotli-rce-vulnerability-fix

Language: en

Published: 2026-10-23

Updated: 2026-10-23

## Key Takeaways

- **Node2Shell concerns CVE-2026-111497**, a high-severity memory-safety vulnerability in the Brotli decoder bundled with Node.js.
- **All three supported release lines were affected:** Node.js 22.x, 24.x, and 26.x. Security updates arrived on October 21, 2026.
- **The minimum patched releases are 22.23.4, 24.22.1, and 26.12.1.** Upgrade within the appropriate release line or use a newer supported release containing the fix.
- Exposure depends on whether attacker-controlled Brotli data reaches the decoder. Review both incoming request bodies and responses fetched from external servers.
- **Updating application dependencies alone does not replace patching the Node.js runtime.** Rebuild deployment artifacts and replace running processes. 

## What Is Node2Shell?

**Node2Shell is the name Imperva Red Team uses for its research into CVE-2026-111497**, a vulnerability that can turn malicious Brotli-compressed input into code execution inside a Node.js process.

The [Node.js security advisory](https://nodejs.org/en/blog/vulnerability/october-2026-security-releases) identifies an out-of-bounds write in the decoder and rates the issue **High**. It credits OpenAI Codex Security researchers Khai Tran and Sylvie Mayer with reporting the vulnerability, and Matteo Collina and Antoine du Hamel with fixing it. 

Brotli is a compression format commonly identified by the HTTP content coding `br`. Applications can encounter it through network traffic or explicit decompression operations. Node.js exposes Brotli functionality through its built-in [compression APIs](https://nodejs.org/api/zlib.html), including `brotliDecompress()` and `createBrotliDecompress()`. 

The security boundary is the decompression operation: bytes supplied by an untrusted party enter native code beneath the JavaScript application.

**Information checked: October 23, 2026.**

## Affected Node.js Versions and Minimum Fixed Releases

The following versions contain the official Node.js fix:

| Node.js release line | Minimum fixed version | Official release notes |
|---|---|---|
| 22.x | **22.23.4** | [Node.js 22.23.4](https://nodejs.org/en/blog/release/v22.23.4) |
| 24.x | **24.22.1** | [Node.js 24.22.1](https://nodejs.org/en/blog/release/v24.22.1) |
| 26.x | **26.12.1** | [Node.js 26.12.1](https://nodejs.org/en/blog/release/v26.12.1) |

Each release explicitly includes the Brotli patch for CVE-2026-111497. These are **minimum fixed versions**, rather than a recommendation to remain on those exact patches indefinitely. 

For official Node.js distributions, an earlier patch in one of these release lines requires updating. A distribution vendor may backport a security fix while retaining a different version scheme; in that case, confirm the installed package against that vendor's advisory.

**Unsupported versions require separate attention.** Node.js 20 is end of life and does not receive this official fix. Moving an application to a supported, patched line is the appropriate migration path. Its absence from the supported-version table should not be interpreted as evidence of safety. 

## How the Brotli Vulnerability Works

The [upstream Brotli patch](https://github.com/google/brotli/commit/0ed4268a4401abcb44720963) changes how the decoder distinguishes an invalid block transition from input that is merely incomplete.

Previously, the relevant decoding functions used a Boolean result. The patch introduces explicit decoder status codes, separating a request for more input from an invalid-format condition. An impossible block switch now produces `BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH`, and callers propagate that error.

This distinction matters in a streaming parser. **An incomplete stream can legitimately continue when more bytes arrive; a structurally invalid stream must stop.** Continuing with invalid internal state can lead to unsafe memory operations. The Node.js release notes confirm that the runtime incorporated this upstream correction. 

The practical implication is that application-level validation may occur too late. If a JSON request must first be decompressed, checking the resulting object's schema does not protect the earlier native decoding step.

Likewise, JavaScript exception handling should not be treated as containment for memory corruption inside a native decoder.

## What Researchers Have Actually Demonstrated

In its [October 22 technical report](https://www.imperva.com/blog/node2shell-from-brotli-data-to-remote-code-execution/), Imperva reported code execution on the **official Node.js 24.22.0 Linux ARM64 image**, without requiring a debugger or specially instrumented runtime.

Its tested entry points included:

- Brotli-compressed responses consumed through Node.js's built-in `fetch()`.
- Incoming request bodies processed by configured Express 5 and body-parser 2 parsers.

The same inputs were rejected by Node.js **24.22.1**. However, execution required multiple attempts, and the researchers tested selected package versions and application configurations rather than every intermediate release or architecture. 

These findings establish a practical laboratory exploit on a standard runtime build. They do **not** establish that every vulnerable deployment is equally exploitable, or that widespread production compromise has occurred.

For defenders, the appropriate distinction is between **a vulnerable runtime**, **a reachable decoding path**, and **evidence of an actual intrusion**. Patching addresses the first; application review establishes the second; incident investigation addresses the third.

## Which Applications Need Priority Review?

**Incoming request parsers**

The [Express 5 migration guide](https://expressjs.com/en/guide/migrating-5/) documents Brotli request-body support in `express.json()`, `express.urlencoded()`, `express.text()`, and `express.raw()`.

Consequently, applications can expose Brotli decoding through ordinary middleware configuration without explicitly importing a compression library. A parser must still be mounted on the relevant route and accept the request's content type. 

Review middleware order as well. If decompression happens before authentication, a later authentication check does not prevent unauthenticated input from reaching that decoder.

**Outbound HTTP clients and background jobs**

A service can encounter malicious compressed data while acting as an HTTP client. URL-preview services, importers, crawlers, document processors, and SSR applications deserve particular attention when they retrieve content from user-selected or otherwise untrusted destinations.

This prioritization follows from the demonstrated fetched-response path. A worker without a public listening port can still process untrusted network content.

**Direct Brotli consumers**

Review code using `brotliDecompress()`, `brotliDecompressSync()`, or `createBrotliDecompress()`. Trace where the input originates, including uploaded files, object storage, queue messages, and third-party data feeds. The HTTP header is not a prerequisite when application code explicitly invokes decompression. 

**Static sites and alternative runtimes**

A site built with Node.js and deployed as static files does not automatically have a Node.js request-processing runtime in production. Assess its build environment and any separate API services independently.

Similarly, Node.js API compatibility does not establish that a platform runs an affected Node.js binary. [Cloudflare documents its own compatibility implementation](https://developers.cloudflare.com/workers/runtime-apis/nodejs/); enabling `nodejs_compat` alone is insufficient to apply the Node.js version table to a Worker. That distinction also does not prove immunity from every underlying Brotli issue. 

## How to Check Your Exposure

**1. Identify the executable in each environment**

Start with a runtime inventory:

```bash
node --version
node -p 'JSON.stringify({node: process.version, execPath: process.execPath, brotli: process.versions.brotli})'
```

Node.js documents `process.version`, `process.execPath`, and `process.versions` as runtime metadata. These values help distinguish the executable selected by a shell from another installation used by a service manager. 

For Docker, inspect the application container:

```bash
docker exec YOUR_CONTAINER node --version
```

This starts a new process inside the container. To verify the application process itself, record its runtime version at startup in internal logs:

```javascript
console.info({
  event: 'runtime-start',
  node: process.version,
  execPath: process.execPath,
  arch: process.arch
});
```

Repeat the inventory for production services, queue workers, scheduled jobs, CI runners, preview environments, and local development installations.

**2. Locate likely decoding paths**

A source search provides a starting point:

```bash
rg -n -F -e 'brotliDecompress' -e 'createBrotliDecompress' -e 'fetch(' -e 'express.json(' -e 'express.raw(' -e 'express.text(' -e 'express.urlencoded(' -e 'body-parser' --glob '!node_modules/**' --glob '!dist/**' .
```

Then inspect the installed middleware tree:

```bash
npm ls express body-parser --all
```

These are inventory aids, not vulnerability verdicts. Wrappers, transitive dependencies, generated code, and other HTTP clients can hide relevant behavior. Conversely, a matching function name does not establish attacker-controlled input.

**3. Trace the complete path**

For each candidate, determine:

- Who can supply or influence the compressed bytes?
- Which process performs decompression?
- Does authentication run before or after that operation?
- Can redirects or external content introduce another source?
- Which deployed runtime actually executes the code?

This review produces a more useful remediation order than ranking applications by framework name alone.

## How to Patch Node2Shell Completely

**Update the runtime through the deployment's established mechanism.** Use the patched release for the current supported major version when that minimizes compatibility changes. Unsupported applications need a supported-version migration.

For containers, update the relevant base-image references and rebuild the application image. An illustrative build and inspection sequence is:

```bash
docker build --pull -t app:node2shell-fixed .
docker run --rm --entrypoint node app:node2shell-fixed --version
```

Docker's [`--pull` option](https://docs.docker.com/reference/cli/docker/buildx/build/) attempts to refresh referenced images. It does not change a Dockerfile that explicitly selects an old version or digest. Update those references first. 

For multi-stage builds, inspect both the build stage and the final runtime stage. Updating one does not establish that the other is patched.

**Replace running instances.** Installing a new executable or building an image does not migrate existing processes. Roll out the replacement through the normal deployment mechanism, including background workers and scheduled workloads.

**Verify the completed rollout.** A useful acceptance checklist is:

- Every active replica reports a patched runtime.
- Normal API requests and expected compressed traffic still work.
- Outbound integrations and background jobs complete successfully.
- Old instances have drained and terminated.
- Deployment templates and rollback artifacts no longer restore a vulnerable runtime.

Use ordinary functional tests and runtime verification for deployment acceptance. An unsuccessful exploit attempt is not proof that an application is patched.

## Temporary Mitigations and Their Limits

**Disable request-body inflation where compatible**

For an API that can temporarily reject compressed JSON requests, change its existing parser configuration:

```javascript
app.use(express.json({ inflate: false }));
```

The [body-parser documentation](https://expressjs.com/en/resources/middleware/body-parser/) states that disabling inflation rejects compressed bodies; the associated unsupported-encoding error uses HTTP status **415**.

This also rejects other compressed encodings handled by that parser, so test legitimate clients. Review raw, text, URL-encoded, and route-specific parsers separately. An earlier decompression middleware would remain relevant. 

**Reduce untrusted outbound processing**

Where immediate patching is temporarily blocked, consider suspending features that fetch arbitrary external URLs or restricting them to necessary destinations. These measures reduce potential input sources; they do not correct the decoder.

**Use WAF protections as an additional layer**

[Vercel's security bulletin](https://vercel.com/kb/bulletin/node-js-security-advisory) states that it deployed rules for the inbound attack path. It explicitly warns that these rules do not cover responses fetched by application functions from other servers.

The same architectural limitation matters beyond Vercel: a front-door request filter cannot be assumed to inspect a separate outbound response. **Runtime patching addresses both entry points.** 

## Common Remediation Mistakes

| Mistake | Why it leaves a gap | Better verification |
|---|---|---|
| Updating npm dependencies only | The fix belongs in the runtime's native decoder | Confirm the deployed Node.js release |
| Checking only the host's Node.js version | A container or service may use another executable | Inspect each workload and its startup metadata |
| Rebuilding without replacing instances | Existing processes continue running old code | Verify every active replica after rollout |
| Reviewing only public API routes | Background clients can consume malicious responses | Include outbound fetches and import jobs |
| Treating WAF coverage as completion | Fetched responses can bypass inbound inspection | Complete runtime replacement |
| Trusting one unsuccessful exploit test | Exploit reliability depends on environment | Verify patch provenance and deployed versions |

A clean `npm audit` result is also insufficient. The [npm documentation](https://docs.npmjs.com/cli/v11/commands/npm-audit/) describes an audit of project dependencies. It does not establish that an externally installed Node.js executable contains this native security patch. 

Another subtle mistake is relying exclusively on `process.versions.brotli`. Node.js incorporated the correction as a **cherry-picked patch**, so a dependency's reported upstream version string alone may not establish whether the fix is present. Evaluate the Node.js release or the distributor's documented backport. 

## Frequently Asked Questions

**Is every application running an affected Node.js version remotely exploitable?**

No. The runtime can contain the vulnerable decoder without an application exposing an attacker-controlled path to it. Nevertheless, supported patched releases should be deployed because dependencies and application changes can introduce new decoding paths.

**Does a plain Node.js HTTP server automatically decompress Brotli request bodies?**

No. Receiving a compressed request alone does not invoke Brotli decompression. Middleware or explicit application behavior must perform that operation. Imperva identifies this distinction in its research. 

**Is Express itself the source of Node2Shell?**

The vulnerable component is the Brotli decoder bundled with Node.js. Express middleware can provide a route into it. Upgrading a middleware package while retaining an affected runtime does not apply the native decoder patch.

**Does disabling response compression fix the problem?**

Disabling the generation of compressed responses does not establish that request bodies or fetched responses will stop being decompressed. Review encoding and decoding as separate operations.

**Can other software using Brotli be affected?**

The upstream project also published [Brotli 1.2.1, 1.1.1, and 1.0.10](https://github.com/google/brotli/releases) with fixes for CVE-2026-111497. Software embedding the affected decoder needs its own vendor assessment. The Node.js version table does not determine another product's status. 

**Should crashes be treated as proof of exploitation?**

No. Crashes and decoder errors can justify investigation, but they are not conclusive indicators by themselves. Correlate them with the runtime version, request or fetch activity, process behavior, and other evidence. If compromise is suspected, preserving evidence and investigating access remain necessary even after patching.

## Conclusion

Node2Shell makes runtime patching urgent wherever applications decode untrusted Brotli content. **Deploy Node.js 22.23.4, 24.22.1, or 26.12.1—or a newer supported release containing the fix—and verify the version in every running workload.** 

Start with public request parsers and services that fetch external content. Then complete the rollout across containers, workers, CI, and development environments. Close the remediation task only after deployed processes and reusable deployment artifacts both contain the patched runtime.
