mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-04 10:40:20 +00:00
fix(browser): fail closed browser auth bootstrap
This commit is contained in:
92
src/browser/server.auth-fail-closed.test.ts
Normal file
92
src/browser/server.auth-fail-closed.test.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { createServer, type AddressInfo } from "node:net";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
controlPort: 0,
|
||||
ensureBrowserControlAuth: vi.fn(async () => {
|
||||
throw new Error("read-only config");
|
||||
}),
|
||||
resolveBrowserControlAuth: vi.fn(() => ({})),
|
||||
ensureExtensionRelayForProfiles: vi.fn(async () => {}),
|
||||
}));
|
||||
|
||||
vi.mock("../config/config.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../config/config.js")>();
|
||||
return {
|
||||
...actual,
|
||||
loadConfig: () => ({
|
||||
browser: {
|
||||
enabled: true,
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./config.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./config.js")>();
|
||||
return {
|
||||
...actual,
|
||||
resolveBrowserConfig: vi.fn(() => ({
|
||||
enabled: true,
|
||||
controlPort: mocks.controlPort,
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./control-auth.js", () => ({
|
||||
ensureBrowserControlAuth: mocks.ensureBrowserControlAuth,
|
||||
resolveBrowserControlAuth: mocks.resolveBrowserControlAuth,
|
||||
}));
|
||||
|
||||
vi.mock("./routes/index.js", () => ({
|
||||
registerBrowserRoutes: vi.fn(() => {}),
|
||||
}));
|
||||
|
||||
vi.mock("./server-context.js", () => ({
|
||||
createBrowserRouteContext: vi.fn(() => ({})),
|
||||
}));
|
||||
|
||||
vi.mock("./server-lifecycle.js", () => ({
|
||||
ensureExtensionRelayForProfiles: mocks.ensureExtensionRelayForProfiles,
|
||||
stopKnownBrowserProfiles: vi.fn(async () => {}),
|
||||
}));
|
||||
|
||||
vi.mock("./pw-ai-state.js", () => ({
|
||||
isPwAiLoaded: vi.fn(() => false),
|
||||
}));
|
||||
|
||||
const { startBrowserControlServerFromConfig, stopBrowserControlServer } =
|
||||
await import("./server.js");
|
||||
|
||||
async function getFreePort(): Promise<number> {
|
||||
const probe = createServer();
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
probe.once("error", reject);
|
||||
probe.listen(0, "127.0.0.1", () => resolve());
|
||||
});
|
||||
const addr = probe.address() as AddressInfo;
|
||||
await new Promise<void>((resolve) => probe.close(() => resolve()));
|
||||
return addr.port;
|
||||
}
|
||||
|
||||
describe("browser control auth bootstrap failures", () => {
|
||||
beforeEach(async () => {
|
||||
mocks.controlPort = await getFreePort();
|
||||
mocks.ensureBrowserControlAuth.mockClear();
|
||||
mocks.resolveBrowserControlAuth.mockClear();
|
||||
mocks.ensureExtensionRelayForProfiles.mockClear();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await stopBrowserControlServer();
|
||||
});
|
||||
|
||||
it("fails closed when auth bootstrap throws and no auth is configured", async () => {
|
||||
const started = await startBrowserControlServerFromConfig();
|
||||
|
||||
expect(started).toBeNull();
|
||||
expect(mocks.ensureBrowserControlAuth).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.resolveBrowserControlAuth).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.ensureExtensionRelayForProfiles).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -30,6 +30,7 @@ export async function startBrowserControlServerFromConfig(): Promise<BrowserServ
|
||||
}
|
||||
|
||||
let browserAuth = resolveBrowserControlAuth(cfg);
|
||||
let browserAuthBootstrapFailed = false;
|
||||
try {
|
||||
const ensured = await ensureBrowserControlAuth({ cfg });
|
||||
browserAuth = ensured.auth;
|
||||
@@ -38,6 +39,16 @@ export async function startBrowserControlServerFromConfig(): Promise<BrowserServ
|
||||
}
|
||||
} catch (err) {
|
||||
logServer.warn(`failed to auto-configure browser auth: ${String(err)}`);
|
||||
browserAuthBootstrapFailed = true;
|
||||
}
|
||||
|
||||
// Fail closed: if auth bootstrap failed and no explicit auth is available,
|
||||
// do not start the browser control HTTP server.
|
||||
if (browserAuthBootstrapFailed && !browserAuth.token && !browserAuth.password) {
|
||||
logServer.error(
|
||||
"browser control startup aborted: authentication bootstrap failed and no fallback auth is configured.",
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const app = express();
|
||||
|
||||
Reference in New Issue
Block a user