mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 11:38:38 +00:00
fix: test browser.request profile body fallback (#28852) (thanks @Sid-Qin)
This commit is contained in:
@@ -39,6 +39,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
- Security/Prompt spoofing hardening: stop injecting queued runtime events into user-role prompt text, route them through trusted system-prompt context, and neutralize inbound spoof markers like `[System Message]` and line-leading `System:` in untrusted message content. (#30448)
|
- Security/Prompt spoofing hardening: stop injecting queued runtime events into user-role prompt text, route them through trusted system-prompt context, and neutralize inbound spoof markers like `[System Message]` and line-leading `System:` in untrusted message content. (#30448)
|
||||||
|
- Gateway/Node browser proxy routing: honor `profile` from `browser.request` JSON body when query params omit it, while preserving query-profile precedence when both are present. (#28852) Thanks @Sid-Qin.
|
||||||
- Browser/Remote CDP ownership checks: skip local-process ownership errors for non-loopback remote CDP profiles when HTTP is reachable but the websocket handshake fails, and surface the remote websocket attach/retry path instead. (#15582) Landed from contributor (#28780) Thanks @stubbi, @bsormagec, @unblockedgamesstudio and @vincentkoc.
|
- Browser/Remote CDP ownership checks: skip local-process ownership errors for non-loopback remote CDP profiles when HTTP is reachable but the websocket handshake fails, and surface the remote websocket attach/retry path instead. (#15582) Landed from contributor (#28780) Thanks @stubbi, @bsormagec, @unblockedgamesstudio and @vincentkoc.
|
||||||
- Docker/Image health checks: add Dockerfile `HEALTHCHECK` that probes gateway `GET /healthz` so container runtimes can mark unhealthy instances without requiring auth credentials in the probe command. (#11478) Thanks @U-C4N and @vincentkoc.
|
- Docker/Image health checks: add Dockerfile `HEALTHCHECK` that probes gateway `GET /healthz` so container runtimes can mark unhealthy instances without requiring auth credentials in the probe command. (#11478) Thanks @U-C4N and @vincentkoc.
|
||||||
- Daemon/systemd checks in containers: treat missing `systemctl` invocations (including `spawn systemctl ENOENT`/`EACCES`) as unavailable service state during `is-enabled` checks, preventing container flows from failing with `Gateway service check failed` before install/status handling can continue. (#26089) Thanks @sahilsatralkar and @vincentkoc.
|
- Daemon/systemd checks in containers: treat missing `systemctl` invocations (including `spawn systemctl ENOENT`/`EACCES`) as unavailable service state during `is-enabled` checks, preventing container flows from failing with `Gateway service check failed` before install/status handling can continue. (#26089) Thanks @sahilsatralkar and @vincentkoc.
|
||||||
|
|||||||
103
src/gateway/server-methods/browser.profile-from-body.test.ts
Normal file
103
src/gateway/server-methods/browser.profile-from-body.test.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
const { loadConfigMock, isNodeCommandAllowedMock, resolveNodeCommandAllowlistMock } = vi.hoisted(
|
||||||
|
() => ({
|
||||||
|
loadConfigMock: vi.fn(),
|
||||||
|
isNodeCommandAllowedMock: vi.fn(),
|
||||||
|
resolveNodeCommandAllowlistMock: vi.fn(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
vi.mock("../../config/config.js", () => ({
|
||||||
|
loadConfig: loadConfigMock,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("../node-command-policy.js", () => ({
|
||||||
|
isNodeCommandAllowed: isNodeCommandAllowedMock,
|
||||||
|
resolveNodeCommandAllowlist: resolveNodeCommandAllowlistMock,
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { browserHandlers } from "./browser.js";
|
||||||
|
|
||||||
|
type RespondCall = [boolean, unknown?, { code: number; message: string }?];
|
||||||
|
|
||||||
|
function createContext() {
|
||||||
|
const invoke = vi.fn(async () => ({
|
||||||
|
ok: true,
|
||||||
|
payload: {
|
||||||
|
result: { ok: true },
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
const listConnected = vi.fn(() => [
|
||||||
|
{
|
||||||
|
nodeId: "node-1",
|
||||||
|
caps: ["browser"],
|
||||||
|
commands: ["browser.proxy"],
|
||||||
|
platform: "linux",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
return {
|
||||||
|
invoke,
|
||||||
|
listConnected,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runBrowserRequest(params: Record<string, unknown>) {
|
||||||
|
const respond = vi.fn();
|
||||||
|
const nodeRegistry = createContext();
|
||||||
|
await browserHandlers["browser.request"]({
|
||||||
|
params,
|
||||||
|
respond: respond as never,
|
||||||
|
context: { nodeRegistry } as never,
|
||||||
|
client: null,
|
||||||
|
req: { type: "req", id: "req-1", method: "browser.request" },
|
||||||
|
isWebchatConnect: () => false,
|
||||||
|
});
|
||||||
|
return { respond, nodeRegistry };
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("browser.request profile selection", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
loadConfigMock.mockReturnValue({
|
||||||
|
gateway: { nodes: { browser: { mode: "auto" } } },
|
||||||
|
});
|
||||||
|
resolveNodeCommandAllowlistMock.mockReturnValue([]);
|
||||||
|
isNodeCommandAllowedMock.mockReturnValue({ ok: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses profile from request body when query profile is missing", async () => {
|
||||||
|
const { respond, nodeRegistry } = await runBrowserRequest({
|
||||||
|
method: "POST",
|
||||||
|
path: "/act",
|
||||||
|
body: { profile: "work", request: { action: "click", ref: "btn1" } },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(nodeRegistry.invoke).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
command: "browser.proxy",
|
||||||
|
params: expect.objectContaining({
|
||||||
|
profile: "work",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const call = respond.mock.calls[0] as RespondCall | undefined;
|
||||||
|
expect(call?.[0]).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("prefers query profile over body profile when both are present", async () => {
|
||||||
|
const { nodeRegistry } = await runBrowserRequest({
|
||||||
|
method: "POST",
|
||||||
|
path: "/act",
|
||||||
|
query: { profile: "chrome" },
|
||||||
|
body: { profile: "work", request: { action: "click", ref: "btn1" } },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(nodeRegistry.invoke).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
params: expect.objectContaining({
|
||||||
|
profile: "chrome",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user