fix(security): harden safeBins stdin-only enforcement

This commit is contained in:
Peter Steinberger
2026-02-19 14:07:43 +01:00
parent 3c127b6eac
commit cfe8457a0f
6 changed files with 200 additions and 7 deletions

View File

@@ -157,4 +157,92 @@ describe("createOpenClawCodingTools safeBins", () => {
expect(blockedResultDetails.status).toBe("completed");
expect(text).not.toContain(secret);
});
it("blocks sort output flags from writing files via safeBins", async () => {
if (process.platform === "win32") {
return;
}
const { createOpenClawCodingTools } = await import("./pi-tools.js");
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-safe-bins-sort-"));
const cfg: OpenClawConfig = {
tools: {
exec: {
host: "gateway",
security: "allowlist",
ask: "off",
safeBins: ["sort"],
},
},
};
const tools = createOpenClawCodingTools({
config: cfg,
sessionKey: "agent:main:main",
workspaceDir: tmpDir,
agentDir: path.join(tmpDir, "agent"),
});
const execTool = tools.find((tool) => tool.name === "exec");
expect(execTool).toBeDefined();
const shortTarget = path.join(tmpDir, "blocked-short.txt");
await expect(
execTool!.execute("call1", {
command: "sort -oblocked-short.txt",
workdir: tmpDir,
}),
).rejects.toThrow("exec denied: allowlist miss");
expect(fs.existsSync(shortTarget)).toBe(false);
const longTarget = path.join(tmpDir, "blocked-long.txt");
await expect(
execTool!.execute("call2", {
command: "sort --output=blocked-long.txt",
workdir: tmpDir,
}),
).rejects.toThrow("exec denied: allowlist miss");
expect(fs.existsSync(longTarget)).toBe(false);
});
it("blocks grep recursive flags from reading cwd via safeBins", async () => {
if (process.platform === "win32") {
return;
}
const { createOpenClawCodingTools } = await import("./pi-tools.js");
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-safe-bins-grep-"));
fs.writeFileSync(
path.join(tmpDir, "secret.txt"),
"SAFE_BINS_RECURSIVE_SHOULD_NOT_LEAK\n",
"utf8",
);
const cfg: OpenClawConfig = {
tools: {
exec: {
host: "gateway",
security: "allowlist",
ask: "off",
safeBins: ["grep"],
},
},
};
const tools = createOpenClawCodingTools({
config: cfg,
sessionKey: "agent:main:main",
workspaceDir: tmpDir,
agentDir: path.join(tmpDir, "agent"),
});
const execTool = tools.find((tool) => tool.name === "exec");
expect(execTool).toBeDefined();
await expect(
execTool!.execute("call1", {
command: "grep -R SAFE_BINS_RECURSIVE_SHOULD_NOT_LEAK",
workdir: tmpDir,
}),
).rejects.toThrow("exec denied: allowlist miss");
});
});