Skip to content

CVE-2026-54466: Verified Repro With Script Download

CVE-2026-54466: websocket-driver: message corruption via abuse of draft-WebSocket length headers

CVE-2026-54466 is verified against faye/websocket-driver-node · npm. Affected versions: < 0.7.5. Fixed in 0.7.5. Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00292.

REPRO-2026-00292 faye/websocket-driver-node · npm RCE Variant found Known vulnerability Jul 16, 2026 CVE entry ↗ .txt
Severity
CRITICAL
CVSS
9.2
Confidence
HIGH
Reproduced in
7m 28s
Tool calls
127
Spend
$1.26
01 · Overview

What Is CVE-2026-54466?

CVE-2026-54466 (GHSA-xv26-6w52-cph6) is a critical message-integrity flaw in the npm package websocket-driver (faye/websocket-driver-node) before 0.7.5. Its legacy draft-75/draft-76 parser mishandles an overlong variable-width length header, causing WebSocket messages to be misframed, corrupted, or lost. Pruva reproduced it (reproduction REPRO-2026-00292).

02 · Severity & CVSS

CVE-2026-54466 Severity & CVSS Score

CVE-2026-54466 is rated critical severity, with a CVSS base score of 9.2 out of 10.

CRITICAL threat level
9.2 / 10 CVSS base
Weakness CWE-130

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected faye/websocket-driver-node Versions

faye/websocket-driver-node · npm versions < 0.7.5 are affected.

How to Reproduce CVE-2026-54466

$ pruva-verify REPRO-2026-00292
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00292/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-54466

Security impact — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

raw bytes on an established hixie-75 WebSocket TCP connection: 0x80 0xFF 0xFF 0xFF 0x7F overlong length header followed by a valid sentinel text frame

Attack chain
  1. TCP loopback
  2. Driver.server()
  3. Draft75.parse()
  4. _parseLeadingByte()
  5. stage-1 base-128 length accumulator (no _maxLength guard)
  6. stage-2 skip mode consumes/misframes the following sentinel text frame
Runnable proof: reproduction_steps.sh
Captured evidence: fixed run
Variants tested

The 0.7.5 fix only guards against oversized length headers (_length > _maxLength) in the draft-75 length accumulator. The actual message-corruption mechanism depends on two independent bugs the fix does not address: (A) _parseLeadingByte does not reset _buffer when entering binary frame mode, and (B) stage 2 treats 0x…

How the agent worked 302 events · 127 tool calls · 7 min
7 minDuration
127Tool calls
93Reasoning steps
302Events
6Dead-ends
Agent activity over 7 min
Policy
1
Support
16
Repro
83
Judge
76
Variant
121
Verify
1
0:0007:28

Root Cause and Exploit Chain for CVE-2026-54466

Versions: websocket-driver < 0.7.5 (representative vulnerable build 0.7.4; fixed control 0.7.5).

websocket-driver (npm package websocket-driver, repo faye/websocket-driver-node) versions before 0.7.5 mishandle an overlong variable-width length header in the legacy draft-75 / draft-76 (hixie-75 / hixie-76) WebSocket parser. The stage-1 base-128 length accumulator in lib/websocket/driver/draft75.js (this._length = (octet & 0x7F) + 128 * this._length) has no upper bound, so a small, attacker-controlled wire sequence makes the declared _length exceed _maxLength (0x3ffffff = 67108863). The driver stays open and enters stage-2 "skip" mode with a gigantic _length, which then consumes, loses, or misframes a subsequent valid text frame instead of delivering its intended message boundary. The 0.7.5 fix adds a per-octet guard if (this._length > this._maxLength) return this.close(); immediately after accumulation.

  • Package/component affected: websocket-driver, the Draft75/Draft76 parser path (lib/websocket/driver/draft75.js), reached via Driver.server(...) / Server.parse(chunk).
  • Affected versions: websocket-driver < 0.7.5 (representative vulnerable build 0.7.4; fixed control 0.7.5).
  • Risk level and consequences: Critical protocol-level message corruption / message loss on any server that accepts legacy hixie-75/76 framing. An attacker who can write raw bytes to an established connection can cause the parser to drop the framing boundary of the following legitimate frame and emit a stale/misframed application message, defeating message integrity. This is a denial-of-integrity / message-corruption issue, not RCE and not memory exhaustion; the wire allocation stays tiny.

