mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 11:30:43 +00:00
test: tighten gateway tool assertions
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { CallGatewayScopedOptions } from "../../gateway/call.js";
|
||||
import { callGatewayTool, resolveGatewayOptions } from "./gateway.js";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
@@ -15,6 +16,15 @@ vi.mock("../../gateway/call.js", () => ({
|
||||
callGateway: (...args: unknown[]) => mocks.callGateway(...args),
|
||||
}));
|
||||
|
||||
function capturedGatewayCall(): CallGatewayScopedOptions {
|
||||
expect(mocks.callGateway).toHaveBeenCalledTimes(1);
|
||||
const call = mocks.callGateway.mock.calls[0];
|
||||
if (!call) {
|
||||
throw new Error("expected callGateway to be called");
|
||||
}
|
||||
return call[0] as CallGatewayScopedOptions;
|
||||
}
|
||||
|
||||
describe("gateway tool defaults", () => {
|
||||
const envSnapshot = {
|
||||
openclaw: process.env.OPENCLAW_GATEWAY_TOKEN,
|
||||
@@ -46,14 +56,13 @@ describe("gateway tool defaults", () => {
|
||||
{ gatewayUrl: "ws://127.0.0.1:18789", gatewayToken: "t", timeoutMs: 5000 },
|
||||
{},
|
||||
);
|
||||
expect(mocks.callGateway).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: "ws://127.0.0.1:18789",
|
||||
token: "t",
|
||||
timeoutMs: 5000,
|
||||
scopes: ["operator.read"],
|
||||
}),
|
||||
);
|
||||
const call = capturedGatewayCall();
|
||||
expect(call.method).toBe("health");
|
||||
expect(call.params).toEqual({});
|
||||
expect(call.url).toBe("ws://127.0.0.1:18789");
|
||||
expect(call.token).toBe("t");
|
||||
expect(call.timeoutMs).toBe(5000);
|
||||
expect(call.scopes).toEqual(["operator.read"]);
|
||||
});
|
||||
|
||||
it("uses OPENCLAW_GATEWAY_TOKEN for allowlisted local overrides", () => {
|
||||
@@ -142,23 +151,19 @@ describe("gateway tool defaults", () => {
|
||||
it("uses least-privilege write scope for write methods", async () => {
|
||||
mocks.callGateway.mockResolvedValueOnce({ ok: true });
|
||||
await callGatewayTool("wake", {}, { mode: "now", text: "hi" });
|
||||
expect(mocks.callGateway).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "wake",
|
||||
scopes: ["operator.write"],
|
||||
}),
|
||||
);
|
||||
const call = capturedGatewayCall();
|
||||
expect(call.method).toBe("wake");
|
||||
expect(call.params).toEqual({ mode: "now", text: "hi" });
|
||||
expect(call.scopes).toEqual(["operator.write"]);
|
||||
});
|
||||
|
||||
it("uses admin scope only for admin methods", async () => {
|
||||
mocks.callGateway.mockResolvedValueOnce({ ok: true });
|
||||
await callGatewayTool("cron.add", {}, { id: "job-1" });
|
||||
expect(mocks.callGateway).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "cron.add",
|
||||
scopes: ["operator.admin"],
|
||||
}),
|
||||
);
|
||||
const call = capturedGatewayCall();
|
||||
expect(call.method).toBe("cron.add");
|
||||
expect(call.params).toEqual({ id: "job-1" });
|
||||
expect(call.scopes).toEqual(["operator.admin"]);
|
||||
});
|
||||
|
||||
it("allows explicit scope overrides for dynamic callers", async () => {
|
||||
@@ -169,23 +174,19 @@ describe("gateway tool defaults", () => {
|
||||
{ requestId: "req-1" },
|
||||
{ scopes: ["operator.admin"] },
|
||||
);
|
||||
expect(mocks.callGateway).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "node.pair.approve",
|
||||
scopes: ["operator.admin"],
|
||||
}),
|
||||
);
|
||||
const call = capturedGatewayCall();
|
||||
expect(call.method).toBe("node.pair.approve");
|
||||
expect(call.params).toEqual({ requestId: "req-1" });
|
||||
expect(call.scopes).toEqual(["operator.admin"]);
|
||||
});
|
||||
|
||||
it("default-denies unknown methods by sending no scopes", async () => {
|
||||
mocks.callGateway.mockResolvedValueOnce({ ok: true });
|
||||
await callGatewayTool("nonexistent.method", {}, {});
|
||||
expect(mocks.callGateway).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "nonexistent.method",
|
||||
scopes: [],
|
||||
}),
|
||||
);
|
||||
const call = capturedGatewayCall();
|
||||
expect(call.method).toBe("nonexistent.method");
|
||||
expect(call.params).toEqual({});
|
||||
expect(call.scopes).toEqual([]);
|
||||
});
|
||||
|
||||
it("rejects non-allowlisted overrides (SSRF hardening)", async () => {
|
||||
|
||||
Reference in New Issue
Block a user