Understanding How Node.js Brotli Decompression Vulnerability CVE-2026-111497 Works: Decompression Happens Even Without Writing It in Your Code

Understanding How Node.js Brotli Decompression Vulnerability CVE-2026-111497 Works: Decompression Happens Even Without Writing It in Your Code

Brotli decoders bundled with Node.js 22/24/26 could misjudge malformed compressed data as awaiting input and write outside an internal buffer. We read the fix commit, trace fetch and body-parser auto-decompression paths, and show how to check impact.
2026.11.02

This page has been translated by machine translation. View original

Hi there! I'm Yuji Nishimura from the Operations Department!

On October 21, 2026, Node.js security releases for 22 / 24 / 26 were published. Among the 10 fixes, CVE-2026-111497 is an out-of-bounds write in the Brotli decoder. Decompressing crafted compressed data can lead to a process crash or memory disclosure. Imperva has also demonstrated remote code execution on the official Node.js 24.22.0 Linux arm64 image. The root cause is in Google's Brotli library, and fixed versions have also been released for the Python Brotli package and the proxy Envoy, which use the same library. On October 27, the US CISA added this vulnerability to the KEV (the catalog of vulnerabilities confirmed to be exploited).

Even if your application code never calls Brotli decompression, a feature that fetch()es an external URL or an Express 5 API that accepts compressed request bodies can be affected. This article reads the upstream fix commit and explains how the decoder misjudges malformed data as "waiting for input" and writes outside its internal buffer. It also covers the paths where decompression happens outside your application code, how to check whether your environment is affected, and the fixed versions and temporary mitigations.

Conclusion first: the mechanism, checks and mitigations

CVE-2026-111497 is an issue where the Brotli decoder misjudges an invalid block switch as "waiting for input", keeps appending to an 8-byte internal buffer, and writes outside the buffer. In the code before the fix, a format error and insufficient input had the same return value (BROTLI_FALSE). In Node.js, the built-in fetch() and the Express 5 / body-parser 2.x parsers decompress by default, so you are affected even if your application code does not use Brotli.

What to check Mitigation
Whether the running Node.js is older than 22.23.4 / 24.22.1 / 26.12.1 (node -v) Update to the fixed version of each line or later (as of November 2, 2026, the latest of each line is 22.23.4 / 24.22.2 / 26.13.0). No fixes are released for end-of-life 20.x and earlier, so move to a supported line
Whether container images, CI runners, or single executables contain an old Node.js Update the base image and rebuild, then confirm after restart that the new runtime is running
Whether you installed the nodejs package from the official Ubuntu (22.04 or later) or Debian repositories This nodejs package uses the system brotli library, so update the brotli package (libbrotli1) to each distribution's fixed version
Whether you have functions running on the AWS Lambda Node.js runtime Check that the runtime version shown in INIT_START in the logs is nodejs:22.mainline.v157 or later for Node.js 22, and nodejs:24.mainline.v114 or later for Node.js 24. For Node.js 24, process.version cannot be used to judge. No fixes are released for the Node.js 18 / 20 runtimes
Whether your Python environment has Brotli older than 1.2.1 or brotlicffi older than 1.2.1.0 (both from 1.0.3 onward) Update to Brotli 1.2.1 / brotlicffi 1.2.1.0 or later
Whether you configured a Brotli decompressor in Envoy Update to v1.40.0 / v1.39.3 / v1.38.6 / v1.37.8 / v1.36.12 or later
Whether you have a feature that fetch()es external URLs or an API that accepts compressed request bodies Features that fetch external URLs cannot be protected by the temporary mitigations in this article (disabling inflate, WAF), so update first. For APIs that do not need compressed request bodies, reject compressed bodies in the body parser until you update

What happened

The Node.js October security releases were published on October 21, fixing 10 vulnerabilities in 22.23.4, 24.22.1 and 26.12.1. As of November 2, 2026, the Node.js vulnerability list shows no security release newer than this. 24.22.2 and 26.13.0 (the "latest" in the table in "Conclusion first"), released after October 21, are both not security releases. 26.13.0 is the release that moves 26.x to LTS, and according to its release notes the only change from 26.12.1 is a metadata update. As for 24.22.2, AWS is moving Lambda to 24.22.2 because 24.22.1 had a known regression (described below).