Impact Parity

  • Disclosed/claimed maximum impact: Protocol message corruption / message loss (impact class other), via abuse of the draft-75/76 length header on a negotiated hixie-75/76 connection.
  • Reproduced impact from this run: On 0.7.4, the driver remained open (readyState=1) after the oversized length header, did not deliver the uniquely-marked sentinel text frame, and instead re-emitted a stale copy of the previous (positive-control) message — i.e. the sentinel was consumed/misframed. On 0.7.5, the identical stream closed (readyState=3, close event) while accumulating the over-limit length header and emitted no corrupted application message. Positive control delivered in both builds, proving the parser/listener path was operational.
  • Parity: full — the reproduced behavior matches the disclosed message-corruption/message-loss impact and the fixed-build negative control exactly.
  • Not demonstrated: No code execution, no memory exhaustion, no native crash — none claimed.

Root Cause

In Draft75.prototype.parse the per-octet state machine has three relevant stages:

  • stage 0 (_parseLeadingByte): a leading byte with the high bit set ((octet & 0x80) === 0x80) initializes _length = 0 and moves to stage 1 (binary/control frame with a variable-width length header).
  • stage 1 (length accumulator): for every octet, _length = (octet & 0x7F) + 128 * this._length. An octet whose high bit is clear is the terminator; if _length !== 0 the parser transitions to stage 2 with _skipped = 0.
  • stage 2 (skip mode): while _length is truthy, each incoming octet increments _skipped until _skipped === _length; the only early exit is octet === 0xFF, which emits Buffer.from(this._buffer) as a message and resets to stage 0. Crucially, in the high-bit-set leading-byte branch _parseLeadingByte does not reset this._buffer, so the stale buffer from a prior text frame is still referenced.

In versions < 0.7.5 there is no bound check on _length during stage 1. The minimal deterministic sequence

0x80  0xFF  0xFF  0xFF  0x7F

drives the accumulator: 0 → 127 → 16383 → 2097151 → 268435455. The terminating 0x7F (high bit clear) produces _length = 268435455, which exceeds _maxLength (0x3ffffff = 67108863), and the parser falls through to stage-2 skip mode. A following valid sentinel text frame (0x00 <utf8 "SENTINEL-VULN-XYZ-12345"> 0xFF) is then consumed byte-by-byte as "skipped" payload; when the sentinel's 0xFF terminator is reached, stage 2 emits Buffer.from(this._buffer) — the stale previous message (POSITIVE-CTRL-OK) — and resets to stage 0. The sentinel text is never delivered; a corrupted/stale message is delivered in its place. The connection stays open, so the corruption is silent and ongoing.

The 0.7.5 patch (lib/websocket/driver/draft75.js, one added line in case 1) inserts the guard immediately after accumulation:

this._length = (octet & 0x7F) + 128 * this._length;
if (this._length > this._maxLength) return this.close();   // <-- added in 0.7.5

so the over-limit header closes the driver (readyState=3, close event) before any sentinel is accepted and before any corrupted message can be emitted.

Fix reference: websocket-driver@0.7.5 release — https://github.com/faye/websocket-driver-node/releases/tag/0.7.5 ; advisory GHSA-xv26-6w52-cph6.

