mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 09:51:22 +00:00
Exec: fail closed when sandbox host is unavailable
This commit is contained in:
committed by
Peter Steinberger
parent
5a0032de3e
commit
c76a47cce2
@@ -280,6 +280,7 @@ export function createExecTool(
|
||||
logInfo(`exec: elevated command ${truncateMiddle(params.command, 120)}`);
|
||||
}
|
||||
const configuredHost = defaults?.host ?? "sandbox";
|
||||
const sandboxHostConfigured = defaults?.host === "sandbox";
|
||||
const requestedHost = normalizeExecHost(params.host) ?? null;
|
||||
let host: ExecHost = requestedHost ?? configuredHost;
|
||||
if (!elevatedRequested && requestedHost && requestedHost !== configuredHost) {
|
||||
@@ -307,6 +308,18 @@ export function createExecTool(
|
||||
}
|
||||
|
||||
const sandbox = host === "sandbox" ? defaults?.sandbox : undefined;
|
||||
if (
|
||||
host === "sandbox" &&
|
||||
!sandbox &&
|
||||
(sandboxHostConfigured || requestedHost === "sandbox")
|
||||
) {
|
||||
throw new Error(
|
||||
[
|
||||
"exec host=sandbox is configured, but sandbox runtime is unavailable for this session.",
|
||||
'Enable sandbox mode (`agents.defaults.sandbox.mode="non-main"` or `"all"`) or set tools.exec.host to "gateway"/"node".',
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
const rawWorkdir = params.workdir?.trim() || defaults?.cwd || process.cwd();
|
||||
let workdir = rawWorkdir;
|
||||
let containerWorkdir = sandbox?.containerWorkdir;
|
||||
|
||||
@@ -601,6 +601,11 @@ describe("Agent-specific tool filtering", () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
tools: {
|
||||
deny: ["process"],
|
||||
exec: {
|
||||
host: "gateway",
|
||||
security: "full",
|
||||
ask: "off",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -622,11 +627,30 @@ describe("Agent-specific tool filtering", () => {
|
||||
expect(resultDetails?.status).toBe("completed");
|
||||
});
|
||||
|
||||
it("fails closed when exec host=sandbox is requested without sandbox runtime", async () => {
|
||||
const tools = createOpenClawCodingTools({
|
||||
config: {},
|
||||
sessionKey: "agent:main:main",
|
||||
workspaceDir: "/tmp/test-main-fail-closed",
|
||||
agentDir: "/tmp/agent-main-fail-closed",
|
||||
});
|
||||
const execTool = tools.find((tool) => tool.name === "exec");
|
||||
expect(execTool).toBeDefined();
|
||||
await expect(
|
||||
execTool!.execute("call-fail-closed", {
|
||||
command: "echo done",
|
||||
host: "sandbox",
|
||||
}),
|
||||
).rejects.toThrow("exec host not allowed");
|
||||
});
|
||||
|
||||
it("should apply agent-specific exec host defaults over global defaults", async () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
tools: {
|
||||
exec: {
|
||||
host: "sandbox",
|
||||
security: "full",
|
||||
ask: "off",
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
@@ -654,6 +678,12 @@ describe("Agent-specific tool filtering", () => {
|
||||
});
|
||||
const mainExecTool = mainTools.find((tool) => tool.name === "exec");
|
||||
expect(mainExecTool).toBeDefined();
|
||||
const mainResult = await mainExecTool!.execute("call-main-default", {
|
||||
command: "echo done",
|
||||
yieldMs: 1000,
|
||||
});
|
||||
const mainDetails = mainResult?.details as { status?: string } | undefined;
|
||||
expect(mainDetails?.status).toBe("completed");
|
||||
await expect(
|
||||
mainExecTool!.execute("call-main", {
|
||||
command: "echo done",
|
||||
@@ -669,12 +699,18 @@ describe("Agent-specific tool filtering", () => {
|
||||
});
|
||||
const helperExecTool = helperTools.find((tool) => tool.name === "exec");
|
||||
expect(helperExecTool).toBeDefined();
|
||||
const helperResult = await helperExecTool!.execute("call-helper", {
|
||||
command: "echo done",
|
||||
host: "sandbox",
|
||||
yieldMs: 1000,
|
||||
});
|
||||
const helperDetails = helperResult?.details as { status?: string } | undefined;
|
||||
expect(helperDetails?.status).toBe("completed");
|
||||
await expect(
|
||||
helperExecTool!.execute("call-helper-default", {
|
||||
command: "echo done",
|
||||
yieldMs: 1000,
|
||||
}),
|
||||
).rejects.toThrow("exec host=sandbox is configured");
|
||||
await expect(
|
||||
helperExecTool!.execute("call-helper", {
|
||||
command: "echo done",
|
||||
host: "sandbox",
|
||||
yieldMs: 1000,
|
||||
}),
|
||||
).rejects.toThrow("exec host=sandbox is configured");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -349,9 +349,13 @@ export function createOpenClawCodingTools(options?: {
|
||||
return [tool];
|
||||
});
|
||||
const { cleanupMs: cleanupMsOverride, ...execDefaults } = options?.exec ?? {};
|
||||
// Fail-closed baseline: when no sandbox context exists, default exec to gateway
|
||||
// so we never silently treat "sandbox" as host execution.
|
||||
const resolvedExecHost =
|
||||
options?.exec?.host ?? execConfig.host ?? (sandbox ? "sandbox" : "gateway");
|
||||
const execTool = createExecTool({
|
||||
...execDefaults,
|
||||
host: options?.exec?.host ?? execConfig.host,
|
||||
host: resolvedExecHost,
|
||||
security: options?.exec?.security ?? execConfig.security,
|
||||
ask: options?.exec?.ask ?? execConfig.ask,
|
||||
node: options?.exec?.node ?? execConfig.node,
|
||||
|
||||
Reference in New Issue
Block a user