CVE-2026-111497, fixed in this release, is described as follows.

A flaw in the Brotli decoder bundled with Node.js allows malformed compressed input to write past the end of an internal decoder buffer.

In other words, passing malformed compressed data to the Brotli decoder bundled with Node.js causes a write past the end of an internal buffer. The reporters are Khai Tran and Sylvie Mayer of OpenAI Codex Security, and the fix was made by Matteo Collina and Antoine du Hamel of Node.js. The Node.js advisory states that end-of-life lines are always affected.

The problem is not limited to Node.js. According to GHSA-9v8h-qqgm-4m35 in the GitHub Advisory Database, the cause is the decoder (c/dec/decode.c) in Google's Brotli 1.0.3 to 1.2.0. The fixed versions per line are 1.0.10 / 1.1.1 / 1.2.1. The same advisory also lists the PyPI packages Brotli (1.0.3 up to but not including 1.2.1) and brotlicffi (1.0.3 up to but not including 1.2.1.0).

According to the Envoy advisory, the affected versions are v1.39.2 / v1.38.5 / v1.37.7 / v1.36.11 in configurations with a Brotli decompressor. The fixed versions are in the table in "Conclusion first".

The Wiz summary also lists the JavaScript runtime Bun, the service mesh Istio, and the Go Brotli implementation andybalholm/brotli. According to each project's announcement:

Severity differs by source.

Source Rating
Node.js advisory High
NVD Critical (9.8 in CVSS 3.1)
GitHub Advisory Database (GHSA-9v8h-qqgm-4m35) Critical (9.2 in CVSS 4.0)
Envoy advisory Critical (9.2 in CVSS 4.0)
Ubuntu Priority high (CISA-KEV is given as the reason)

As for exploitation, Imperva's analysis demonstrated code execution on a pre-fix official Node.js 24.22.0 (Linux arm64) image. Imperva also writes that the attack does not succeed every time and that they did not try every build or application. As of November 2, 2026, GitHub also has a repository created on October 22 that claims to publish verification code (I have not checked its contents).

On October 27, CISA added this vulnerability to the KEV (Known Exploited Vulnerabilities Catalog). The KEV is CISA's catalog of vulnerabilities confirmed to have been exploited in the wild. The remediation deadline for US federal agencies is November 10. CISA has not published details of the confirmed attacks. Meanwhile, the Microsoft Security Update Guide says "Exploited: No" as of its October 31 revision. The assessment of exploitation differs by source.

Attack flow

What the attacker mainly needs to prepare is "the contents of the Brotli data that the application decompresses". The CVSS vector in the GitHub Advisory Database (GHSA-9v8h-qqgm-4m35) also says no privileges required (PR:N) and no user interaction (UI:N). On the other hand, it is also rated as having attack requirements (AT:P). Imperva verified two paths: decompressing a received request body, and decompressing a response fetched from outside. The following diagram was drawn by me based on Imperva's analysis.

  1. Precondition: an application running on pre-fix Node.js 22 / 24 / 26 decompresses Brotli data whose contents the attacker can control.
  2. Intrusion/exploitation: in the request body path, the body parser is made to decompress the Brotli data; in the response path, fetch() is.
  3. Impact: a crash, disclosure of process memory, and depending on conditions, code execution with the privileges of the Node.js process.

Paths where decompression happens without being written in your code