Reproduction Steps

  1. Reference: bundle/repro/reproduction_steps.sh (driver script) and bundle/repro/harness.js (Node TCP peer harness). Both are self-contained; the script reuses the prepared project cache (bundle/project_cache_context.json, prepared=true) when available and otherwise falls back to npm install websocket-driver@0.7.4 / @0.7.5.
  2. What the script does:
    • Resolves the vulnerable (0.7.4) and fixed (0.7.5) published websocket-driver packages from the durable project cache (or installs them).
    • Verifies the versions and the presence/absence of the _maxLength guard in draft75.js for each build.
    • For each build, runs harness.js, which starts a real loopback TCP server using net.createServer + Driver.server({requireMasking:false}), pipes connection ↔ driver.io, and a raw TCP client performs a draft-75 handshake (no sec-websocket-* headers, so Server.http selects Draft75), waits for the 101 response, then sends (a) a positive-control text frame 0x00 "POSITIVE-CTRL-OK" 0xFF, then (b) the malicious overlong length header 0x80 0xFF 0xFF 0xFF 0x7F immediately followed by (c) a uniquely-marked sentinel text frame 0x00 "SENTINEL-VULN-XYZ-12345" 0xFF. All emitted open/message/close/error events, the readyState, and the exact wire bytes are recorded to JSON.
    • Evaluates the oracle: vulnerable build must deliver the positive control, stay open (no close), not deliver the sentinel, and emit a corrupted/stale message; fixed build must deliver the positive control, emit close on the malicious header, and emit no corrupted message.
    • Writes bundle/repro/runtime_manifest.json, bundle/repro/validation_verdict.json, and best-effort copies proof artifacts into the project-cache proof-carry directory.
  3. Expected evidence of reproduction (see bundle/logs/oracle_eval.txt, bundle/logs/vuln_result.json, bundle/logs/fixed_result.json):
    • Vulnerable 0.7.4: positive_delivered=true, sentinel_delivered=false, close_after_malicious=false, corrupted_message_after_malicious=true, positive_count=2 (stale re-emit), readyState_final=1.
    • Fixed 0.7.5: positive_delivered=true, sentinel_delivered=false, close_after_malicious=true, corrupted_message_after_malicious=false, positive_count=1, readyState_final=3.
    • Oracle CONFIRMED=true only when both paths match.

Evidence

  • bundle/logs/reproduction_steps.log — full driver script transcript.
  • bundle/logs/vuln_result.json — events + wire bytes for 0.7.4.
  • bundle/logs/fixed_result.json — events + wire bytes for 0.7.5.
  • bundle/logs/vuln_run.log, bundle/logs/fixed_run.log — raw harness stdout per build.
  • bundle/logs/oracle_eval.txt — oracle evaluation summary.
  • bundle/repro/runtime_manifest.json — runtime manifest (entrypoint_kind=tcp_peer, service_started=true, healthcheck_passed=true, target_path_reached=true).
  • bundle/repro/validation_verdict.json — structured verdict (claim_outcome=confirmed).

Key excerpt (vulnerable 0.7.4 events):

[
  {"type":"open"},
  {"type":"message","data":"POSITIVE-CTRL-OK"},
  {"type":"message","data":"POSITIVE-CTRL-OK"}   // stale re-emit; sentinel "SENTINEL-VULN-XYZ-12345" never delivered
]

Key excerpt (fixed 0.7.5 events):

[
  {"type":"open"},
  {"type":"message","data":"POSITIVE-CTRL-OK"},
  {"type":"close"}                                // guard closes on over-limit length header; no corrupted message
]

Wire bytes (hex): malicious header 80ffffff7f → computed _length = 268435455 > maxLength 67108863; sentinel 0053454e54494e454c2d56554c4e2d58595a2d3132333435ff.

Environment: Node.js v24.x on Linux; net loopback TCP; websocket-driver 0.7.4 (vulnerable) and 0.7.5 (fixed) from the published npm package; no sanitizers (production path, sanitizer_used=false).

Recommendations / Next Steps

  • Upgrade: Pin websocket-driver to >= 0.7.5 (the guard if (this._length > this._maxLength) return this.close(); is the canonical fix).
  • Defense in depth: Where legacy hixie-75/76 framing is not required, disable draft-75/76 negotiation at the HTTP upgrade layer so Server.http never selects Draft75/Draft76.
  • Testing: Add a regression test that feeds 0x80 0xFF 0xFF 0xFF 0x7F + a sentinel text frame through Driver.server() over a real socket and asserts (1) a close event fires and (2) no message event carries stale/sentinel content. Also assert the positive-control text frame still delivers.
  • Variant exploration (next stage): Confirm the same accumulator path is reachable through Draft76 (hixie-76) once the 8-byte challenge body is satisfied, and check whether intermediate _length values just under _maxLength can still cause partial misframing without tripping the guard.

