펜리젠트 헤더

CVE-2026-111497: Node.js Brotli RCE and Fixed Versions

CVE-2026-111497 is a Node.js Brotli decoder out-of-bounds write that the Node.js project says can permit remote code execution. 그리고 October 21, 2026 security release covers the 22.x, 24.x and 26.x release lines. For a developer running an API, worker or server-rendered application, the useful first step is to identify the actual Node executable in each deployment—not just the version selected in a local terminal.

Reviewed October 22, 2026. This analysis uses the official security release and API documentation. The public summary does not establish the exact trigger for every application. We ran a small local Brotli behavior check with benign input, described below; we did not reproduce this CVE or test a production service.

CVE-2026-111497 Node.js Brotli RCE and fixed versions cover
Editorial illustration; not an exploit trace or product screenshot.

Node.js versions that contain the security update

Release lineOctober 21 fixed releaseDeployment question
22.x22.23.4Does every active instance use the updated runtime?
24.x24.22.1Was the image or service replaced after the update?
26.x26.12.1Is the serving executable the one you inspected?

These are upstream release baselines, confirmed by the linked release notes. A distribution or hosting provider may supply a differently numbered backport; require its advisory and complete revision before accepting that build. For end-of-life branches, move through an actively supported provider path rather than interpreting their absence from this table as immunity.

A useful inventory row contains the workload, environment, executable or image identity, observed runtime, provider and activation status. Keep build jobs separate from serving jobs. An updated CI build image can produce an artifact that is later served by an older runtime, while a static frontend build may have no Node process handling incoming requests at all. The product name alone cannot distinguish these cases.

Where Brotli decoding enters an application

Node’s zlib module exposes Brotli compression and decompression through stream and convenience APIs. Compression produces an encoded representation; decompression interprets one. For this decoder issue, finding a Brotli encoder is therefore not equivalent to establishing an attacker-reachable decoder. Follow the bytes to the operation that actually consumes them.

Map three things together: the origin of compressed data, the component selecting a decoding path, and the process performing that decode. Possible review locations include an application import feature, a worker consuming an object, or an HTTP client processing an upstream response. These are investigation candidates, not a claim that all such features exploit the CVE. Confirm the behavior of your framework and client rather than assuming every Content-Encoding header reaches the same implementation.

Observed flowQuestion to resolveUseful evidence
Node compresses a responseIs any untrusted input decoded in this process?Code path and relevant runtime calls
Node processes compressed upstream dataWho can influence the upstream bytes?Client configuration and request/response trace
Worker unpacks an imported objectWhich owner can supply or replace that object?Job identity, object provenance and decoder path
Proxy handles compression before NodeDoes Node still decode any other input?Proxy/application boundary plus worker review
Conceptual Node.js Brotli decoding exposure and runtime response model
Conceptual exposure and response model; no CVE exploit chain was reproduced.
Test Your Own Application with Penligent →

For a hypothetical SaaS import job, authentication to the website does not make every submitted object trustworthy. A signed-in customer can still control the uploaded bytes. Conversely, the mere presence of an upload form does not establish Brotli processing. An evidence-based exposure note should name the decoder and input path or state explicitly that this part remains unresolved.

Read the executable identity without dumping secrets

그리고 Node process documentation provides executable, version and platform information. This local command reports a narrow subset for the invocation you run. Use it inside the deployment context you own. Do not add an environment dump to the output; it is unnecessary for this inventory and can disclose credentials.

node -e 'console.log(JSON.stringify({
  node: process.version,
  executable: process.execPath,
  platform: process.platform,
  arch: process.arch
}, null, 2))'

Connect this result to the serving process through your service definition or deployment record. A shell opened in a fresh administrative container can accurately report its own version while the application continues using an older container. Record the active replica set, including workers and fallbacks. If the provider manages the runtime, ask it to identify the fixed build applied to your specific workload.

A source search can help locate explicit decoder usage in an owned repository. The following is a review aid, not a vulnerability scanner. It can miss wrappers, dependencies, generated code and dynamically selected methods. Inspect relevant dependency behavior as well; an absence of literal matches cannot establish absence of decoding.

rg -n --glob '!node_modules/**' \
  'brotliDecompress|createBrotliDecompress|content-encoding' .

A bounded local Brotli check you can reproduce

This CommonJS script uses a short string generated in memory. It checks a normal round trip, rejection under a deliberately tiny output budget, and rejection of plain text supplied as compressed data. It makes no network requests and includes no exploit payload. Save it as brotli-check.cjs and run it in an isolated development workspace.

const assert = require('node:assert/strict');
const { brotliCompressSync, brotliDecompressSync } = require('node:zlib');

const plain = Buffer.from('owned staging fixture\n'.repeat(8));
const compressed = brotliCompressSync(plain);
const restored = brotliDecompressSync(compressed, { maxOutputLength: 4096 });
assert.deepEqual(restored, plain);

