Neutron, our AI engine, scored 97.1% on UC Berkeley's CyberGym benchmark. Learn more

Deep Dive

Deep Dive

Node2Shell: Brotli RCE in Node.js, Bun and Python (CVE-2026-111497)

A decoder overflow rated CVSS v4.0 9.2, CVE-2026-111497 affects Node.js, Bun, .NET, the Python Brotli packages, Envoy and Istio; fixes shipped October 21 to 25, 2026.

TL;DR

  • CVE-2026-111497 is an out-of-bounds write in the C Brotli decoder, the code that unpacks Brotli-compressed (Content-Encoding: br) data across the web. A crafted stream corrupts process memory, causing a crash, memory disclosure, or code execution. The CVE record rates it CVSS v4.0 9.2 (CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N); Node.js rates it High and Red Hat rates it 8.1.
  • The bug lives in Google Brotli 1.0.3 through 1.2.0 and travels into everything that bundles that code: Node.js 22.x, 24.x and 26.x, Bun 1.4.3 and earlier, the PyPI Brotli and brotlicffi packages, Envoy and Istio when Brotli decompression is enabled, cdxgen standalone builds, and the .NET runtime, fixed October 25 across .NET 8 through the .NET 11 release candidates.
  • Exposure requires decoding Brotli data someone else controls: a fetch() response, an Express 5 or body-parser 2 request body, a proxy decompression filter, or Python HTTP clients, which decode Brotli automatically once the package is installed.
  • No exploitation in the wild is known: CISA's October 23 assessment recorded exploitation as "none". Imperva's Red Team demonstrated code execution on an unmodified Node.js 24.22.0 ARM64 build and has not released the exploit; the public proof of concept that appeared on October 22 reaches code execution only on a deliberately weakened build, producing a crash and a limited memory read on standard builds, and Red Hat says there is no reliable public proof of concept for running code.
  • Update every copy of the decoder now, then restart services and rebuild container images: a running process keeps the old decoder until it restarts.

Am I affected?

You are affected if both of these hold:

  • Software you run bundles or links the Brotli decoder in an affected version, and it decodes Brotli data from parties you do not fully control. A plain Node.js HTTP server does not decode request bodies on its own, so exposure comes through the paths listed below.
  • For Python specifically, the risk comes from the Brotli or brotlicffi package being installed at all. Once either is present, HTTP clients such as requests, urllib3, httpx and aiohttp decode Brotli responses on their own, so an application can be exposed without ever importing Brotli in its own code.
Software Affected versions Fixed version
Google Brotli (C library) 1.0.3 to 1.2.0 1.0.10, 1.1.1 or 1.2.1, per branch (October 21, 2026)
Node.js 26.x before 26.12.1 26.12.1 (October 21, 2026)
Node.js 24.x before 24.22.1 24.22.1 (October 21, 2026)
Node.js 22.x before 22.23.4 22.23.4 (October 21, 2026)
Node.js 20.x and older all (end of life) none: move to a supported line
Bun 1.4.3 and earlier 1.4.4 (October 22, 2026)
.NET 8 8.0.0 to 8.0.32 8.0.33 (October 25, 2026)
.NET 9 9.0.0 to 9.0.21 9.0.22 (October 25, 2026)
.NET 10 10.0.0 to 10.0.13 10.0.14 (October 25, 2026)
.NET 11 previews and RCs 11.0.0-preview.1 to 11.0.0-rc.3.26522.147 11.0.0-rc.3.26522.148
PyPI Brotli before 1.2.1 1.2.1
PyPI brotlicffi before 1.2.1.0 1.2.1.0
Go, andybalholm/brotli before 1.2.7 1.2.7 (October 23, 2026)
Envoy 1.36.11, 1.37.7, 1.38.5, 1.39.2 1.36.12, 1.37.8, 1.38.6, 1.39.3, 1.40.0
Istio 1.29.0 to 1.29.8, 1.30.0 to 1.30.5, 1.31.0 to 1.31.1 1.29.9, 1.30.6, 1.31.2
cdxgen standalone binaries and images 13.4.0 and earlier, 12.8.8 and earlier 13.5.0, 12.8.9

