Summary
redis-parser 3.0.0 reads the multi-bulk array length off the wire with no
upper bound and passes it directly to new Array(length). A ~14-byte header with
length > 2^32 - 1 makes new Array throw RangeError: Invalid array length.
No try/catch exists in the call chain, so the throw escapes execute() as an
uncaughtException and terminates the Node process.
Untrusted RESP framing crosses the network-reply →
execute() → client-process boundary via a 14-byte multi-bulk header, triggering
an uncaught exception that terminates the client process.
Trust Boundary
The RESP byte stream from the Redis endpoint is the sole untrusted input crossing
into parser.execute(). The endpoint controls RESP framing (type bytes, declared
lengths, terminators) — a strictly greater capability than storing a value via
SET, which Redis re-frames with its own correct headers on read-back.
"The endpoint is trusted" is a deployment hope, not an architectural guarantee the
library can rely on: Redis is cleartext TCP unless TLS is configured, AUTH
authenticates the client to the server (not the server's reply bytes to the
client), and a parser is by definition a trust-boundary component. A redirected or
compromised endpoint (SSRF-to-Redis, on-path attacker, failed-over cluster node,
malicious managed/multi-tenant instance) delivers attacker-chosen framing through
the same API. The crash is additionally not routed through returnFatalError;
it escapes execute() as an uncaught exception, so a consumer following the
README's protocol-error handling cannot intercept it — behavior beyond the
documented contract.
CVSS 3.1 Breakdown
Base Score: 7.5 (High)
Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
| Metric |
Value |
Justification |
| AV |
Network |
Multi-bulk header from a remote peer. |
| AC |
Low |
A single ~14-byte chunk. |
| PR |
None |
— |
| UI |
None |
— |
| S |
Unchanged |
— |
| C |
None |
— |
| I |
None |
— |
| A |
High |
Uncaught exception -> process termination. |
Description
function parseArray (parser) { // lib/parser.js:204-214
const length = parseLength(parser)
if (length === undefined) return
if (length < 0) return null
const responses = new Array(length) // :212 — no upper bound
return parseArrayElements(parser, responses, 0)
}
parseLength applies no cap, so length is fully attacker-controlled. For
length > 2^32 - 1, new Array(length) (lib/parser.js:212) throws a RangeError
that nothing in the execute → parseType → parseArray chain catches — execute
(lib/parser.js:491-549) has no try/catch — so it propagates to the host as an
uncaught exception.
Proof of Concept
poc/cve6_array_prealloc_crash.js — proves the throw reaches
process.uncaughtException, i.e. terminates the process under normal integration:
'use strict'
// CVE-6 (crash variant): prove it is truly an UNCAUGHT exception that terminates the process
// when the host does not wrap execute() in try/catch (the normal integration).
const Parser = require('redis-parser')
const p = new Parser({ returnReply: () => {}, returnError: () => {}, returnFatalError: () => {} })
process.on('uncaughtException', (e) => {
console.log('uncaughtException handler fired :', e.constructor.name + ': ' + e.message)
console.log('RESULT: VULNERABLE (process would terminate without this handler)')
process.exit(0)
})
p.execute(Buffer.from('*99999999999\r\n')) // no try/catch here -> escapes to uncaughtException
console.log('RESULT: not-repro')
Execution Steps
cd poc
npm install
node cve6_array_prealloc_crash.js
Reproduction Evidence
Environment: Node.js v26.5.0, redis-parser@3.0.0.
uncaughtException handler fired : RangeError: Invalid array length
RESULT: VULNERABLE (process would terminate without this handler)
The 14-byte header *99999999999\r\n threw a RangeError that reached the
process-level uncaughtException handler. The error is not routed to
returnFatalError, so a consumer cannot intercept it; without the handler the
process terminates.
Impact
A single tiny header from a malicious/compromised/MITM Redis peer kills the
client process — every connection, tenant, and in-flight request dies together.
Recommended Fix
- Impose a hard
MAX_ARRAY_LENGTH ceiling before allocating; treat violations as
fatal protocol errors routed through returnFatalError.
- Build result arrays incrementally with
push() rather than pre-allocating.
- Wrap
execute() in try/catch so unexpected throws degrade to a connection
error instead of process termination.
Reference
- CWE-248: Uncaught Exception
- CWE-1284: Improper Validation of Specified Quantity in Input
Summary
redis-parser3.0.0 reads the multi-bulk array length off the wire with noupper bound and passes it directly to
new Array(length). A ~14-byte header withlength > 2^32 - 1makesnew ArraythrowRangeError: Invalid array length.No
try/catchexists in the call chain, so the throw escapesexecute()as anuncaughtExceptionand terminates the Node process.Untrusted RESP framing crosses the network-reply →
execute() → client-process boundary via a 14-byte multi-bulk header, triggering
an uncaught exception that terminates the client process.
Trust Boundary
The RESP byte stream from the Redis endpoint is the sole untrusted input crossing
into
parser.execute(). The endpoint controls RESP framing (type bytes, declaredlengths, terminators) — a strictly greater capability than storing a value via
SET, which Redis re-frames with its own correct headers on read-back."The endpoint is trusted" is a deployment hope, not an architectural guarantee the
library can rely on: Redis is cleartext TCP unless TLS is configured,
AUTHauthenticates the client to the server (not the server's reply bytes to the
client), and a parser is by definition a trust-boundary component. A redirected or
compromised endpoint (SSRF-to-Redis, on-path attacker, failed-over cluster node,
malicious managed/multi-tenant instance) delivers attacker-chosen framing through
the same API. The crash is additionally not routed through
returnFatalError;it escapes
execute()as an uncaught exception, so a consumer following theREADME's protocol-error handling cannot intercept it — behavior beyond the
documented contract.
CVSS 3.1 Breakdown
Base Score: 7.5 (High)
Vector:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HDescription
parseLengthapplies no cap, solengthis fully attacker-controlled. Forlength > 2^32 - 1,new Array(length)(lib/parser.js:212) throws aRangeErrorthat nothing in the
execute → parseType → parseArraychain catches —execute(
lib/parser.js:491-549) has notry/catch— so it propagates to the host as anuncaught exception.
Proof of Concept
poc/cve6_array_prealloc_crash.js— proves the throw reachesprocess.uncaughtException, i.e. terminates the process under normal integration:Execution Steps
cd poc npm install node cve6_array_prealloc_crash.jsReproduction Evidence
Environment: Node.js v26.5.0,
redis-parser@3.0.0.The 14-byte header
*99999999999\r\nthrew aRangeErrorthat reached theprocess-level
uncaughtExceptionhandler. The error is not routed toreturnFatalError, so a consumer cannot intercept it; without the handler theprocess terminates.
Impact
A single tiny header from a malicious/compromised/MITM Redis peer kills the
client process — every connection, tenant, and in-flight request dies together.
Recommended Fix
MAX_ARRAY_LENGTHceiling before allocating; treat violations asfatal protocol errors routed through
returnFatalError.push()rather than pre-allocating.execute()intry/catchso unexpected throws degrade to a connectionerror instead of process termination.
Reference