Additional Notes

  • Idempotency: The script was run twice consecutively with identical results (oracle CONFIRMED=true both times, identical event shapes and wire bytes); the only nondeterministic field is the event timestamp t.
  • Minimal deterministic octet sequence derived from shipped source: 0x80 0xFF 0xFF 0xFF 0x7F (5 bytes) drives _length to 268435455, the smallest terminator-using sequence that exceeds _maxLength (67108863) using all-high-bit continuation octets. The physical wire input (header + sentinel) is under 40 bytes — no resource exhaustion.
  • Real package, real socket: The proof exercises the published websocket-driver package through Driver.server(...).parse(chunk) over a real loopback TCP socket (not a copied parser or reimplementation), satisfying the network_protocol / tcp_peer entrypoint requirement.
  • Limitations: The oracle uses an in-process net loopback peer; no external network is involved. The driver's close() in the fixed build ends the connection before the sentinel, so sentinel_delivered=false in both builds — the discriminator is the close event plus the absence of a corrupted/stale message in the fixed build versus the stale re-emit + open state in the vulnerable build.

Variant Analysis & Alternative Triggers for CVE-2026-54466

Versions: versions (as tested): websocket-driver@0.7.4 (vulnerable) and websocket-driver@0.7.5 (fixed — bypass confirmed). Both are affected by this variant.

The 0.7.5 fix for CVE-2026-54466 adds a single guard in the draft-75 length accumulator (case 1): if (this._length > this._maxLength) return this.close(). This prevents oversized length headers but does not address the two independent root-cause bugs that actually produce the message corruption: (A) _parseLeadingByte does not reset this._buffer when entering binary frame mode, leaving a stale reference to the previous text frame's content; and (B) in stage 2, the if (octet === 0xFF) frame-terminator check fires before the skip-mode branch, so 0xFF is treated as a frame terminator even during binary-frame payload consumption. By sending a binary frame with a valid length (within _maxLength) that is >= the next frame's wire size, an attacker can cause the parser to consume a subsequent valid sentinel text frame as skip data; when the sentinel's 0xFF terminator is encountered in skip mode, the stale _buffer is emitted as a message, and the sentinel is lost. This bypass is confirmed on the fixed 0.7.5 build — the _maxLength guard never fires because the length is well within bounds.

Fix Coverage / Assumptions

  • Invariant the fix relies on: "If _length <= _maxLength, the parser state is safe." The fix assumes that message corruption requires an oversized _length to function.
  • Code paths explicitly covered: case 1 (the base-128 length accumulator in Draft75.prototype.parse). The guard fires after every continuation octet, closing the driver if the accumulated _length exceeds _maxLength. This covers both Draft75 and Draft76 (which inherits parse from Draft75).
  • What the fix does NOT cover:
    • _parseLeadingByte does not reset this._buffer when the high bit is set (binary frame path). The stale buffer from a prior text frame persists.
    • Stage 2's if (octet === 0xFF) check fires unconditionally — before the skip-mode branch (if (this._length)) — so 0xFF is treated as a frame terminator even in skip mode, emitting the stale buffer.
    • These two gaps combine to allow the same message corruption with a valid, bounded _length.

Variant / Alternate Trigger

Bypass path: binary frame with valid (within _maxLength) length header

  • Entry point: Driver.server(...).parse(chunk) / Server.parse(chunk)Draft75.parse() — the same public runtime path as the original CVE, selected via a draft-75 handshake (no Sec-WebSocket-* headers).

  • Wire sequence:

    1. Positive control text frame: 0x00 "POSITIVE-CTRL-OK" 0xFF → populates _buffer with "POSITIVE-CTRL-OK"
    2. Variant binary frame header: 0x80 0x1E_parseLeadingByte: high bit set → _length=0, _stage=1. Then case 1: _length = (0x1E & 0x7F) + 128*0 = 30. Fix guard: 30 > 67108863false, does NOT fire. High bit clear → _length != 0_skipped=0, _stage=2 (skip mode). _buffer still holds stale "POSITIVE-CTRL-OK".
    3. Sentinel text frame: 0x00 "SENTINEL-VULN-XYZ-12345" 0xFF (25 bytes) → consumed byte-by-byte in skip mode. Bytes 0–23: not 0xFF, _skipped increments to 24 (< 30, stay in stage 2). Byte 24: 0xFFif (octet === 0xFF) fires → emits Buffer.from(this._buffer) = stale "POSITIVE-CTRL-OK" as a message → _stage=0.
  • Code path: lib/websocket/driver/draft75.jsparse()case 0 (_parseLeadingByte) → case 1 (length accumulator, fix guard passes) → case 2 (skip mode, 0xFF triggers stale buffer emission).

  • Result: Sentinel is consumed and lost. A stale duplicate of the previous message is emitted instead. The connection stays open (readyState=1). This is identical to the original CVE's impact.

  • Package/component affected: websocket-driver, Draft75/Draft76 parser path (lib/websocket/driver/draft75.js), reached via Driver.server(...) / Server.parse(chunk).

  • Affected versions (as tested): websocket-driver@0.7.4 (vulnerable) and websocket-driver@0.7.5 (fixed — bypass confirmed). Both are affected by this variant.

  • Risk level and consequences: Critical protocol-level message corruption / message loss. An attacker who can write raw bytes to an established draft-75/76 connection can cause the parser to drop the framing boundary of a legitimate frame and emit a stale/misframed application message, defeating message integrity. The wire allocation is tiny (7 bytes for the variant + sentinel). The connection stays open, so the corruption is silent and ongoing.

