From 664aebe4066b2032f316369ce28536b9795bd65e Mon Sep 17 00:00:00 2001 From: DeepView Autofix <276251120+deepview-autofix@users.noreply.github.com> Date: Fri, 17 Apr 2026 05:50:56 +0300 Subject: [PATCH] fix(websocket): use 1002 close code on parser errors onParserError passed null as the close code to failWebsocketConnection, causing closeWebSocketConnection to default the code to 1000 ("Normal Closure") whenever the reason string was non-empty. Protocol errors (invalid frames, RSV bit violations, etc.) surfaced through the parser were thus reported to the peer as normal closures instead of 1002 ("Protocol Error"), violating RFC 6455 section 7.4.1. Co-Authored-By: Claude Co-Authored-By: DeepView Autofix <276251120+deepview-autofix@users.noreply.github.com> Co-Authored-By: Nikita Skovoroda Signed-off-by: Nikita Skovoroda --- lib/web/websocket/websocket.js | 2 +- test/websocket/parser-error-close-code.js | 34 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 test/websocket/parser-error-close-code.js diff --git a/lib/web/websocket/websocket.js b/lib/web/websocket/websocket.js index a2abd9c9ab6..5261fbf7caa 100644 --- a/lib/web/websocket/websocket.js +++ b/lib/web/websocket/websocket.js @@ -76,7 +76,7 @@ class WebSocket extends EventTarget { #handler = { onConnectionEstablished: (response, extensions) => this.#onConnectionEstablished(response, extensions), onMessage: (opcode, data) => this.#onMessage(opcode, data), - onParserError: (err) => failWebsocketConnection(this.#handler, null, err.message), + onParserError: (err) => failWebsocketConnection(this.#handler, 1002, err.message), onParserDrain: () => this.#onParserDrain(), onSocketData: (chunk) => { if (!this.#parser.write(chunk)) { diff --git a/test/websocket/parser-error-close-code.js b/test/websocket/parser-error-close-code.js new file mode 100644 index 00000000000..9e762c39ce2 --- /dev/null +++ b/test/websocket/parser-error-close-code.js @@ -0,0 +1,34 @@ +'use strict' + +const { test } = require('node:test') +const { WebSocketServer } = require('ws') +const { WebSocket } = require('../..') +const { ByteParser } = require('../../lib/web/websocket/receiver') + +test('Parser errors close the connection with 1002 (Protocol Error)', async (t) => { + const originalWrite = ByteParser.prototype._write + ByteParser.prototype._write = function (chunk, enc, cb) { + this.emit('error', new Error('simulated parser error')) + cb() + } + t.after(() => { + ByteParser.prototype._write = originalWrite + }) + + const server = new WebSocketServer({ port: 0, host: '127.0.0.1' }) + await new Promise((resolve) => server.on('listening', resolve)) + t.after(() => server.close()) + + const receivedCode = new Promise((resolve) => { + server.on('connection', (serverWs) => { + serverWs.on('close', (code) => resolve(code)) + // Trigger the patched parser with any valid frame (a PING). + serverWs._socket.write(Buffer.from([0x89, 0x00])) + }) + }) + + const ws = new WebSocket(`ws://127.0.0.1:${server.address().port}`) + t.after(() => ws.close()) + + t.assert.strictEqual(await receivedCode, 1002) +})