CVE-2026-111181 is a denial-of-service vulnerability in body-parser’s handling of compressed request bodies. The October 22 maintainer advisory says a request can produce a normal response before a later decoder error terminates the process. Fixed versions are 2.4.0 and 1.20.9. A successful response is therefore not enough to assess the server’s health after the request.
For an Express application, begin with the resolved body-parser copies and the middleware that actually receives traffic. Then rebuild and deploy the corrected dependency. Keep this package update separate from the Node.js runtime work associated with Node2Shell: replacing a runtime does not, by itself, establish that an application dependency changed.
The response can finish before the error lifecycle does
The advisory describes a decompression stream left connected without an error listener after the body is read. Trailing input can reach the finished decoder and raise an uncaught error. It rates the issue High, CVSS 3.1 7.5, and states that neither authentication nor a matching route is required for its described middleware setup.
The important boundary is between request completion and resource ownership. An application may have finished using parsed data while a stream still has work or failure events to deliver. Cleanup must account for that remaining lifecycle. Logging the HTTP response alone leaves a blind spot if the associated process subsequently exits.
Node’s EventEmitter documentation explains that an emitted error without an error listener is thrown and can terminate the process when uncaught. This explains the general failure mode; it does not mean every emitted error or every compressed request triggers this CVE.

Which dependency versions need attention
Use the maintainer’s affected and patched ranges when reviewing the dependency tree. Check each resolved copy: an application can contain a direct package and another copy under a framework or plugin.
| body-parser line | Affected range | Patched release |
|---|---|---|
| 1.x | Earlier than 1.20.9 | 1.20.9 |
| 2.x | ≥2.0.0 and <2.4.0 | 2.4.0 |
Record the package manager, lockfile revision and application artifact that reaches production. An edited manifest shows intent; the resolved installation and built artifact show what was selected. If a framework owns a transitive dependency, follow its supported update path rather than assuming a top-level install replaces every nested copy.
# Read-only inventory examples for an npm-managed application.
# Run inside the owned application checkout used for the release.
npm ls body-parser --all
node --version
# Inspect the full dependency tree when a nested copy remains unresolved.
# These commands do not update packages or verify a live service.
These inventory commands are examples for your environment and were not executed against an Express application for this article. A nonzero dependency-list exit or an unexpected tree needs interpretation; neither should be relabeled as a vulnerability test. Retain enough build provenance to connect the reviewed dependency to the deployed application.
Check where compressed input enters the application
The Express middleware documentation describes parsing before handlers, matching by content type, and automatic inflation. Review where the parser is registered and which requests reach it. The existence of an authentication check inside a later handler does not establish that earlier parsing is protected by that check.
Draw the actual inbound path, including any proxy that decompresses or rejects requests before forwarding. Keep client-to-proxy and proxy-to-application observations separate. A gateway response can explain one tested route without establishing what happens on a direct origin path or another ingress. Test only the paths you own and are authorized to exercise.
For triage, correlate unexpected restarts with application error logs, release identity and the relevant request window. An ordinary decoder error or a process exit is not a unique indicator of exploitation. This article provides no verified compromise claim or newly collected attack telemetry; its vulnerability facts come from the project advisory.
A safe local model of a late unowned error
The following program uses only built-in Node.js modules. Save it as lifecycle_model.cjs and run node lifecycle_model.cjs. It starts no HTTP server, constructs no compressed payload and imports no body-parser code. It compares removing an error listener with retaining an owner for a synthetic error.
const { EventEmitter } = require('node:events');
const assert = require('node:assert/strict');
// Offline lifecycle model only; no HTTP, compression or body-parser.
function scenario(keepErrorOwner) {
const stream = new EventEmitter();
const observed = [];
const onError = () => observed.push('error-owned');
stream.on('error', onError);
observed.push('response-complete');
if (!keepErrorOwner) stream.off('error', onError);
// Catch only this synthetic synchronous emission to keep the demo bounded.
// This does not model catching a later asynchronous stream error in a server.
try {
stream.emit('error', new Error('synthetic late error'));
} catch {
observed.push('throw-observed');
}
return observed.join(' -> ');
}
const released = scenario(false);
const retained = scenario(true);
assert.equal(released, 'response-complete -> throw-observed');
assert.equal(retained, 'response-complete -> error-owned');
console.log('released listener:', released);
console.log('retained listener:', retained);
console.log('Two offline lifecycle checks passed.');
console.log('runtime:', process.version);
This exact model ran locally on October 23, 2026, using Node.js v24.19.0. Both assertions passed:
released listener: response-complete -> throw-observed
retained listener: response-complete -> error-owned
Two offline lifecycle checks passed.
runtime: v24.19.0
The try/catch surrounds a synchronous teaching event so the demonstration ends normally. It is not a proposed way to catch an arbitrary asynchronous error in a server. Retaining a listener here illustrates ownership, not the complete vendor repair. Passing this example says nothing about whether your application has a patched body-parser or a fixed Node runtime.
If compressed request bodies are unnecessary
The documented workaround is inflate: false when the application does not need compressed request bodies. The middleware rejects those requests before creating a decompression stream. Review the actual clients before choosing that compatibility tradeoff.
// Configuration example, not executed against an application here.
const bodyParser = require('body-parser');
app.use(bodyParser.json({ inflate: false }));
app.use(bodyParser.urlencoded({ extended: false, inflate: false }));
// Review every parser instance used by the application.
// Do not add duplicate middleware blindly to an existing stack.
The middleware error documentation describes HTTP 415 with encoding.unsupported for encoded input rejected with inflation disabled. Normal uncompressed traffic still needs a compatibility check. This setting is a policy choice for request bodies; do not confuse it with disabling compressed responses sent to browsers.
Do not treat process restarts, a body-size limit or a generic error page as evidence that this lifecycle defect is repaired. Those observations answer different operational questions. Install the supported package correction, preserve sensible resource limits, and verify the actual request path under your deployment’s normal controls.

