Files
openclaw/src/agents/tools/gateway.e2e.test.ts
2026-02-19 14:37:56 +01:00

82 lines
2.6 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import { callGatewayTool, resolveGatewayOptions } from "./gateway.js";
const callGatewayMock = vi.fn();
vi.mock("../../config/config.js", () => ({
loadConfig: () => ({}),
resolveGatewayPort: () => 18789,
}));
vi.mock("../../gateway/call.js", () => ({
callGateway: (...args: unknown[]) => callGatewayMock(...args),
}));
describe("gateway tool defaults", () => {
beforeEach(() => {
callGatewayMock.mockReset();
});
it("leaves url undefined so callGateway can use config", () => {
const opts = resolveGatewayOptions();
expect(opts.url).toBeUndefined();
});
it("accepts allowlisted gatewayUrl overrides (SSRF hardening)", async () => {
callGatewayMock.mockResolvedValueOnce({ ok: true });
await callGatewayTool(
"health",
{ gatewayUrl: "ws://127.0.0.1:18789", gatewayToken: "t", timeoutMs: 5000 },
{},
);
expect(callGatewayMock).toHaveBeenCalledWith(
expect.objectContaining({
url: "ws://127.0.0.1:18789",
token: "t",
timeoutMs: 5000,
scopes: ["operator.read"],
}),
);
});
it("uses least-privilege write scope for write methods", async () => {
callGatewayMock.mockResolvedValueOnce({ ok: true });
await callGatewayTool("wake", {}, { mode: "now", text: "hi" });
expect(callGatewayMock).toHaveBeenCalledWith(
expect.objectContaining({
method: "wake",
scopes: ["operator.write"],
}),
);
});
it("uses admin scope only for admin methods", async () => {
callGatewayMock.mockResolvedValueOnce({ ok: true });
await callGatewayTool("cron.add", {}, { id: "job-1" });
expect(callGatewayMock).toHaveBeenCalledWith(
expect.objectContaining({
method: "cron.add",
scopes: ["operator.admin"],
}),
);
});
it("default-denies unknown methods by sending no scopes", async () => {
callGatewayMock.mockResolvedValueOnce({ ok: true });
await callGatewayTool("nonexistent.method", {}, {});
expect(callGatewayMock).toHaveBeenCalledWith(
expect.objectContaining({
method: "nonexistent.method",
scopes: [],
}),
);
});
it("rejects non-allowlisted overrides (SSRF hardening)", async () => {
await expect(
callGatewayTool("health", { gatewayUrl: "ws://127.0.0.1:8080", gatewayToken: "t" }, {}),
).rejects.toThrow(/gatewayUrl override rejected/i);
await expect(
callGatewayTool("health", { gatewayUrl: "ws://169.254.169.254", gatewayToken: "t" }, {}),
).rejects.toThrow(/gatewayUrl override rejected/i);
});
});