You are affected even if you never call zlib.brotliDecompress() yourself.

  • The body-parser README says all parsers automatically decompress gzip, br (Brotli), deflate and zstd. The default value of the inflate option, which decides whether to decompress compressed bodies, is true.
  • Imperva verified paths configured with json() and raw(). The targets are Express 5.0.0 (body-parser 2.0.1), Express 5.3.0 (body-parser 2.3.0), and body-parser 2.0.0 alone. It writes that versions in between were not tested individually.
  • body-parser 2.4.0 (released October 22) was not among Imperva's test targets. 2.4.0 added automatic zstd decompression, but it still automatically decompresses br, and the default value of inflate is unchanged.
  • The README of body-parser 1.x, used by Express 4, says it automatically decompresses gzip and deflate, and does not include br.
  • As in the path Imperva verified, Node.js's built-in fetch() automatically decompresses responses with Content-Encoding: br.
  • On the other hand, according to Imperva, a plain Node.js HTTP server does not decompress a Brotli body just by receiving the request.

Python has the same structure. According to Aikido, urllib3 and requests decompress Brotli responses if Brotli or brotlicffi is installed. httpx is affected if httpx[brotli] is installed, and aiohttp if Brotli is installed.

Aikido gives webhook delivery, URL previews and crawlers as examples of features where the response path applies. Aikido's write-up centered on Python and has since broadened its scope to other languages.

Why it works

The decoder judged a "format error" as "waiting for input"

According to the Node.js advisory, an invalid block switch was treated not as a permanent format error but as a temporary state meaning more input is needed. A block switch is an instruction, in the middle of Brotli compressed data, to switch the code tables used to read the following data.

Looking at the diff of the upstream fix commit, 0ed4268 in google/brotli ("Detect and report invalid stream earlier."), before the fix the function DecodeBlockTypeAndLength(), which reads block switches, returned its result as a boolean. In the following excerpt (pre-fix code of Brotli 1.2.0), the first return BROTLI_FALSE is the value returned when there is only one block type and the switch is invalid as a format. The second is the value returned when there is not yet enough input to read the symbol. Since both are the same value, the caller cannot tell them apart.

c/dec/decode.c
  if (max_block_type <= 1) {
    return BROTLI_FALSE;
  }
  /* omitted */
    if (!SafeReadSymbol(type_tree, br, &block_type)) return BROTLI_FALSE;

When it is not certain that enough input is available, the path with safe true is taken. In this path, the caller's macro BROTLI_SAFE uniformly converted BROTLI_FALSE into "waiting for input" (BROTLI_DECODER_NEEDS_MORE_INPUT). The following excerpt is also pre-fix code of Brotli 1.2.0.

c/dec/decode.c
#define BROTLI_SAFE(METHOD)                       \
  {                                               \
    if (safe) {                                   \
      if (!Safe##METHOD) {                        \
        result = BROTLI_DECODER_NEEDS_MORE_INPUT; \
        goto saveStateAndReturn;                  \
      }                                           \
    } else {                                      \
      METHOD;                                     \
    }                                             \
  }

In the KEV, CISA names this vulnerability "Google Brotli Unchecked Return Value Vulnerability" and lists CWE-252 (unchecked return value) and CWE-787 (out-of-bounds write).

"Waiting for input" never ends, and writes go outside the 8-byte buffer

When decompressing a stream, the decoder accumulates data that spans input boundaries in an internal 8-byte buffer (s->buffer.u8) and reads it. If "waiting for input" occurs while reading with this buffer, it adds 1 byte from the next input to the buffer and re-reads. The following excerpt is also pre-fix code of Brotli 1.2.0.

