mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 15:30:39 +00:00
* config(browser): add cdpPortRangeStart type * config(schema): validate browser.cdpPortRangeStart * config(labels): add browser.cdpPortRangeStart label * config(help): document browser.cdpPortRangeStart * browser(config): resolve custom cdp port range start * browser(profiles): allocate ports from resolved CDP range * test(browser): cover cdpPortRangeStart config behavior * test(browser): cover cdpPortRangeStart profile allocation * test(browser): include CDP range fields in remote tab harness * test(browser): include CDP range fields in ensure-tab harness * test(browser): include CDP range fields in bridge auth config * build(browser): add resolved CDP range metadata * fix(browser): fallback CDP port allocation to derived range * test(browser): cover missing resolved CDP range fallback * fix(browser): remove duplicate resolved CDP range fields * fix(agents): provide resolved CDP range in sandbox browser config * chore(browser): format sandbox bridge resolved config * chore(browser): reformat sandbox imports to satisfy oxfmt
112 lines
3.4 KiB
TypeScript
112 lines
3.4 KiB
TypeScript
import { afterEach, describe, expect, it } from "vitest";
|
|
import { startBrowserBridgeServer, stopBrowserBridgeServer } from "./bridge-server.js";
|
|
import type { ResolvedBrowserConfig } from "./config.js";
|
|
import {
|
|
DEFAULT_OPENCLAW_BROWSER_COLOR,
|
|
DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME,
|
|
} from "./constants.js";
|
|
|
|
function buildResolvedConfig(): ResolvedBrowserConfig {
|
|
return {
|
|
enabled: true,
|
|
evaluateEnabled: false,
|
|
controlPort: 0,
|
|
cdpPortRangeStart: 18800,
|
|
cdpPortRangeEnd: 18899,
|
|
cdpProtocol: "http",
|
|
cdpHost: "127.0.0.1",
|
|
cdpIsLoopback: true,
|
|
remoteCdpTimeoutMs: 1500,
|
|
remoteCdpHandshakeTimeoutMs: 3000,
|
|
extraArgs: [],
|
|
color: DEFAULT_OPENCLAW_BROWSER_COLOR,
|
|
executablePath: undefined,
|
|
headless: true,
|
|
noSandbox: false,
|
|
attachOnly: true,
|
|
defaultProfile: DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME,
|
|
profiles: {
|
|
[DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME]: {
|
|
cdpPort: 1,
|
|
color: DEFAULT_OPENCLAW_BROWSER_COLOR,
|
|
},
|
|
},
|
|
} as unknown as ResolvedBrowserConfig;
|
|
}
|
|
|
|
describe("startBrowserBridgeServer auth", () => {
|
|
const servers: Array<{ stop: () => Promise<void> }> = [];
|
|
|
|
async function expectAuthFlow(
|
|
authConfig: { authToken?: string; authPassword?: string },
|
|
headers: Record<string, string>,
|
|
) {
|
|
const bridge = await startBrowserBridgeServer({
|
|
resolved: buildResolvedConfig(),
|
|
...authConfig,
|
|
});
|
|
servers.push({ stop: () => stopBrowserBridgeServer(bridge.server) });
|
|
|
|
const unauth = await fetch(`${bridge.baseUrl}/`);
|
|
expect(unauth.status).toBe(401);
|
|
|
|
const authed = await fetch(`${bridge.baseUrl}/`, { headers });
|
|
expect(authed.status).toBe(200);
|
|
}
|
|
|
|
afterEach(async () => {
|
|
while (servers.length) {
|
|
const s = servers.pop();
|
|
if (s) {
|
|
await s.stop();
|
|
}
|
|
}
|
|
});
|
|
|
|
it("rejects unauthenticated requests when authToken is set", async () => {
|
|
await expectAuthFlow({ authToken: "secret-token" }, { Authorization: "Bearer secret-token" });
|
|
});
|
|
|
|
it("accepts x-openclaw-password when authPassword is set", async () => {
|
|
await expectAuthFlow(
|
|
{ authPassword: "secret-password" },
|
|
{ "x-openclaw-password": "secret-password" },
|
|
);
|
|
});
|
|
|
|
it("requires auth params", async () => {
|
|
await expect(
|
|
startBrowserBridgeServer({
|
|
resolved: buildResolvedConfig(),
|
|
}),
|
|
).rejects.toThrow(/requires auth/i);
|
|
});
|
|
|
|
it("serves noVNC bootstrap html without leaking password in Location header", async () => {
|
|
const bridge = await startBrowserBridgeServer({
|
|
resolved: buildResolvedConfig(),
|
|
authToken: "secret-token",
|
|
resolveSandboxNoVncToken: (token) => {
|
|
if (token !== "valid-token") {
|
|
return null;
|
|
}
|
|
return { noVncPort: 45678, password: "Abc123xy" };
|
|
},
|
|
});
|
|
servers.push({ stop: () => stopBrowserBridgeServer(bridge.server) });
|
|
|
|
const res = await fetch(`${bridge.baseUrl}/sandbox/novnc?token=valid-token`);
|
|
expect(res.status).toBe(200);
|
|
expect(res.headers.get("location")).toBeNull();
|
|
expect(res.headers.get("cache-control")).toContain("no-store");
|
|
expect(res.headers.get("referrer-policy")).toBe("no-referrer");
|
|
|
|
const body = await res.text();
|
|
expect(body).toContain("window.location.replace");
|
|
expect(body).toContain(
|
|
"http://127.0.0.1:45678/vnc.html#autoconnect=1&resize=remote&password=Abc123xy",
|
|
);
|
|
expect(body).not.toContain("?password=");
|
|
});
|
|
});
|