Impact Parity

  • Disclosed/claimed maximum impact: Protocol message corruption / message loss (impact class other), via abuse of the draft-75/76 framing on a negotiated hixie-75/76 connection.
  • Reproduced impact from this variant run: On both 0.7.4 and 0.7.5, the driver remained open (readyState=1) after the variant binary frame, did not deliver the uniquely-marked sentinel text frame, and instead re-emitted a stale copy of the previous (positive-control) message. Positive control delivered in both builds, proving the parser/listener path was operational.
  • Parity: full — the reproduced behavior matches the disclosed message-corruption/message-loss impact exactly, and the bypass reproduces identically on the fixed build.
  • Not demonstrated: No code execution, no memory exhaustion, no native crash — none claimed.

Root Cause

The message corruption in the draft-75 parser has two independent root-cause bugs:

  1. Stale _buffer (Gap A): In _parseLeadingByte, when a leading byte with the high bit set is received (binary frame), _buffer is NOT reset. It retains the content from the previous text frame. The text-frame path (high bit clear) correctly creates a fresh this._buffer = [], but the binary-frame path does not.

  2. 0xFF as unconditional frame terminator in stage 2 (Gap B): In case 2, the check if (octet === 0xFF) is the first branch, before the skip-mode/text-mode split. In skip mode (this._length is truthy), 0xFF should be treated as a payload data byte, not a frame terminator. Instead, it emits Buffer.from(this._buffer) (the stale buffer from Gap A) as a message and resets to stage 0.

The 0.7.5 fix (commit 5b197ca) only adds the _maxLength guard in case 1. It does not address Gap A or Gap B. Because a valid, bounded _length (e.g., 30) is sufficient to trigger the corruption — the attacker just needs _length >= the sentinel frame's wire size so the sentinel's 0xFF terminator is encountered in skip mode before _skipped reaches _length — the fix is bypassable.

Fix reference: commit 5b197ca874dab58e96cacad8a3c256797d804680lib/websocket/driver/draft75.js, one added line.

Reproduction Steps

  1. Reference: bundle/vuln_variant/reproduction_steps.sh (driver script) and bundle/vuln_variant/variant_harness.js (Node TCP peer harness). Both are self-contained; the script reuses the prepared project cache (bundle/project_cache_context.json, prepared=true) when available and otherwise falls back to npm install websocket-driver@0.7.4 / @0.7.5.
  2. What the script does:
    • Resolves the vulnerable (0.7.4) and fixed (0.7.5) published websocket-driver packages from the durable project cache (or installs them).
    • Verifies the versions and the presence of the _maxLength guard in draft75.js for the fixed build.
    • For each build, runs variant_harness.js, which starts a real loopback TCP server using net.createServer + Driver.server({requireMasking:false}), pipes connection ↔ driver.io, and a raw TCP client performs a draft-75 handshake (no Sec-WebSocket-* headers) so Server.http selects Draft75.
    • Sends: (1) a positive-control draft-75 text frame, (2) a variant binary frame header 0x80 0x1E (valid length=30, within _maxLength), immediately followed by (3) a uniquely-marked sentinel text frame — all in a contiguous wire stream.
    • Records every emitted open/message/close/error event, the driver readyState, and the exact wire bytes, then writes a JSON result file.
    • Evaluates the oracle: on both versions, sentinel_delivered=false, corrupted_message_after_malicious=true (stale re-emit), close_after_malicious=false, readyState_final=1.
  3. Expected evidence: bundle/logs/variant_oracle_eval.txt shows BYPASS CONFIRMED: true — the variant reproduces on the fixed 0.7.5 build with exit code 0.

