mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-03 03:03:24 -04:00
fix(browser): gate evaluate behind config flag
This commit is contained in:
@@ -14,7 +14,13 @@ function enhanceBrowserFetchError(url: string, err: unknown, timeoutMs: number):
|
||||
? "If this is a sandboxed session, ensure the sandbox browser is running and try again."
|
||||
: `Start (or restart) the Clawdbot gateway (Clawdbot.app menubar, or \`${formatCliCommand("clawdbot gateway")}\`) and try again.`;
|
||||
const msg = String(err);
|
||||
if (msg.toLowerCase().includes("timed out") || msg.toLowerCase().includes("timeout")) {
|
||||
const msgLower = msg.toLowerCase();
|
||||
const looksLikeTimeout =
|
||||
msgLower.includes("timed out") ||
|
||||
msgLower.includes("timeout") ||
|
||||
msgLower.includes("aborted") ||
|
||||
msgLower.includes("abort");
|
||||
if (looksLikeTimeout) {
|
||||
return new Error(
|
||||
`Can't reach the clawd browser control service (timed out after ${timeoutMs}ms). ${hint}`,
|
||||
);
|
||||
@@ -48,7 +54,7 @@ export async function fetchBrowserJson<T>(
|
||||
const timeoutMs = init?.timeoutMs ?? 5000;
|
||||
try {
|
||||
if (isAbsoluteHttp(url)) {
|
||||
return await fetchHttpJson<T>(url, init ? { ...init, timeoutMs } : { timeoutMs });
|
||||
return await fetchHttpJson<T>(url, { ...init, timeoutMs });
|
||||
}
|
||||
const started = await startBrowserControlServiceFromConfig();
|
||||
if (!started) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { resolveGatewayPort } from "../config/paths.js";
|
||||
import {
|
||||
DEFAULT_CLAWD_BROWSER_COLOR,
|
||||
DEFAULT_CLAWD_BROWSER_ENABLED,
|
||||
DEFAULT_BROWSER_EVALUATE_ENABLED,
|
||||
DEFAULT_BROWSER_DEFAULT_PROFILE_NAME,
|
||||
DEFAULT_CLAWD_BROWSER_PROFILE_NAME,
|
||||
} from "./constants.js";
|
||||
@@ -15,6 +16,7 @@ import { CDP_PORT_RANGE_START, getUsedPorts } from "./profiles.js";
|
||||
|
||||
export type ResolvedBrowserConfig = {
|
||||
enabled: boolean;
|
||||
evaluateEnabled: boolean;
|
||||
controlPort: number;
|
||||
cdpProtocol: "http" | "https";
|
||||
cdpHost: string;
|
||||
@@ -140,6 +142,7 @@ export function resolveBrowserConfig(
|
||||
rootConfig?: ClawdbotConfig,
|
||||
): ResolvedBrowserConfig {
|
||||
const enabled = cfg?.enabled ?? DEFAULT_CLAWD_BROWSER_ENABLED;
|
||||
const evaluateEnabled = cfg?.evaluateEnabled ?? DEFAULT_BROWSER_EVALUATE_ENABLED;
|
||||
const gatewayPort = resolveGatewayPort(rootConfig);
|
||||
const controlPort = deriveDefaultBrowserControlPort(gatewayPort ?? DEFAULT_BROWSER_CONTROL_PORT);
|
||||
const defaultColor = normalizeHexColor(cfg?.color);
|
||||
@@ -197,6 +200,7 @@ export function resolveBrowserConfig(
|
||||
|
||||
return {
|
||||
enabled,
|
||||
evaluateEnabled,
|
||||
controlPort,
|
||||
cdpProtocol,
|
||||
cdpHost: cdpInfo.parsed.hostname,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const DEFAULT_CLAWD_BROWSER_ENABLED = true;
|
||||
export const DEFAULT_BROWSER_EVALUATE_ENABLED = true;
|
||||
export const DEFAULT_CLAWD_BROWSER_COLOR = "#FF4500";
|
||||
export const DEFAULT_CLAWD_BROWSER_PROFILE_NAME = "clawd";
|
||||
export const DEFAULT_BROWSER_DEFAULT_PROFILE_NAME = "chrome";
|
||||
|
||||
@@ -39,6 +39,7 @@ export function registerBrowserAgentActRoutes(
|
||||
const cdpUrl = profileCtx.profile.cdpUrl;
|
||||
const pw = await requirePwAi(res, `act:${kind}`);
|
||||
if (!pw) return;
|
||||
const evaluateEnabled = ctx.state().resolved.evaluateEnabled;
|
||||
|
||||
switch (kind) {
|
||||
case "click": {
|
||||
@@ -210,6 +211,16 @@ export function registerBrowserAgentActRoutes(
|
||||
: undefined;
|
||||
const fn = toStringOrEmpty(body.fn) || undefined;
|
||||
const timeoutMs = toNumber(body.timeoutMs) ?? undefined;
|
||||
if (fn && !evaluateEnabled) {
|
||||
return jsonError(
|
||||
res,
|
||||
403,
|
||||
[
|
||||
"wait --fn is disabled by config (browser.evaluateEnabled=false).",
|
||||
"Docs: /gateway/configuration#browser-clawd-managed-browser",
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
if (
|
||||
timeMs === undefined &&
|
||||
!text &&
|
||||
@@ -240,6 +251,16 @@ export function registerBrowserAgentActRoutes(
|
||||
return res.json({ ok: true, targetId: tab.targetId });
|
||||
}
|
||||
case "evaluate": {
|
||||
if (!evaluateEnabled) {
|
||||
return jsonError(
|
||||
res,
|
||||
403,
|
||||
[
|
||||
"act:evaluate is disabled by config (browser.evaluateEnabled=false).",
|
||||
"Docs: /gateway/configuration#browser-clawd-managed-browser",
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
const fn = toStringOrEmpty(body.fn);
|
||||
if (!fn) return jsonError(res, 400, "fn is required");
|
||||
const ref = toStringOrEmpty(body.ref) || undefined;
|
||||
|
||||
@@ -7,6 +7,7 @@ let testPort = 0;
|
||||
let cdpBaseUrl = "";
|
||||
let reachable = false;
|
||||
let cfgAttachOnly = false;
|
||||
let cfgEvaluateEnabled = true;
|
||||
let createTargetId: string | null = null;
|
||||
let prevGatewayPort: string | undefined;
|
||||
|
||||
@@ -89,6 +90,7 @@ vi.mock("../config/config.js", async (importOriginal) => {
|
||||
loadConfig: () => ({
|
||||
browser: {
|
||||
enabled: true,
|
||||
evaluateEnabled: cfgEvaluateEnabled,
|
||||
color: "#FF4500",
|
||||
attachOnly: cfgAttachOnly,
|
||||
headless: true,
|
||||
@@ -185,6 +187,7 @@ describe("browser control server", () => {
|
||||
beforeEach(async () => {
|
||||
reachable = false;
|
||||
cfgAttachOnly = false;
|
||||
cfgEvaluateEnabled = true;
|
||||
createTargetId = null;
|
||||
|
||||
cdpMocks.createTargetViaCdp.mockImplementation(async () => {
|
||||
@@ -349,6 +352,30 @@ describe("browser control server", () => {
|
||||
slowTimeoutMs,
|
||||
);
|
||||
|
||||
it(
|
||||
"blocks act:evaluate when browser.evaluateEnabled=false",
|
||||
async () => {
|
||||
cfgEvaluateEnabled = false;
|
||||
const base = await startServerAndBase();
|
||||
|
||||
const waitRes = (await postJson(`${base}/act`, {
|
||||
kind: "wait",
|
||||
fn: "() => window.ready === true",
|
||||
})) as { error?: string };
|
||||
expect(waitRes.error).toContain("browser.evaluateEnabled=false");
|
||||
expect(pwMocks.waitForViaPlaywright).not.toHaveBeenCalled();
|
||||
|
||||
const res = (await postJson(`${base}/act`, {
|
||||
kind: "evaluate",
|
||||
fn: "() => 1",
|
||||
})) as { error?: string };
|
||||
|
||||
expect(res.error).toContain("browser.evaluateEnabled=false");
|
||||
expect(pwMocks.evaluateViaPlaywright).not.toHaveBeenCalled();
|
||||
},
|
||||
slowTimeoutMs,
|
||||
);
|
||||
|
||||
it("agent contract: hooks + response + downloads + screenshot", async () => {
|
||||
const base = await startServerAndBase();
|
||||
|
||||
|
||||
@@ -308,6 +308,8 @@ describe("backward compatibility (profile parameter)", () => {
|
||||
|
||||
testPort = await getFreePort();
|
||||
_cdpBaseUrl = `http://127.0.0.1:${testPort + 1}`;
|
||||
prevGatewayPort = process.env.CLAWDBOT_GATEWAY_PORT;
|
||||
process.env.CLAWDBOT_GATEWAY_PORT = String(testPort - 2);
|
||||
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
@@ -344,6 +346,11 @@ describe("backward compatibility (profile parameter)", () => {
|
||||
afterEach(async () => {
|
||||
vi.unstubAllGlobals();
|
||||
vi.restoreAllMocks();
|
||||
if (prevGatewayPort === undefined) {
|
||||
delete process.env.CLAWDBOT_GATEWAY_PORT;
|
||||
} else {
|
||||
process.env.CLAWDBOT_GATEWAY_PORT = prevGatewayPort;
|
||||
}
|
||||
const { stopBrowserControlServer } = await import("./server.js");
|
||||
await stopBrowserControlServer();
|
||||
});
|
||||
|
||||
@@ -285,6 +285,8 @@ describe("profile CRUD endpoints", () => {
|
||||
|
||||
testPort = await getFreePort();
|
||||
_cdpBaseUrl = `http://127.0.0.1:${testPort + 1}`;
|
||||
prevGatewayPort = process.env.CLAWDBOT_GATEWAY_PORT;
|
||||
process.env.CLAWDBOT_GATEWAY_PORT = String(testPort - 2);
|
||||
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
@@ -299,6 +301,11 @@ describe("profile CRUD endpoints", () => {
|
||||
afterEach(async () => {
|
||||
vi.unstubAllGlobals();
|
||||
vi.restoreAllMocks();
|
||||
if (prevGatewayPort === undefined) {
|
||||
delete process.env.CLAWDBOT_GATEWAY_PORT;
|
||||
} else {
|
||||
process.env.CLAWDBOT_GATEWAY_PORT = prevGatewayPort;
|
||||
}
|
||||
const { stopBrowserControlServer } = await import("./server.js");
|
||||
await stopBrowserControlServer();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user