fix: align browser and Codex ports with v6.11

This commit is contained in:
Dallin Romney
2026-07-14 04:57:00 -07:00
parent 998e4c4d23
commit 3e53c7adac
7 changed files with 38 additions and 5 deletions
+23 -2
View File
@@ -22,7 +22,7 @@ import {
import { CDP_HTTP_REQUEST_TIMEOUT_MS, CDP_WS_HANDSHAKE_TIMEOUT_MS } from "./cdp-timeouts.js";
import { BrowserCdpEndpointBlockedError } from "./errors.js";
import { resolveBrowserRateLimitMessage } from "./rate-limit-message.js";
import { withAllowedHostname } from "./ssrf-policy-helpers.js";
import { withAllowedHostname, withExactHostnamePolicy } from "./ssrf-policy-helpers.js";
import { normalizeBrowserTimerDelayMs } from "./timer-delay.js";
const CDP_URL_IN_TEXT_RE = /\b(?:https?|wss?):\/\/[^\s"'<>`]+/gi;
@@ -75,6 +75,17 @@ export function isDirectCdpWebSocketEndpoint(url: string): boolean {
/* c8 ignore stop */
}
/** Restricts discovered CDP endpoints to the configured control-plane host. */
export function scopeCdpPolicyToConfiguredEndpoint(
cdpUrl: string,
ssrfPolicy?: SsrFPolicy,
): SsrFPolicy | undefined {
if (!ssrfPolicy) {
return undefined;
}
return withExactHostnamePolicy(ssrfPolicy, new URL(cdpUrl).hostname);
}
export async function assertCdpEndpointAllowed(
cdpUrl: string,
ssrfPolicy?: SsrFPolicy,
@@ -116,6 +127,14 @@ export type CdpSendFn = (
sessionId?: string,
) => Promise<unknown>;
function decodeUrlUserInfo(value: string): string {
try {
return decodeURIComponent(value);
} catch {
return value;
}
}
function rawCdpMessageToString(data: WebSocket.RawData): string {
if (typeof data === "string") {
return data;
@@ -144,7 +163,9 @@ export function getHeadersWithAuth(url: string, headers: Record<string, string>
return mergedHeaders;
}
if (parsed.username || parsed.password) {
const auth = Buffer.from(`${parsed.username}:${parsed.password}`).toString("base64");
const username = decodeUrlUserInfo(parsed.username);
const password = decodeUrlUserInfo(parsed.password);
const auth = Buffer.from(`${username}:${password}`).toString("base64");
return { ...mergedHeaders, Authorization: `Basic ${auth}` };
}
} catch {
@@ -363,6 +363,7 @@ export async function diagnoseChromeCdp(
startedAt,
});
}
const cdpControlPolicy = scopeCdpPolicyToConfiguredEndpoint(cdpUrl, ssrfPolicy);
if (isDirectCdpWebSocketEndpoint(cdpUrl)) {
return await diagnoseCdpWebSocketEndpoint({
@@ -34,7 +34,6 @@ import {
isWebSocketUrl,
normalizeCdpHttpBaseForJsonEndpoints,
redactCdpErrorText,
scopeCdpPolicyToConfiguredEndpoint,
stripCdpUrlCredentials,
withCdpSocket,
} from "./cdp.helpers.js";
@@ -3,6 +3,7 @@ import { describe, expect, it, vi } from "vitest";
import type { BrowserRouteContext, ProfileContext } from "../server-context.js";
import "../../test-support/browser-security.mock.js";
import {
handleRouteError,
readBody,
resolveSafeRouteTabUrl,
resolveTargetIdFromBody,
@@ -14,3 +14,16 @@ export function withAllowedHostname(
allowedHostnames: uniqueStrings([...(ssrfPolicy?.allowedHostnames ?? []), hostname]),
};
}
/** Returns an SSRF policy restricted to one exact control-plane hostname. */
export function withExactHostnamePolicy(
ssrfPolicy: SsrFPolicy | undefined,
hostname: string,
): SsrFPolicy {
const { allowedOrigins: _allowedOrigins, ...basePolicy } = ssrfPolicy ?? {};
return {
...basePolicy,
allowedHostnames: [hostname],
hostnameAllowlist: [hostname],
};
}
@@ -16,7 +16,6 @@ import {
} from "openclaw/plugin-sdk/agent-harness-runtime";
import { normalizeTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
import { formatCodexDisplayText } from "../command-formatters.js";
import { resolveCodexToolAbortTerminalReason } from "./dynamic-tool-execution.js";
import {
approvalRequestExplicitlyUnavailable,
mapExecDecisionToOutcome,
@@ -42,7 +42,6 @@ import {
} from "./dynamic-tool-diagnostics.js";
import {
handleDynamicToolCallWithTimeout,
resolveCodexToolAbortTerminalReason,
resolveDynamicToolCallTimeoutMs,
} from "./dynamic-tool-execution.js";
import {