Evidence

  • Log locations:

    • bundle/logs/variant_repro.log — driver script output
    • bundle/logs/vuln_variant_run.log — vulnerable 0.7.4 harness output
    • bundle/logs/fixed_variant_run.log — fixed 0.7.5 harness output
    • bundle/logs/vuln_variant_result.json — vulnerable 0.7.4 structured result
    • bundle/logs/fixed_variant_result.json — fixed 0.7.5 structured result
    • bundle/logs/variant_oracle_eval.txt — oracle evaluation
  • Key excerpt (fixed 0.7.5):

    Fixed 0.7.5:
      positive_control_delivered : true
      sentinel_delivered         : false
      close_after_malicious      : false
      corrupted_message_emitted  : true
      positive_message_count     : 2 (2+ => stale re-emit = misframing)
      readyState_final           : 1 (1=open, 3=closed)
    
    Oracle verdict:
      vuln_path_matches  : true
      fixed_path_matches : true
      BYPASS CONFIRMED   : true
    
    variant_length : 30 <= maxLength 67108863 (fix guard does NOT fire)
    
  • Environment: Node.js, loopback TCP, websocket-driver@0.7.4 and @0.7.5 from published npm packages (cached in project cache). Fixed build commit: 5d6a9aaf5f019007d917bd9ddc7eeb775c86cc1f.

Recommendations / Next Steps

The fix must be extended to close Gap A and Gap B:

  1. Reset _buffer in _parseLeadingByte when entering binary frame mode. Add this._buffer = []; (or delete this._buffer) in the if ((octet & 0x80) === 0x80) branch, alongside this._length = 0 and this._stage = 1.

  2. Restrict the 0xFF frame-terminator check in stage 2 to text mode only. Move the if (octet === 0xFF) check inside the else branch (where _length is falsy / text mode), or restructure so that in skip mode (this._length is truthy), 0xFF is treated as a regular skip byte. For example:

    case 2:
      if (this._length) {
        // skip mode: 0xFF is a data byte, not a terminator
        this._skipped += 1;
        if (this._skipped === this._length) this._stage = 0;
      } else {
        // text mode
        if (octet === 0xFF) {
          this._stage = 0;
          message = Buffer.from(this._buffer).toString('utf8', 0, this._buffer.length);
          this.emit('message', new Base.MessageEvent(message));
        } else {
          this._buffer.push(octet);
          if (this._buffer.length > this._maxLength) return this.close();
        }
      }
      break;
    

Either fix alone would partially mitigate the issue, but both should be applied for defense in depth.

Additional Notes

  • Idempotency: The reproduction script was run twice with identical results (exit code 0 both times). The script is idempotent and deterministic.
  • Edge cases: The variant length must be >= the sentinel frame's wire size (25 bytes in this test) for the sentinel's 0xFF terminator to be encountered in skip mode before _skipped reaches _length. A length of 30 provides a safe margin. Any length from 25 to _maxLength (67,108,863) works.
  • Draft76 coverage: Draft76 inherits parse from Draft75 and does not override it, so the bypass applies to draft-76 connections as well. The server selects Draft76 when Sec-WebSocket-Key1/Sec-WebSocket-Key2 headers are present.
  • Client-side parser: The client-side driver (client.js) uses Hybi (modern WebSocket protocol), not Draft75, so the client-side parser is not affected by this variant.