The .NET advisory, GHSA-p3jp-9v83-76w9 (High, CVSS v3.1 8.1), covers 225 NuGet packages across the four release lines and carries the same precondition as the proxies: ASP.NET Core and HttpClient do not automatically decompress Brotli data in their default configurations, so a .NET application is exposed only when it enables Brotli decompression or explicitly uses BrotliStream, BrotliDecoder or the automatic-decompression options on attacker-controlled data. .NET Framework is not affected.

The Go port of the decoder, andybalholm/brotli, carried the same conflated return in its decodeBlockTypeAndLength, and Andy Balholm fixed it on October 23, 2026 with commit 33c2a988, a direct port of the Google fix 0ed4268, released as v1.2.7 with no advisory and no CVE. The scope is narrower than the C decoder's: Go's bounds-checked slice accesses turn the out-of-range block-type index into a panic rather than a silent out-of-bounds write, so the failure mode in Go is a crash on attacker-supplied input, not the memory corruption the C library allows. Go services that decompress attacker-supplied Brotli should still move to 1.2.7 or later.

Beyond the table, projects that compile their own copy of the decoder inherit the bug until they update it, and a community tracker follows them. Fixed by October 24: the Ruby brotli gem 0.9.1, the PHP brotli extension 0.21.1 and UEFITool A76, all bundling Brotli 1.2.1, plus MuPDF 1.28.x, whose Brotli fork picked the fix up in April. Still without a fixed release as of October 26: ClickHouse, NanaZip, GraalJS (graal-nodejs), YDB and libjxl, which have the fix merged on their main branches but whose latest releases still bundle an affected decoder.

Envoy and Istio are affected when Brotli decompression is explicitly enabled, for example through an Envoy decompressor filter. Where it is off, the flaw cannot be reached.

Check what you run, on hosts, in containers and in CI images:

node --version && node -p "process.versions.brotli"   # Node.js: runtime and bundled decoder
bun --version                                          # Bun 1.4.4+ is fixed
python -m pip show Brotli brotlicffi 2>/dev/null | grep -E '^(Name|Version)'
envoy --version                                        # only meaningful with decompression enabled
istioctl version
curl -V | grep -o 'brotli/[0-9.]*'                     # brotli library linked into curl, e.g. brotli/1.1.0
go list -m all | grep brotli                           # Go: andybalholm/brotli 1.2.7+ is fixed

For Node.js, the lock file does not answer the question: applications decode Brotli through the built-in node:zlib module, which uses the decoder bundled in the runtime, so the Node.js version above is the check. The npm brotli package is a separate JavaScript port used for WOFF2 fonts, not the C decoder, and its presence or absence in package-lock.json says nothing about exposure. For Python, pinned requirements do matter:

grep -iE '^(brotli|brotlicffi)' requirements.txt

The PyPI Brotli package recorded about 82.8 million downloads a month, so this dependency is widespread in application and CI stacks.

How do I fix it?

Update the decoder everywhere it lives, then restart the process and rebuild any container image: the running process keeps its old decoder in memory until it restarts.

  • Node.js: update to 26.12.1, 24.22.1 or 22.23.4 from the October 21, 2026 security releases. Node.js 20.x is end of life and will not receive the fix; move those deployments to a supported line.
  • Bun: update to 1.4.4, released October 22, 2026.
  • Python: pip install --upgrade Brotli brotlicffi to reach 1.2.1 and 1.2.1.0, and rebuild images that pin either package.
  • Go: update andybalholm/brotli to 1.2.7 or later in any service that decompresses attacker-supplied Brotli.
  • .NET: update to 8.0.33, 9.0.22, 10.0.14 or 11.0.0-rc.3.26522.148. Exposure needs Brotli decompression explicitly enabled or the Brotli APIs used on attacker-controlled data; .NET Framework is not affected. Self-contained and NativeAOT deployments carry their own copy of the runtime, so updating the machine's shared runtime does not fix them: rebuild and redeploy self-contained applications with the updated runtime, and republish NativeAOT applications with the patched SDK. Per the community tracker, the public GitHub branches had not yet caught up with the internal fix, so source builds from GitHub remained affected.
  • Envoy and Istio: update to the fixed releases in the table if Brotli decompression is enabled in your configuration.
  • Google Brotli, if you build against it directly: 1.0.10, 1.1.1 or 1.2.1 per your branch.
  • curl and other tools that link the system library: update the operating system package. Debian fixed brotli 1.1.0 for Debian 13 (trixie) on October 23, 2026 as 1.1.0-2+deb13u1; Amazon Linux 2023 still listed its brotli and Node.js 24 packages as pending a fix as of October 22, 2026, so check your distribution's status.

