Skip to content

Commit 66e1281

Browse files
committed
fix(websocket): reject unrequested subprotocols
Guard the opening-handshake protocol check when the client did not request any subprotocols. Fail the WebSocket connection with protocol error 1002 instead of allowing a TypeError to escape from the response microtask and terminate the process. Fixes: GHSA-rfgv-xxqx-mfg5 CVE: CVE-2026-19534 Signed-off-by: Matteo Collina <hello@matteocollina.com>
1 parent 7aac7f1 commit 66e1281

2 files changed

Lines changed: 34 additions & 1 deletion

File tree

‎lib/web/websocket/connection.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ function establishWebSocketConnection (url, protocols, client, handler, options)
200200
// is specified, the server needs to include the same field and one of
201201
// the selected subprotocol values in its response for the connection to
202202
// be established.
203-
if (!requestProtocols.includes(secProtocol)) {
203+
if (requestProtocols === null || !requestProtocols.includes(secProtocol)) {
204204
failWebsocketConnection(handler, 1002, 'Protocol was not set in the opening handshake.')
205205
return
206206
}

‎test/websocket/opening-handshake.js‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,3 +516,36 @@ test('Server sends invalid Sec-WebSocket-Extensions header', { skip: runtimeFeat
516516
})
517517
})
518518
})
519+
520+
test('Server sends Sec-WebSocket-Protocol header when no protocols were requested', { skip: runtimeFeatures.has('crypto') === false }, (t) => {
521+
const uid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
522+
523+
return new Promise((resolve, reject) => {
524+
const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {
525+
const key = req.headers['sec-websocket-key']
526+
t.assert.ok(key)
527+
t.assert.strictEqual(req.headers['sec-websocket-protocol'], undefined)
528+
529+
const accept = require('node:crypto').hash('sha1', key + uid, 'base64')
530+
531+
res.setHeader('Upgrade', 'websocket')
532+
res.setHeader('Connection', 'upgrade')
533+
res.setHeader('Sec-WebSocket-Accept', accept)
534+
res.setHeader('Sec-WebSocket-Protocol', 'chat') // <-- never requested
535+
res.statusCode = 101
536+
537+
res.end()
538+
}).listen(0, () => {
539+
// No subprotocols requested, so the request omits Sec-WebSocket-Protocol.
540+
const ws = new WebSocket(`ws://localhost:${server.address().port}`)
541+
542+
ws.onopen = reject
543+
544+
ws.addEventListener('error', ({ error }) => {
545+
t.assert.ok(error)
546+
server.close()
547+
resolve()
548+
})
549+
})
550+
})
551+
})

0 commit comments

Comments
 (0)