CVE-2026-54466 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired. Phases: support · claim contract · reproduction · judge · variant analysis

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:000:30
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · CVE-2026-54466 · REPRO-20
0:02
0:03
0:04
web search
0:06
0:07
web search
0:09
0:10
0:11
0:21
0:21
extract_facts
no facts extracted
0:22
0:22
supportclaim_contract
0:24
0:24
0:24
0:25
0:25
0:25
0:26
0:26
0:26
0:26
0:27
0:27
$cd /data/pruva/project-cache/a075e72d-24e1-4a1d-a5ed-88cbb2e0a266 && ls -la repo vuln fixed 2>/dev/null; echo "---vuln---"; ls -la vuln 2>/dev/null; echo "---fixed---"; ls -la fixed 2>/dev/null
{"cwd":"/data/pruva/runs/294686a0-89e7-4ba8-a27d-7fca0e3a9e70","duration_ms":7,"exit_code":0,"stderr":"","stdout":"fixed:\ntotal 8\ndrwxr-xr-x 1 vscode vscode   82 Jul 16 08:11 .\ndrwxr-xr-x 1 vscode vscode   62 Jul 16 08:18 ..\ndrwxr-xr-x 1 vscode vscode  158 Jul 16 08:11 node_modules\n-rw-r--r-- 1 vscode vscode 2085 Jul 16 08:11 package-lock.json\n-rw-r--r-- 1 vscode vscode  299 Jul 16 08:11 package.json\n\nrepo:\ntotal 40\ndrwxr-xr-x 1 vscode vscode   194 Jul 16 08:10 .\ndrwxr-xr-x 1 vscode vscode    62 Jul 16 08:18 ..\ndrwxr-xr-x 1 vscode vscode   122 Jul 16 10:42 .git\ndrwxr-xr-x 1 vscode vscode    18 Jul 16 08:10 .github\n-rw-r--r-- 1 vscode vscode    31 Jul 16 08:10 .gitignore\n-rw-r--r-- 1 vscode vscode  4358 Jul 16 08:10 CHANGELOG.md\n-rw-r--r-- 1 vscode vscode   162 Jul 16 08:10 CODE_OF_CONDUCT.md\n-rw-r--r-- 1 vscode vscode   558 Jul 16 08:10 LICENSE.md\n-rw-r--r-- 1 vscode vscode 12960 Jul 16 08:10 README.md\ndrwxr-xr-x 1 vscode vscode    52 Jul 16 08:10 examples\ndrwxr-xr-x 1 vscode vscode    18 Jul 16 08:10 lib\n-rw-r--r-- 1 vscode vscode  1204 Jul 16 08:10 package.json\ndrwxr-xr-x 1 vscode vscode    36 Jul 16 08:10 spec\n\nvuln:\ntotal 8\ndrwxr-xr-x 1 vscode vscode  … [truncated]
0:29
0:29
0:29
$cd /data/pruva/project-cache/a075e72d-24e1-4a1d-a5ed-88cbb2e0a266 && echo "=== vuln pkg ==="; cat vuln/package.json; echo "=== fixed pkg ==="; cat fixed/package.json; echo "=== vuln node_modules ==="; ls vuln/node_modules; echo "=== fixed node_modules ==="; ls fixed/node_modules
=== vuln pkg ===
{
  "name": "vuln",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
08 · How to Fix

How to Fix CVE-2026-54466

Upgrade faye/websocket-driver-node · npm to 0.7.5 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-54466 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-54466

Does CVE-2026-54466 lead to remote code execution?

No. The impact is protocol message corruption and message loss (Integrity High) — not remote code execution or memory exhaustion. It is rated CVSS v4 9.2 Critical because message misframing can silently corrupt a WebSocket stream.

How is CVE-2026-54466 fixed?

Version 0.7.5 adds a per-octet bound check — 'if (this._length > this._maxLength) return this.close();' — immediately after the length accumulation, so an overlong header closes the connection instead of desynchronizing the parser. Upgrade websocket-driver to 0.7.5 or later.

Which versions of websocket-driver are affected by CVE-2026-54466?

websocket-driver (faye/websocket-driver-node) versions < 0.7.5 are affected, via the Draft75/Draft76 (hixie-75/hixie-76) parser path. It is fixed in 0.7.5.

How can I reproduce CVE-2026-54466?

Download the verified script from this page and run it in an isolated environment against websocket-driver < 0.7.5. It sends a small crafted wire sequence that drives _length past _maxLength and shows a subsequent valid text frame being misframed, which the 0.7.5 fix prevents.
11 · References

References for CVE-2026-54466

Authoritative sources for CVE-2026-54466 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.