fix(desktop): validate gateway WebSocket URLs

This commit is contained in:
ethernet 2026-07-22 14:31:53 -04:00
parent 9160f10fc9
commit 02fa447f8e
2 changed files with 24 additions and 2 deletions

View file

@ -43,6 +43,12 @@ describe('JsonRpcGatewayClient connect() URL guard', () => {
)
})
it('rejects a malformed ws URL before opening a socket', async () => {
const client = new JsonRpcGatewayClient()
await expect(client.connect('ws://')).rejects.toThrow(/requires a ws:\/\/ or wss:\/\/ URL string/)
expect(client.connectionState).toBe('idle')
})
it('keeps connection state idle on rejection', async () => {
const client = new JsonRpcGatewayClient()
await client.connect(undefined as unknown as string).catch(() => undefined)

View file

@ -97,9 +97,25 @@ export class JsonRpcGatewayClient {
async connect(wsUrl: string): Promise<void> {
// Refuse garbage; WebSocket coerces non-strings into
// `ws://<origin>/[object%20Object]` (#68250 stale-emit boot loop).
if (typeof wsUrl !== 'string' || !/^wss?:\/\//i.test(wsUrl)) {
const invalidUrl = () => {
const got = typeof wsUrl === 'string' ? JSON.stringify(wsUrl) : `type "${typeof wsUrl}"`
throw new Error(`gateway connect() requires a ws:// or wss:// URL string, got ${got}`)
return new Error(`gateway connect() requires a ws:// or wss:// URL string, got ${got}`)
}
if (typeof wsUrl !== 'string') {
throw invalidUrl()
}
let url: URL
try {
url = new URL(wsUrl)
} catch {
throw invalidUrl()
}
if (url.protocol !== 'ws:' && url.protocol !== 'wss:') {
throw invalidUrl()
}
if (this.socket?.readyState === WebSocket.OPEN || this.state === 'connecting') {