From ff1159dab381404a267e2d7bad5f2f3f96f2b8c2 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Thu, 30 Jul 2026 14:09:15 +0800 Subject: [PATCH] fix(ws): prevent host message self-delivery --- package.json | 1 + src/sync/ws/server.test.ts | 68 ++++++++++++++++++++++++++++++++++++++ src/sync/ws/server.ts | 2 +- 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/sync/ws/server.test.ts diff --git a/package.json b/package.json index 92a55fc..086491b 100644 --- a/package.json +++ b/package.json @@ -570,6 +570,7 @@ }, "scripts": { "build": "tsdown --env.NODE_ENV production --minify --clean", + "test": "tsx --test src/**/*.test.ts", "dev": "tsdown --watch --env.NODE_ENV development", "typecheck": "tsc --noEmit", "vscode:prepublish": "pnpm run build", diff --git a/src/sync/ws/server.test.ts b/src/sync/ws/server.test.ts new file mode 100644 index 0000000..eeb60ef --- /dev/null +++ b/src/sync/ws/server.test.ts @@ -0,0 +1,68 @@ +import type { DownlinkMessageContent } from './protocol.ts' +import assert from 'node:assert/strict' +// eslint-disable-next-line test/no-import-node-test +import { describe, it } from 'node:test' +import { createServer } from './server.ts' + +const hostId = 'host' +const guestId = 'guest' +const roomId = 'room' +const terminalOutput = '\x1B]3008;start=abc;type=shell\x1B\\\x1B[?2004h\x1B[32m$ \x1B[0m' + +describe('WebSocketSignalingServer host routing', () => { + function setup() { + const hostMessages: DownlinkMessageContent[] = [] + const server = createServer({ + port: 0, + hostname: '127.0.0.1', + hostMode: { + roomId, + hostId, + onHostMessage: message => hostMessages.push(message), + }, + }) + return { hostMessages, server } + } + + it('does not deliver host broadcasts back to the host', () => { + const { hostMessages, server } = setup() + + server.handleMessage({ action: 'terminal', data: terminalOutput }, roomId, hostId) + + assert.deepEqual(hostMessages, []) + }) + + it('does not deliver explicitly self-targeted host messages', () => { + const { hostMessages, server } = setup() + + server.handleMessage({ action: 'terminal', data: terminalOutput, targetPeers: hostId }, roomId, hostId) + + assert.deepEqual(hostMessages, []) + }) + + it('delivers guest broadcasts to the host without changing terminal data', () => { + const { hostMessages, server } = setup() + + server.handleMessage({ action: 'terminal', data: terminalOutput }, roomId, guestId) + + assert.deepEqual(hostMessages, [{ + action: 'terminal', + data: terminalOutput, + peerId: guestId, + metadata: undefined, + }]) + }) + + it('delivers guest messages targeted at the host', () => { + const { hostMessages, server } = setup() + + server.handleMessage({ action: 'terminal', data: 'ls\r', targetPeers: hostId }, roomId, guestId) + + assert.deepEqual(hostMessages, [{ + action: 'terminal', + data: 'ls\r', + peerId: guestId, + metadata: undefined, + }]) + }) +}) diff --git a/src/sync/ws/server.ts b/src/sync/ws/server.ts index 41f3aa2..651839f 100644 --- a/src/sync/ws/server.ts +++ b/src/sync/ws/server.ts @@ -172,7 +172,7 @@ class WebSocketSignalingServer { : undefined if (this.options.hostMode) { - if (!targets || targets.includes(this.options.hostMode.hostId)) { + if (senderId !== this.options.hostMode.hostId && (!targets || targets.includes(this.options.hostMode.hostId))) { this.options.hostMode.onHostMessage(downlinkPayload) } }