Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions src/shared/utils/wsPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,75 @@ export function deriveLiveWsPath(publicUrl?: string): string {
}
}

/**
* Validate the live WebSocket port reported by the handshake endpoint.
*
* The WebSocket server exposes its actual listening port, which may differ from
* the compiled-in default. Only a valid TCP port should ever override the
* default URL.
*/
export function sanitizeLiveWsPort(port: unknown): number | null {
if (typeof port === "number") {
if (!Number.isInteger(port) || port < 1 || port > 65535) return null;
return port;
}

if (typeof port === "string") {
const trimmed = port.trim();
if (!/^\d+$/.test(trimmed)) return null;
const numericPort = Number(trimmed);
if (!Number.isInteger(numericPort) || numericPort < 1 || numericPort > 65535) return null;
return numericPort;
}

return null;
}

/**
* Resolve the browser's live dashboard WebSocket URL from the handshake values.
*
* Priority:
* 1. explicit wsUrl passed by the caller
* 2. publicUrl reported by the handshake
* 3. default URL with the runtime port and path applied
* 4. the original default URL
*/
export function resolveLiveWsUrl({
explicit,
handshakeUrl,
handshakePort,
handshakePath,
defaultUrl,
}: {
explicit?: string;
handshakeUrl?: string | null;
handshakePort?: number | string | null;
handshakePath?: string | null;
defaultUrl: string;
}): string {
if (typeof explicit === "string") {
const trimmed = explicit.trim();
if (trimmed.startsWith("ws://") || trimmed.startsWith("wss://")) return trimmed;
}

if (typeof handshakeUrl === "string") {
const trimmed = handshakeUrl.trim();
if (trimmed.startsWith("ws://") || trimmed.startsWith("wss://")) return trimmed;
}

try {
const parsed = new URL(defaultUrl);
const sanitizedPort = sanitizeLiveWsPort(handshakePort);
if (sanitizedPort !== null) parsed.port = String(sanitizedPort);
if (typeof handshakePath === "string" && handshakePath.startsWith("/")) {
parsed.pathname = handshakePath;
}
return parsed.toString();
} catch {
return defaultUrl;
}
}

/**
* The operator-declared public WebSocket URL, resolved at RUNTIME.
*
Expand Down
52 changes: 49 additions & 3 deletions tests/unit/live-ws-url-11331.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,20 @@ describe("sanitizeLiveWsPort", () => {
assert.equal(sanitizeLiveWsPort(value), null, `expected null for ${String(value)}`);
}
});

it("rejects hexadecimal numeric strings", () => {
assert.equal(sanitizeLiveWsPort("0x10"), null);
});

it("rejects scientific-notation numeric strings", () => {
assert.equal(sanitizeLiveWsPort("1e3"), null);
});
});

describe("resolveLiveWsUrl", () => {
it("uses the port the handshake reports instead of the compiled-in one", () => {
const url = resolveLiveWsUrl({ handshakePort: 20140, defaultUrl: DEFAULT_URL });

assert.equal(new URL(url).port, "20140");
assert.equal(new URL(url).hostname, "omniroute.example.tld");
assert.equal(new URL(url).pathname, "/live-ws");
Expand All @@ -51,14 +60,25 @@ describe("resolveLiveWsUrl", () => {

it("applies the port and the path together", () => {
const url = new URL(
resolveLiveWsUrl({ handshakePort: 9443, handshakePath: "/ws/live", defaultUrl: DEFAULT_URL })
resolveLiveWsUrl({
handshakePort: 9443,
handshakePath: "/ws/live",
defaultUrl: DEFAULT_URL,
})
);

assert.equal(url.port, "9443");
assert.equal(url.pathname, "/ws/live");
});

it("ignores a path that is not a path", () => {
const url = new URL(resolveLiveWsUrl({ handshakePath: "live-ws", defaultUrl: DEFAULT_URL }));
const url = new URL(
resolveLiveWsUrl({
handshakePath: "live-ws",
defaultUrl: DEFAULT_URL,
})
);

assert.equal(url.pathname, "/live-ws");
});

Expand All @@ -85,8 +105,34 @@ describe("resolveLiveWsUrl", () => {
);
});

it("rejects an explicit non-websocket URL", () => {
assert.equal(
resolveLiveWsUrl({
explicit: "http://weird",
defaultUrl: DEFAULT_URL,
}),
DEFAULT_URL
);
});

it("rejects a handshake URL that is not a websocket URL", () => {
assert.equal(
resolveLiveWsUrl({
handshakeUrl: "https://nope",
defaultUrl: DEFAULT_URL,
}),
DEFAULT_URL
);
});

it("falls back to the default rather than throwing on an unparseable default", () => {
assert.equal(resolveLiveWsUrl({ handshakePort: 20140, defaultUrl: "not a url" }), "not a url");
assert.equal(
resolveLiveWsUrl({
handshakePort: 20140,
defaultUrl: "not a url",
}),
"not a url"
);
});

it("leaves deriveLiveWsPath alone", () => {
Expand Down