fix(browser): rewrite 0.0.0.0 and [::] wildcard addresses in CDP WebSocket URLs

Containerized browsers (e.g. browserless in Docker) report
`ws://0.0.0.0:<internal-port>` in their `/json/version` response.
`normalizeCdpWsUrl` rewrites loopback WS hosts to the external
CDP host:port, but `0.0.0.0` and `[::]` were not treated as
addresses needing rewriting, causing OpenClaw to try connecting
to `ws://0.0.0.0:3000` literally — which always fails.

Fixes #17752

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Joe Harouni
2026-02-15 22:59:50 -06:00
committed by Peter Steinberger
parent 4bfa800cc7
commit dfa3605bee
2 changed files with 21 additions and 1 deletions

View File

@@ -320,6 +320,22 @@ describe("cdp", () => {
expect(normalized).toBe("wss://user:pass@example.com/devtools/browser/ABC?token=abc");
});
it("rewrites 0.0.0.0 wildcard bind address to remote CDP host", () => {
const normalized = normalizeCdpWsUrl(
"ws://0.0.0.0:3000/devtools/browser/ABC",
"http://192.168.1.202:18850?token=secret",
);
expect(normalized).toBe("ws://192.168.1.202:18850/devtools/browser/ABC?token=secret");
});
it("rewrites :: wildcard bind address to remote CDP host", () => {
const normalized = normalizeCdpWsUrl(
"ws://[::]:3000/devtools/browser/ABC",
"http://192.168.1.202:18850",
);
expect(normalized).toBe("ws://192.168.1.202:18850/devtools/browser/ABC");
});
it("upgrades ws to wss when CDP uses https", () => {
const normalized = normalizeCdpWsUrl(
"ws://production-sfo.browserless.io",

View File

@@ -19,7 +19,11 @@ export {
export function normalizeCdpWsUrl(wsUrl: string, cdpUrl: string): string {
const ws = new URL(wsUrl);
const cdp = new URL(cdpUrl);
if (isLoopbackHost(ws.hostname) && !isLoopbackHost(cdp.hostname)) {
// Treat 0.0.0.0 and :: as wildcard bind addresses that need rewriting.
// Containerized browsers (e.g. browserless) report ws://0.0.0.0:<internal-port>
// in /json/version — these must be rewritten to the external cdpUrl host:port.
const isWildcardBind = ws.hostname === "0.0.0.0" || ws.hostname === "[::]";
if ((isLoopbackHost(ws.hostname) || isWildcardBind) && !isLoopbackHost(cdp.hostname)) {
ws.hostname = cdp.hostname;
const cdpPort = cdp.port || (cdp.protocol === "https:" ? "443" : "80");
if (cdpPort) {