c/dec/decode.c
          } else if (*available_in != 0) {
            /* Not enough data in buffer, but can take one more byte from
               input stream. */
            result = BROTLI_DECODER_SUCCESS;
            BROTLI_DCHECK(s->buffer_length < 8);
            s->buffer.u8[s->buffer_length] = **next_in;
            s->buffer_length++;

If input is truly just insufficient, adding a few bytes finishes the read and the buffer returns to empty. A format error, on the other hand, is never resolved no matter how many bytes are added. As a result, "waiting for input" never ends, and appending to the buffer continues past 8 bytes.

The only check on the limit is BROTLI_DCHECK. This macro becomes an assert only when BROTLI_DEBUG or BROTLI_ENABLE_LOG is defined (c/common/platform.h). In a normal build it does nothing, so writes from the 9th byte onward overwrite the decoder state located right after the buffer.

The append to the 8-byte internal buffer described in the Node.js advisory can be read as corresponding to this processing. The NVD description also lists corruption of adjacent decoder state memory (corrupt adjacent decoder state memory) as an impact.

The following diagram was drawn by me based on the pre-fix code.

After the fix, processing stops as a format error

The post-fix DecodeBlockTypeAndLength() returns an error code instead of a boolean. The following excerpt is taken from the fix commit's diff.

c/dec/decode.c
  if (max_block_type <= 1) {
    return BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH;
  }
  /* omitted */
    if (!SafeReadSymbol(type_tree, br, &block_type)) {
      return BROTLI_DECODER_NEEDS_MORE_INPUT;
    }

When the switch is invalid as a format, it returns a new error code BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH (-17). When input is insufficient, it returns BROTLI_DECODER_NEEDS_MORE_INPUT. On the caller side, the new macro BROTLI_SAFE_WITH_STATUS uses the received value as-is as the result and stops processing. Imperva also explains that the upstream fix returns a format error for an invalid switch and propagates it to the caller. Node.js exposed this -17 as zlib.constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH.

Brotli is in the runtime, not in npm dependencies

Node.js embeds Brotli in the runtime. Since the runtime is not an npm package, this issue does not appear in the dependencies of package-lock.json. Container images containing an old Node.js and single executables remain affected until the runtime is replaced and rebuilt.

However, the nodejs package installed from the official Ubuntu or Debian repositories uses the system brotli library rather than the bundled Brotli (how to check is described below).

Checking whether your environment is affected

Check whether Node.js is a fixed version

node -v

If it is older than the fixed versions in the "Conclusion first" table at the top, it is affected. However, for the distribution's official nodejs package and AWS Lambda, check using the methods described below. Imperva also recommends checking per host and per image. For containers, check the runtime version inside the image. Put the image name you want to check in <image>.

docker run --rm --entrypoint node <image> -v

The reason for --entrypoint node is so that node -v can be run even for images that define the app's start command with ENTRYPOINT (docker run reference). Trying it with the official image node:22.23.4-alpine printed v22.23.4.

Distinguish by the presence of a constant, not by process.versions.brotli

Judge by the Node.js version itself, and use the presence of zlib.constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH as a clue to whether the fix is included. The brotli value of process.versions does not change before and after the fix, so it cannot be used for judgment.

The value does not change because of how the fix was applied. According to the release notes of 22.23.4 / 24.22.1 / 26.12.1, the fix for CVE-2026-111497 is not a version update of Brotli itself but a cherry-pick of the upstream fix commit (described in the release notes as deps: brotli: cherry-pick 0ed4268a4401abcb44720963). It also includes a change exposing the post-fix error code -17 in zlib.constants.

This point was also asked about in a Node.js issue (nodejs/node#67075). Antoine du Hamel, who handled the fix, explained that because the security release was shipped before Brotli 1.2.1 was out, individual commits were cherry-picked. He then answered that scanners that judge by version strings should instead check for the presence of zlib.constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH.

Locally, I also compared the pre-fix 24.22.0 (the version Imperva tested) with the fixed 24.22.1. In a container on Ubuntu 24.04 (arm64), using the official Linux arm64 binaries downloaded from nodejs.org, I ran a script that only prints the following values.

check.js
const zlib = require("node:zlib");
console.log(
  process.version,
  "brotli=" + process.versions.brotli,
  "BLOCK_SWITCH=" + zlib.constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH,
);

Result of printing process.versions.brotli and zlib.constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH on pre-fix Node.js 24.22.0 and fixed 24.22.1. Both show brotli=1.2.0, and the constant is undefined on 24.22.0 and -17 on 24.22.1

Both showed brotli=1.2.0, and the only difference was the presence of the constant. With 22.23.1 and 22.23.4 installed via mise (macOS arm64), the same script gave the following results.

v22.23.1 brotli=1.1.0 BLOCK_SWITCH=undefined
v22.23.4 brotli=1.1.0 BLOCK_SWITCH=-17

I tried 26.x and 24.22.2 with the official macOS arm64 binaries downloaded from nodejs.org. Only the pre-fix 26.12.0 lacked the constant; the fixed 26.12.1, and the later 26.13.0 / 24.22.2, had it.

v26.12.0 brotli=1.2.0 BLOCK_SWITCH=undefined
v26.12.1 brotli=1.2.0 BLOCK_SWITCH=-17
v26.13.0 brotli=1.2.0 BLOCK_SWITCH=-17
v24.22.2 brotli=1.2.0 BLOCK_SWITCH=-17

Comparing the process.versions.brotli value against the GHSA affected range (1.0.3–1.2.0), both 1.1.0 on 22.x and 1.2.0 on 24.x / 26.x fall within the range. Therefore, judging by the version string would misjudge even the fixed 22.23.4 and 24.22.1 as affected.

What I checked is limited to the official binaries from nodejs.org (Linux arm64 and macOS arm64), 22.x installed via mise (macOS arm64), and the node:22.23.4-alpine and node:24.22.2-slim (arm64) images. The constant was -17 in both images. I have not verified whether the same constant was added in Windows or x64 builds, so if you use the constant as a clue in those builds, it seems you need to compare the values before and after the fix in your own environment. This way of telling cannot be used with the distribution's official nodejs package (next section).

For Ubuntu and Debian nodejs packages, check the brotli package

The nodejs package installed from the official Ubuntu or Debian repositories uses the system brotli library rather than the bundled Brotli. On the Ubuntu CVE page, the nodejs package for jammy (22.04) and later is marked not affected as "uses system brotli", with the fix made on the brotli package side. The nodejs of focal (20.04) uses the bundled Brotli, and as of November 2, 2026 the fix remains "needed". The Debian security tracker also says "nodejs and python-brotlicffi use system provided brotli library". The official node Docker images and Node.js installed from non-distribution repositories are not covered by these trackers.

Which case applies can be told by whether the node binary links the system libbrotlidec. If it does, compare the version of the brotli package (libbrotli1) with the fixed version on each distribution's page.

ldd "$(command -v node)" | grep -i brotli
dpkg-query -W libbrotli1

I installed the official repository's nodejs in an Ubuntu 24.04 (arm64) container and ran these two commands and check.js; the result was as follows.

$ ldd "$(command -v node)" | grep -i brotli
	libbrotlidec.so.1 => /lib/aarch64-linux-gnu/libbrotlidec.so.1 (0x0000e1a5cc930000)
	libbrotlienc.so.1 => /lib/aarch64-linux-gnu/libbrotlienc.so.1 (0x0000e1a5cc880000)
	libbrotlicommon.so.1 => /lib/aarch64-linux-gnu/libbrotlicommon.so.1 (0x0000e1a5cb940000)
$ dpkg-query -W libbrotli1
libbrotli1:arm64	1.1.0-2ubuntu0.1
$ node check.js
v18.19.1 brotli=1.1.0 BLOCK_SWITCH=undefined

1.1.0-2ubuntu0.1 of libbrotli1 is the version given as the noble (24.04) fix on the Ubuntu CVE page. Yet the constant was undefined. This nodejs (v18.19.1) does not include the change exposing the constant, so even with a fixed libbrotli the constant does not appear. The constant is something the Node.js source (src/node_zlib.cc) exposes from the definitions in the brotli headers, and its value is determined when Node.js is built. In builds that use the system brotli, the presence of the constant does not tell you whether the library loaded at runtime is fixed.

For comparison, running the same ldd on the official node:24.22.2-slim image showed no libbrotli. The Node.js in this image bundles Brotli in the binary, and the constant was -17.

For AWS Lambda, check the runtime version

According to the AWS security bulletin (2026-155-AWS), the Lambda Node.js and .NET runtimes are affected (.NET is not covered in this article). For Node.js 22 / 24 / 26 ZIP functions, Phases 1 / 2 of the security fix are complete, and base images for container images have also been updated. No fix is released for end-of-life runtimes such as Node.js 18 / 20.

For the Node.js 24 runtime, because 24.22.1 had a known regression, AWS initially applied only the fix to the previous stable version. Therefore, even when fixed, process.version does not change. AWS has since been moving to 24.22.2, where the regression is fixed. As of the October 30 update, Phase 2 of this migration (the stage where Lambda updates the runtime automatically) was scheduled to finish on October 31, and as of November 2 I could not find an addendum saying it was complete.

Whether it is fixed is judged by the runtime version shown in the INIT_START line of the function's logs. This line is output at each cold start of the execution environment. The thresholds AWS gives are nodejs:22.mainline.v157 or later for Node.js 22 and nodejs:24.mainline.v114 or later for Node.js 24. No threshold is given for Node.js 26.

Check the Python Brotli version

Aikido gives the following commands. pipdeptree is a tool that displays dependencies as a tree (it needs to be installed separately), and with -r -p Brotli you can trace backward which packages depend on Brotli.

pip show Brotli brotlicffi
pipdeptree -r -p Brotli

I have not tried pipdeptree locally (I tried pip show in the container example below).

For containers, run pip show inside the image. Aikido gives docker run --rm <image> pip show Brotli brotlicffi, but for images that define the app's start command with ENTRYPOINT, pip will not be run. As with Node.js, specify pip with --entrypoint.

docker run --rm --entrypoint pip <image> show Brotli brotlicffi

I tried it with a test image that has Brotli 1.2.1 installed on python:3.13-slim and starts the app with ENTRYPOINT. Without --entrypoint, the app just started and printed app started. With it, the following was displayed (line 4 onward omitted).

WARNING: Package(s) not found: brotlicffi
Name: brotli
Version: 1.2.1

If it is within the range listed in the "Conclusion first" table, it is affected.

According to AWS, the Lambda Python runtime itself does not include Brotli. If you include Brotli in a function's deployment package, redeploy with the fixed version.

Paths to update first

Update services that match either of the following first. The temporary mitigations in this article protect only the received request body path, so make the first one the top priority.

  1. Features that send requests to external servers and decompress the responses (webhooks, URL previews, crawlers, fetching external APIs, etc.)
  2. APIs that accept compressed request bodies (including cases where the Express 5 / body-parser 2.x parsers are used with default settings)

How to protect yourself

Update to a fixed version

  • Update Node.js to the fixed version of each line or later. For 20.x and earlier, move to a supported line.
  • Rebuild artifacts that bundle a Node.js binary, such as base images, CI runners and single executables. Confirm the replaced binary with node -v, then restart the process.
  • If you use the distribution's official nodejs package, update the brotli package (libbrotli1) and restart the Node.js process.
  • For AWS Lambda, apply it as described in the next section, "Applying the fix to AWS Lambda".
  • For Python, update Brotli / brotlicffi and rebuild the lockfile and image.
  • Update Envoy to the fixed version in the "Conclusion first" table or later, and Bun, Istio and Go's andybalholm/brotli to the fixed versions listed in "What happened" or later.

For managed runtime environments, also check what the platform has done. For example, according to the Vercel announcement (dated October 29), from 11:00 a.m. Pacific Time on October 28, all new Vercel deployments use the fixed Node.js. It also says projects on Node.js 20.x must be changed to 22.x or 24.x and redeployed.

Vercel has also taken measures on the platform side until the rollout of fixed versions finishes. For new deployments using pre-fix Node.js, Brotli responses are not decompressed in outbound requests from fetch() and node:http / node:https. For existing projects, this is applied when you redeploy. The announcement did not say under what conditions pre-fix Node.js would be used for a new deployment. The temporary mitigations introduced in the "Temporary mitigations until you update" section are separate ones that you set up yourself.

Applying the fix to AWS Lambda

For container image functions, rebuild from the updated base image and redeploy. How the fix is applied to ZIP functions depends on the runtime update mode (Lambda documentation).

  • Auto (default): Lambda updates automatically (Phase 2). Even earlier than that, it is applied when you update the function's code or configuration (Phase 1).
  • Function update: applied only when you update the function's code or configuration.
  • Manual: keeps using the specified runtime version, so it is not updated. AWS positions Manual as a temporary mode for rolling back when a runtime update causes problems, and recommends returning to Auto or Function update as soon as possible (shared responsibility model page).

For Function update functions, and functions you want to update without waiting for Phase 2, you need to actually change the function. AWS writes "A zero-change update is not sufficient." and provides a script that adds the environment variable TRIGGER_RUNTIME_UPDATE to update the configuration. For functions managed with CDK or similar, change the setting on the IaC side and deploy, because adding an environment variable via the CLI creates a drift between the code definition and the actual configuration.

Also, after the Node.js 24 runtime moved to 24.22.2, a problem has been reported for functions using @h4ad/serverless-adapter (H4ad/serverless-adapter#423). Streaming responses never finish and time out. According to the issue, 24.22.2 is runtime version nodejs:24.mainline.v115, and v114 is Node.js 24.21.0. The cause is said to be a change to Node.js's http introduced in 24.22.0, and it was fixed on the adapter side in v4.4.1. After the runtime changes, also check that streaming responses are returned to the end.

Temporary mitigations until you update

For paths that do not need to accept compressed request bodies, setting body-parser's inflate to false lets you reject compressed bodies (body-parser README). inflate is a per-parser option, so if you also use raw(), text() and urlencoded(), specify it on each.

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

All requests whose Content-Encoding is anything other than identity (no compression) are rejected, and not just br: gzip, deflate and zstd also get 415 (encoding.unsupported) (the Errors section of the README). Before applying this, check in your WAF or application logs whether any requests carry a Content-Encoding other than identity.

Another option is to reject requests with Content-Encoding: br at the edge or in a WAF. The AWS security bulletin includes a sample AWS WAF rule, Node2Shell_BROTLI, with a procedure of first running it in Count (only records matching requests without blocking) and then switching to Block. AWS also writes that this rule also blocks legitimate uploads compressed with Brotli, and that it is an example that does not necessarily match all Brotli requests. On October 25, Cloudflare added "Brotli - Request Content-Encoding - CVE:CVE-2026-111497" to the Cloudflare Managed Ruleset. The action at the time of addition was Log, which on its own does not block requests. Vercel and Imperva have also added WAF rules.

However, both disabling inflate and rejecting in a WAF protect only the received request body path. AWS also notes "AWS WAF inspects inbound requests only." Vercel's announcement also says the WAF covers only traffic passing through Vercel, not responses that functions fetch from outside. The primary sources I checked as of November 2, 2026 did not present any temporary mitigation you can set in your own app to protect the fetch() response path (measures that stop decompression on the platform side, like Vercel's, are as described in the "Update to a fixed version" section). Protect this path by updating Node.js.

Clues for detection

Investigate process crashes and decoder errors that occur during decompression with priority. Imperva says neither is a reliable indicator of exploitation on its own, and writes that they should be cross-checked against the runtime version, application logs and the source of the compressed data.

Summary

  • CVE-2026-111497 is an issue in the Brotli decoder bundled with Node.js. Because an invalid block switch and insufficient input have the same return value, a format error is misjudged as "waiting for input", and writes outside the 8-byte internal buffer continue. After the fix, it returns a dedicated error code (-17) and stops processing. It is fixed in Node.js 22.23.4 / 24.22.1 / 26.12.1, Python Brotli 1.2.1 / brotlicffi 1.2.1.0, and each fixed version of Envoy.
  • On October 27 it was added to CISA's KEV. GitHub also has a repository that claims to publish verification code.
  • Decompression happens outside your application code. fetch() decompresses responses and the Express 5 / body-parser 2.x parsers (express.json(), etc.) decompress request bodies by default, so you are affected even if you don't think you use Brotli.
  • For Node.js, run node -v on each host and image. process.versions.brotli cannot be used because it does not change before and after the fix, and the clue that the fix is included is the presence of zlib.constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH. Check the distribution's official nodejs package via the brotli package, Lambda via the INIT_START runtime version, and Python via pip show.
  • The temporary mitigations in this article protect only the request body path, so update services that fetch external URLs first.

I hope this helps someone.


References:

Share this article