If you cannot update yet, stop decoding untrusted Brotli. The recommendations from OpenAI's disclosure page and from Aikido:

  • Reject incoming requests carrying Content-Encoding: br before any proxy or parser decodes the body.
  • Turn off automatic Brotli decoding in HTTP clients, and reject Brotli responses before they are decoded.
  • In Python, uninstall the Brotli and brotlicffi packages so clients fall back to gzip and deflate.

State the limits plainly. Dropping br from the Accept-Encoding header does not help on its own, because a server can send Brotli whatever the client asked for. For Node.js itself, no general workaround has been published, so updating the runtime is the only fix. One inbound path can be closed in the meantime: Express 5 and body-parser 2 parsers set with inflate: false reject any compressed request body with a 415 error before decompressing it, but this does nothing for fetch() responses or other code that decodes Brotli. A web application firewall helps only partially: Vercel, which shipped firewall rules on disclosure day, states that the rules do not cover "responses your functions fetch from other servers", a path only the runtime update closes.

What is the root cause?

The decoder reported a permanent format error as a recoverable "need more input" condition, and the fast decoding path discarded even that, so decoding continued past a point where it should have stopped and wrote outside an internal buffer. The flaw is in DecodeBlockTypeAndLength in c/dec/decode.c, and the fix, commit 0ed4268 by Evgenii Kliuchnikov on February 27, 2026, makes the mechanics visible.

