mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-04 14:50:24 +00:00
* fix(sandbox): honor effective sandbox alsoAllow policy * fix(sandbox): prefer resolved sandbox context policy * fix: honor sandbox alsoAllow policy (#54492) (thanks @ngutman)
165 lines
4.1 KiB
TypeScript
165 lines
4.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { OpenClawConfig } from "../config/config.js";
|
|
import { createOpenClawCodingTools } from "./pi-tools.js";
|
|
import { resolveSandboxConfigForAgent } from "./sandbox/config.js";
|
|
import { createHostSandboxFsBridge } from "./test-helpers/host-sandbox-fs-bridge.js";
|
|
import { createPiToolsSandboxContext } from "./test-helpers/pi-tools-sandbox-context.js";
|
|
|
|
function listToolNames(params: {
|
|
cfg: OpenClawConfig;
|
|
agentId?: string;
|
|
sessionKey?: string;
|
|
sandboxAgentId?: string;
|
|
}): string[] {
|
|
const workspaceDir = "/tmp/openclaw-sandbox-policy";
|
|
const sessionKey = params.sessionKey ?? "agent:tavern:main";
|
|
const sandboxAgentId = params.sandboxAgentId ?? params.agentId ?? "tavern";
|
|
const sandbox = createPiToolsSandboxContext({
|
|
workspaceDir,
|
|
fsBridge: createHostSandboxFsBridge(workspaceDir),
|
|
sessionKey,
|
|
tools: resolveSandboxConfigForAgent(params.cfg, sandboxAgentId).tools,
|
|
});
|
|
return createOpenClawCodingTools({
|
|
config: params.cfg,
|
|
agentId: params.agentId,
|
|
sessionKey,
|
|
sandbox,
|
|
workspaceDir,
|
|
})
|
|
.map((tool) => tool.name)
|
|
.toSorted();
|
|
}
|
|
|
|
describe("pi-tools sandbox policy", () => {
|
|
it("re-exposes omitted sandbox tools via sandbox alsoAllow", () => {
|
|
const names = listToolNames({
|
|
cfg: {
|
|
agents: {
|
|
defaults: {
|
|
sandbox: { mode: "all", scope: "agent" },
|
|
},
|
|
list: [
|
|
{
|
|
id: "tavern",
|
|
tools: {
|
|
sandbox: {
|
|
tools: {
|
|
alsoAllow: ["message", "tts"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
} as OpenClawConfig,
|
|
});
|
|
|
|
expect(names).toContain("message");
|
|
expect(names).toContain("tts");
|
|
});
|
|
|
|
it("re-enables default-denied sandbox tools when explicitly allowed", () => {
|
|
const names = listToolNames({
|
|
cfg: {
|
|
agents: {
|
|
defaults: {
|
|
sandbox: { mode: "all", scope: "agent" },
|
|
},
|
|
list: [{ id: "tavern" }],
|
|
},
|
|
tools: {
|
|
sandbox: {
|
|
tools: {
|
|
allow: ["browser"],
|
|
},
|
|
},
|
|
},
|
|
} as OpenClawConfig,
|
|
});
|
|
|
|
expect(names).toContain("browser");
|
|
});
|
|
|
|
it("prefers the resolved sandbox context policy for legacy main session aliases", () => {
|
|
const cfg = {
|
|
agents: {
|
|
defaults: {
|
|
sandbox: { mode: "all", scope: "agent" },
|
|
},
|
|
list: [
|
|
{
|
|
id: "tavern",
|
|
default: true,
|
|
tools: {
|
|
sandbox: {
|
|
tools: {
|
|
allow: ["browser"],
|
|
alsoAllow: ["message"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
} as OpenClawConfig;
|
|
|
|
const names = listToolNames({
|
|
cfg,
|
|
sessionKey: "main",
|
|
sandboxAgentId: "tavern",
|
|
});
|
|
|
|
expect(names).toContain("browser");
|
|
expect(names).toContain("message");
|
|
});
|
|
|
|
it("preserves allow-all semantics for allow: [] plus alsoAllow", () => {
|
|
const names = listToolNames({
|
|
cfg: {
|
|
agents: {
|
|
defaults: {
|
|
sandbox: { mode: "all", scope: "agent" },
|
|
},
|
|
list: [{ id: "tavern" }],
|
|
},
|
|
tools: {
|
|
sandbox: {
|
|
tools: {
|
|
allow: [],
|
|
alsoAllow: ["browser"],
|
|
},
|
|
},
|
|
},
|
|
} as OpenClawConfig,
|
|
});
|
|
|
|
expect(names).toContain("browser");
|
|
expect(names).toContain("read");
|
|
});
|
|
|
|
it("keeps explicit sandbox deny precedence over explicit allow", () => {
|
|
const names = listToolNames({
|
|
cfg: {
|
|
agents: {
|
|
defaults: {
|
|
sandbox: { mode: "all", scope: "agent" },
|
|
},
|
|
list: [{ id: "tavern" }],
|
|
},
|
|
tools: {
|
|
sandbox: {
|
|
tools: {
|
|
allow: ["browser", "message"],
|
|
deny: ["browser"],
|
|
},
|
|
},
|
|
},
|
|
} as OpenClawConfig,
|
|
});
|
|
|
|
expect(names).not.toContain("browser");
|
|
expect(names).toContain("message");
|
|
});
|
|
});
|