Verify the release with bounded evidence
Start with dependency and artifact evidence, then test expected application behavior in an owned staging environment. Use harmless synthetic requests and monitor the worker after the response completes. A regression that sends only normal JSON supports compatibility; it does not reproduce the trailing-input condition in the advisory. Keep that limitation attached to the result.
| प्रमाण | Useful conclusion | Remaining limit |
|---|---|---|
| Resolved fixed body-parser package | The selected dependency includes the supported correction. | The reviewed artifact still needs deployment evidence. |
| Serving artifact and worker identity | The intended release handles the tested route. | Other workers or ingress paths may differ. |
| Ordinary uncompressed request succeeds | A required happy path remains usable. | No exploit regression was performed. |
| Encoded request rejected under inflate:false | The tested parser follows the selected request-encoding policy. | Does not cover a different parser instance. |
| Worker remains healthy after the check | No exit was observed during that bounded test. | Does not prove absence of every malformed-input failure. |
If evaluating Penligent for an authorized application assessment, require evidence tied to the tested route, build and expected behavior. Keep that evaluation separate from proving the package revision. This article does not demonstrate a dedicated CVE-2026-111181 detector or claim that an application-testing report inventories every transitive dependency.
The video below introduces AI-assisted pentesting. It is workflow background, not a reproduction of this denial-of-service vulnerability or a body-parser patch test.
Questions before closing the update
Is this Node2Shell?
No. This advisory concerns body-parser’s compressed-request lifecycle and process availability. Do not use a Node runtime update as a substitute for checking this package, or vice versa.
Will a normal HTTP 200 prove the service is safe?
No. The reported failure can occur after a normal response. Preserve the process timeline and distinguish successful application behavior from lifecycle failure.
Can I disable inflation permanently?
Only if it matches the application’s supported request contract. Check legitimate clients, all relevant parser instances and intermediary behavior. Prefer a supported fixed dependency even when this restriction is retained.
Close the change when a known corrected application artifact is serving the intended routes, required client requests still work, and the evidence makes clear which conditions were actually tested. The final HTTP status and the final stream lifecycle are separate observations; retain both where they matter.