Before the fix, the function returned BROTLI_BOOL, the library's boolean. BROTLI_FALSE meant two very different things: a malformed stream, and an incomplete read that more input could fix:

 static BROTLI_INLINE BROTLI_BOOL DecodeBlockTypeAndLength(
     int safe, BrotliDecoderState* s, int tree_type) {
   ...
   if (max_block_type <= 1) {
-    return BROTLI_FALSE;

max_block_type <= 1 means the stream declares a single block type, so a block-switch command at this point is invalid input, not a paused read. The caller could not tell the two cases apart. In the streaming path, Node.js describes the consequence: "an invalid block switch was reported as a temporary need for more input instead of a permanent format error, so the streaming decoder kept appending input bytes to an eight-byte internal buffer."

In the fast path, the result was discarded altogether. The unsafe variant of the literal block switch was declared void, so the failure never reached the caller:

-static void BROTLI_NOINLINE DecodeLiteralBlockSwitch(BrotliDecoderState* s) {
-  DecodeLiteralBlockSwitchInternal(0, s);

The BROTLI_SAFE macro, used at the three block-switch call sites, checks the result in the speculative safe mode and ignores it in unsafe mode. The fix adds the comment that says so outright: "in case of 'unsafe' execution, when input is guaranteed to be sufficient, result is ignored." That guarantee held for well-formed streams, not for one carrying an invalid block switch, so the fast path kept decoding with stale block-type ring-buffer and Huffman-tree state, and subsequent writes landed past the end of their buffer. The GitHub advisory for the flaw tags CWE-252, an unchecked return value, and the overflow itself is CWE-787, an out-of-bounds write.

How is it exploited?

The attack needs one delivery route where the target decompresses Brotli the attacker controls, then one crafted stream. Numbered:

  1. The attacker builds a Brotli stream that reaches DecodeBlockTypeAndLength with a block-switch command while the stream declares only one block type for that tree.
  2. The stream is delivered as a Content-Encoding: br response to a fetch() call, as a Brotli-compressed request body parsed by Express 5 or body-parser 2, through an Envoy or Istio decompression filter, or as a response decoded automatically by a Python HTTP client with Brotli installed.
  3. The invalid block switch returns BROTLI_FALSE. The streaming path reads it as "feed me more bytes"; the fast path drops it. In both, decoding continues instead of rejecting the stream.
  4. Decoding with stale block-type state writes attacker-influenced data past the end of an internal decoder buffer, corrupting adjacent process memory.
  5. The corruption crashes the process, discloses memory, or redirects execution. Imperva's Red Team, calling its work Node2Shell, ran attacker-supplied code on an unmodified Node.js 24.22.0 build for Linux on ARM64, delivering the payload both as a fetched response and as an Express request body. The exploit took several tries, has not been released, and was not tested on other builds.

OpenAI's Codex Security team, which reported the flaw, describes the exposure as working in both directions: "an unpatched server that accepts compressed input could be taken over by a malicious client, and an unpatched client that accepts compressed input (such as 'curl --compressed') could be taken over by a malicious server."

How does the attack flow?

The flow is short: deliver a crafted stream through any Brotli decode path, let the conflated error keep the decoder alive past an invalid block switch, and the writes that follow land outside the buffer.

flowchart LR A["Crafted Brotli stream with an
invalid block-switch command"] --> B["Delivered via fetch response,
request body or proxy filter"] B --> C["Decoder hits the invalid switch:
format error, not missing input"] C --> D["TRUE or FALSE boolean conflates
the two: error reporting fails"] D --> E["Fast path discards the result
and keeps decoding"] E --> F["Stale block-type state drives
writes past the buffer end"] F --> G["Crash, memory disclosure
or code execution"]

What can an attacker do?

  • Crash the decoding process. A denial of service is the most reliable outcome, since heap corruption frequently crashes before anything else happens.
  • Disclose process memory. Adjacent decoder state and heap contents can leak through the corrupted decode results.
  • Execute code. Imperva demonstrated this on one Node.js build; reliability depends on the process's heap layout, which is why the demonstration took several attempts and why no reliable public exploit exists, per Red Hat.
  • Compromise clients as well as servers. An unpatched client that decodes Brotli from a malicious or compromised server is exposed the same way, a path most server-side hardening does not touch.

Conditions the attacker needs: a decode path for attacker-controlled Brotli, as listed above, and nothing else. No authentication beyond the carrying HTTP exchange, and no user interaction for server-side decoding.

Why does the fix work?

Commit 0ed4268, "Detect and report invalid stream earlier", rebuilds the error handling so the two states stop collapsing into one boolean:

  • DecodeBlockTypeAndLength now returns BrotliDecoderErrorCode, a tri-state: success, a hard error, or needs-more-input.
  • The invalid block-switch case returns a new permanent error, BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH, error code -17, which the header previously left as "reserved".
  • The incomplete-read cases return BROTLI_DECODER_NEEDS_MORE_INPUT distinctly, so the streaming loop keeps its legitimate resume behavior.
  • A new BROTLI_SAFE_WITH_STATUS macro checks the status in both safe and unsafe paths and jumps to saveStateAndReturn on failure, so the fast path can no longer ignore a block-switch error:
+#define BROTLI_SAFE_WITH_STATUS(METHOD)         \
+  {                                             \
+    BrotliDecoderErrorCode status;              \
+    if (safe) {                                 \
+      status = Safe##METHOD;                    \
+    } else {                                    \
+      status = METHOD;                          \
+    }                                           \
+    if (status != BROTLI_DECODER_SUCCESS) {     \
+      result = status;                          \
+      goto saveStateAndReturn;                  \
+    }                                           \
+  }

The three block-switch call sites now use the status-checking macro, and the patch adds a debug assertion on the meta-block length invariant. The fix is complete for this flaw: an invalid block switch is now a permanent, reported error on every path.

The deployment lesson matters as much as the code. The fix landed in google/brotli's source on February 27, 2026, and no Brotli release carried it until October 21, 2026. OpenAI's account: the root cause "was actually fixed in the Brotli source code in February, but it wasn't known at the time that it gave rise to a security vulnerability and there was no more recent release of Brotli." Every runtime and package then had to ship its own copy of the fix in October, which is why the fixed versions above span three days and eight ecosystems.

How do I check if I was compromised?

No exploitation in the wild has been reported, and no indicators of compromise exist for this flaw. CISA's October 23, 2026 assessment recorded exploitation as "none" and automation as "no".

What to look for in general terms until that changes: clusters of crashes in Node.js, Bun, Envoy or Istio processes that handle Brotli-encoded traffic, memory-content anomalies in crash dumps from those processes, and, if code execution did occur on a host, the usual process-level artifacts of unexpected child processes and outbound connections. Any such finding on a Brotli-decoding path is worth treating as related until proven otherwise, and the response is the standard one for suspected code execution: isolate, snapshot, rotate credentials reachable from the process, and rebuild from a clean image.

Timeline

  • 2026-02-27: commit 0ed4268 lands in google/brotli with the fix; no release follows, and the security impact is not yet recognized.
  • 2026-10-21: Node.js ships 26.12.1, 24.22.1 and 22.23.4, and google/brotli publishes 1.0.10, 1.1.1 and 1.2.1.
  • 2026-10-22: CVE-2026-111497 published with the CVSS v4.0 9.2 rating; Bun 1.4.4 released; GitHub advisory GHSA-9v8h-qqgm-4m35 published for the Python packages.
  • 2026-10-23: Imperva's Node2Shell demonstration reported; CISA records no exploitation; the GitHub advisory is updated to map pip brotli below 1.2.1 and brotlicffi below 1.2.1.0; Debian fixes trixie's brotli package; PYSEC-2026-4322 and PYSEC-2026-4323 are added; Andy Balholm ports the fix to the Go decoder as commit 33c2a988 and releases it as andybalholm/brotli v1.2.7.
  • 2026-10-24: cross-ecosystem affected matrices published.
  • 2026-10-25: Microsoft publishes the .NET advisory GHSA-p3jp-9v83-76w9, fixing 225 NuGet packages across .NET 8, 9, 10 and the 11 release candidates; the community tracker records the Ruby brotli gem, PHP brotli extension and UEFITool fixes of October 24.
  • 2026-10-25: this post.

FAQ

Is a plain Node.js HTTP server exposed?

A plain Node.js server does not decode request bodies on its own, so it is outside the demonstrated paths. Exposure comes when the server parses Brotli request bodies through Express 5 or body-parser 2, when it calls fetch() and decodes a Content-Encoding: br response, or when its own code passes untrusted data to zlib.brotliDecompress(), zlib.createBrotliDecompress() or the other node:zlib Brotli APIs, for example in a WebSocket handler, an RPC stream or a file-processing pipeline. If none of these happens in your code, the runtime is still worth updating on schedule.

Does dropping br from Accept-Encoding protect my clients?

No. A server can send Brotli whatever the client asked for, and Python clients decode Brotli automatically once the package is installed. Turn off automatic Brotli decoding in the client configuration, reject Brotli responses before they are decoded, or uninstall the Python packages so clients fall back to gzip and deflate.

Why did the fix take from February to October to reach users?

The change landed in google/brotli's source on February 27, 2026, but the project published no release after that date until the October security response, and the security impact of the change was not known at the time. Runtimes that vendor the decoder, including Node.js and Bun, could not pick the fix up until a release existed to vendor.

Will my dependency scanner flag the Python packages?

The GitHub advisory now maps pip brotli below 1.2.1 and brotlicffi below 1.2.1.0, updated on October 23, 2026, and the PYSEC-2026-4322 and PYSEC-2026-4323 entries exist, so database-syncing tooling can flag them. Stale or air-gapped tooling may lag behind those updates, so checking the installed versions manually, as above, is the reliable test this week.

Are Envoy and Istio exposed by default?

No. Both are affected when Brotli decompression is explicitly enabled, for example through an Envoy decompressor filter. If your configuration never decompresses Brotli, the flaw is unreachable; if it does, the fixed versions in the table apply.

What about curl and other system tools?

curl and many other tools link the operating system's Brotli library rather than bundling it. curl -V shows the linked version, for example brotli/1.1.0. Update through the distribution: Debian shipped trixie's fixed package on October 23, 2026, and other distributions are shipping on their own schedules.

Find the old decoder before an attacker does

Scan your repositories, container images and internet-facing services with Ostorlab to surface every Node.js, Bun and Python deployment still bundling a Brotli decoder from before the October 21, 2026 fixes.

References

  • CVE-2026-111497 record with the CVSS v4.0 9.2 vector and the 1.0.3 to 1.2.0 affected range: https://cveawg.mitre.org/api/cve/CVE-2026-111497 (checked October 25, 2026)
  • Node.js October 21, 2026 security releases, with the per-flaw description, affected lines, fixed versions and credits (reporters Khai Tran and Sylvie Mayer of OpenAI Codex Security; fixers Matteo Collina and Antoine du Hamel): https://nodejs.org/en/blog/vulnerability/october-2026-security-releases (checked October 25, 2026)
  • Fix commit 0ed4268, "Detect and report invalid stream earlier", Evgenii Kliuchnikov, February 27, 2026: https://github.com/google/brotli/commit/0ed4268 (checked October 25, 2026)
  • google/brotli releases 1.0.10, 1.1.1 and 1.2.1, published October 21, 2026: https://github.com/google/brotli/releases (checked October 25, 2026)
  • GHSA-9v8h-qqgm-4m35, reviewed advisory mapping pip brotli and brotlicffi, with CWE-252: https://github.com/advisories/GHSA-9v8h-qqgm-4m35 (checked October 25, 2026; PyPI mapping added by the October 23, 2026 update)
  • Bun 1.4.4 release, October 22, 2026: https://github.com/oven-sh/bun/releases/tag/bun-v1.4.4 (checked October 25, 2026)
  • andybalholm/brotli fix commit 33c2a988, "Detect and report invalid stream earlier", a port of the Google fix, October 23, 2026, released as v1.2.7: https://github.com/andybalholm/brotli/commit/33c2a988 and https://github.com/andybalholm/brotli/releases/tag/v1.2.7 (checked October 26, 2026)
  • Microsoft's .NET advisory GHSA-p3jp-9v83-76w9, CVE-2026-111497, 225 NuGet packages fixed in 8.0.33, 9.0.22, 10.0.14 and 11.0.0-rc.3.26522.148, with the automatic-decompression precondition and the .NET-Framework-not-affected statement: https://github.com/dotnet/runtime/security-advisories/GHSA-p3jp-9v83-76w9 and https://github.com/dotnet/announcements/issues/494 (checked October 26, 2026)
  • The Broccoli2Shell community tracker for bundled copies, the October 22 public proof-of-concept status (crash and limited memory read on standard builds, code execution only on a deliberately weakened build) and per-project fix state: https://broccoli2shell.com/ (checked October 26, 2026)
  • Envoy advisory GHSA-3wvj-j6qm-62mr: https://github.com/envoyproxy/envoy/security/advisories/GHSA-3wvj-j6qm-62mr (checked October 25, 2026)
  • Istio security advisory 2026-007: https://istio.io/latest/news/security/istio-security-2026-007/ (checked October 25, 2026)
  • cdxgen advisory GHSA-5266-jm88-3grm: https://github.com/cdxgen/cdxgen/security/advisories/GHSA-5266-jm88-3grm (checked October 25, 2026)
  • The Hacker News, "Brotli Decoder Flaw Behind Node.js RCE Also Affects Bun, Python and Other Widely Used Software", October 24, 2026, with the affected matrix, the OpenAI bidirectional quote, the CISA and Red Hat assessments and the workaround recommendations: https://thehackernews.com/2026/10/brotli-decoder-flaw-behind-nodejs-rce.html (checked October 25, 2026)
  • The Hacker News on Imperva's Node2Shell demonstration, October 23, 2026: https://thehackernews.com/2026/10/researchers-demonstrate-node2shell-rce.html (checked October 25, 2026)
  • OpenAI's disclosure page for the flaw, patchtheplanet.org: https://patchtheplanet.org/brotli (referenced via The Hacker News, October 24, 2026; the page does not render as static text from this environment)
  • Red Hat's CVE assessment, rating 8.1 and noting no reliable public proof of concept: https://access.redhat.com/security/cve/CVE-2026-111497 (referenced via The Hacker News, October 24, 2026)
  • Vercel's Node.js security bulletin on the limits of its firewall rules: https://vercel.com/kb/bulletin/node-js-security-advisory (referenced via The Hacker News, October 24, 2026)
  • Debian security announce for brotli 1.1.0-2+deb13u1, October 23, 2026: https://lists.debian.org/debian-security-announce/2026/msg00498.html (referenced via The Hacker News, October 24, 2026)
  • PyPI download statistics for Brotli, about 82.8 million per month: https://pypistats.org/packages/brotli (checked October 25, 2026)
  • Aikido's analysis of the Python package exposure and workarounds: https://www.aikido.dev/blog/python-packages-vulnerable-node2shell-brotli-cve-2026-111497 (referenced via The Hacker News, October 24, 2026)