function rejected(input, maxOutputLength) {
  try {
    brotliDecompressSync(input, { maxOutputLength });
    return false;
  } catch {
    return true;
  }
}

const checks = {
  valid_round_trip: restored.equals(plain),
  tiny_output_budget_rejected: rejected(compressed, 16),
  invalid_text_rejected: rejected(Buffer.from('not a brotli stream'), 4096),
};
assert.equal(Object.values(checks).every(Boolean), true);
console.log(JSON.stringify({ node: process.version, checks }, null, 2));

We executed this exact script on October 22, 2026. The available test runtime was Node.js v24.19.0, which is older than the security release listed above. All three checks passed:

{
  "node": "v24.19.0",
  "checks": {
    "valid_round_trip": true,
    "tiny_output_budget_rejected": true,
    "invalid_text_rejected": true
  }
}

A passing result is not an exploit regression test, and it does not establish that CVE-2026-111497 is fixed. Here it shows only that these benign cases behaved as asserted on the named runtime. Keeping that limitation next to the output matters: an ordinary decoder test can pass on a runtime that still needs a security update.

BrotliOptions documents maxOutputLength for limiting convenience-method output. That budget helps make this small functional exercise bounded. It is not evidence that the option prevents the native memory-safety flaw. Application resource limits and the runtime patch answer different questions; do not substitute one for the other.

Separate patch delivery, activation and compatibility

A package update changes what is available to launch. Deployment activation changes which executable handles real work. A functional check asks whether the application still behaves as intended. Treat those as three acceptance gates, with an owner for each. A green package-manager result should not close an application whose old processes remain active.

GateRecordWhat it cannot prove
DeliveryApproved fixed runtime or documented vendor backportThat existing processes have adopted it
ActivationEvery serving instance and worker mapped to the intended buildThat normal business operations still work
CompatibilityExpected decode behavior and application smoke testsThat all exploit paths were tested
Incident reviewPreserved evidence and investigation outcomeThat installing a patch removed prior access
Recommended Node.js runtime activation and benign functional verification workflow
Recommended workflow. Passing benign checks does not establish a CVE fix.
Test Your Own Application with Penligent →

For an application with an import queue, use a purpose-created account and a harmless owned asset. Exercise the complete path: upload, queue dispatch, processing result and expected access rule. Record the runtime used by the worker, not just the web frontend. A test that never reaches the decoder provides availability evidence for the frontend but no evidence about the job you intended to review.

When a release includes several security fixes, avoid reducing the maintenance record to one CVE. The October Node.js release also addresses DNS, TLS and other issues. Prefer the complete supported update. If an application fails after activation, investigate the actual behavior before weakening certificate validation or substituting a separately downloaded library.

Why npm audit and a framework update are incomplete answers

A dependency report and a runtime inventory cover different objects. Updating a package.json entry does not establish which native components are compiled into the Node binary. Likewise, a framework release may be useful but does not itself prove that your deployment provider selected a fixed Node runtime. Ask which layer owns each change and collect the corresponding evidence.

For a managed deployment, request the exact runtime release and rollout scope. For an owned container, rebuild through the maintained process and replace the intended instances. Preserve the image identity used for the change. If you must roll back, include the resulting runtime security state in the rollback decision instead of silently restoring a version that predates the fix.

After the runtime work, an authorized application assessment can review who can submit an import, which tenant owns its result and whether a failed decode leaves partial application state. Penligent is an option for that separate staging workflow. This article does not demonstrate a CVE-2026-111497-specific Penligent check or claim that an application assessment proves the runtime patch is present.

Questions about the Node.js Brotli vulnerability

Does using Brotli compression mean my app has a reachable RCE?

The official issue concerns decoding. Establish the runtime and whether attacker-influenced data reaches that decoder. Update affected deployments without treating a generic compression setting as proof of exploitation.

Which releases should I look for?

The October 21 upstream security releases are 22.23.4, 24.22.1 and 26.12.1. Confirm a provider backport through its own advisory and complete revision.

Does the local script prove I am patched?

No. It tests benign behavior only. Its published results came from v24.19.0, which predates the relevant update.

Can I rely on maxOutputLength instead?

It is an output-budget option for the documented convenience methods. This article does not establish it as a mitigation for this CVE. Apply the supported security update.

Do I need to update workers as well as the web server?

Inventory both. A worker may own the decoding path even when the frontend only stores an object or enqueues a job. Record the runtime that performs the work.

A defensible closeout connects the fixed runtime to the real workload, shows the intended application path still works and preserves any unresolved incident evidence. A passing decoder demo is useful only when its narrow purpose stays visible.

게시물을 공유하세요:
관련 게시물
ko_KRKorean