mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 20:04:45 +00:00
114 lines
3.1 KiB
TypeScript
114 lines
3.1 KiB
TypeScript
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
import { captureEnv } from "../test-utils/env.js";
|
|
import { resetProcessRegistryForTests } from "./bash-process-registry.js";
|
|
import { createExecTool } from "./bash-tools.exec.js";
|
|
|
|
describe("exec security floor", () => {
|
|
let envSnapshot: ReturnType<typeof captureEnv>;
|
|
let tempRoot: string | undefined;
|
|
|
|
beforeEach(() => {
|
|
envSnapshot = captureEnv([
|
|
"HOME",
|
|
"USERPROFILE",
|
|
"HOMEDRIVE",
|
|
"HOMEPATH",
|
|
"OPENCLAW_HOME",
|
|
"OPENCLAW_STATE_DIR",
|
|
"SHELL",
|
|
]);
|
|
tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-exec-security-floor-"));
|
|
process.env.HOME = tempRoot;
|
|
process.env.USERPROFILE = tempRoot;
|
|
process.env.OPENCLAW_HOME = tempRoot;
|
|
process.env.OPENCLAW_STATE_DIR = path.join(tempRoot, "state");
|
|
if (process.platform === "win32") {
|
|
const parsed = path.parse(tempRoot);
|
|
process.env.HOMEDRIVE = parsed.root.slice(0, 2);
|
|
process.env.HOMEPATH = tempRoot.slice(2) || "\\";
|
|
} else {
|
|
delete process.env.HOMEDRIVE;
|
|
delete process.env.HOMEPATH;
|
|
}
|
|
resetProcessRegistryForTests();
|
|
});
|
|
|
|
afterEach(() => {
|
|
const dir = tempRoot;
|
|
tempRoot = undefined;
|
|
envSnapshot.restore();
|
|
if (dir) {
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("ignores model-supplied allowlist security when configured security is full", async () => {
|
|
const tool = createExecTool({
|
|
security: "full",
|
|
ask: "off",
|
|
});
|
|
|
|
const result = await tool.execute("call-1", {
|
|
command: "echo hello",
|
|
security: "allowlist",
|
|
ask: "off",
|
|
});
|
|
|
|
expect(result.content[0]?.type).toBe("text");
|
|
const text = (result.content[0] as { text?: string }).text ?? "";
|
|
expect(text).not.toMatch(/exec denied/i);
|
|
expect(text).not.toMatch(/allowlist miss/i);
|
|
expect(text.trim()).toContain("hello");
|
|
});
|
|
|
|
it("enforces configured allowlist security when model also passes allowlist", async () => {
|
|
const tool = createExecTool({
|
|
security: "allowlist",
|
|
ask: "off",
|
|
safeBins: [],
|
|
});
|
|
|
|
await expect(
|
|
tool.execute("call-2", {
|
|
command: "echo hello",
|
|
security: "allowlist",
|
|
ask: "off",
|
|
}),
|
|
).rejects.toThrow(/exec denied: allowlist miss/i);
|
|
});
|
|
|
|
it("ignores model-supplied deny security when configured security is allowlist", async () => {
|
|
const tool = createExecTool({
|
|
security: "allowlist",
|
|
ask: "off",
|
|
safeBins: [],
|
|
});
|
|
|
|
await expect(
|
|
tool.execute("call-3", {
|
|
command: "echo hello",
|
|
security: "deny",
|
|
ask: "off",
|
|
}),
|
|
).rejects.toThrow(/exec denied: allowlist miss/i);
|
|
});
|
|
|
|
it("ignores model-supplied full security when configured security is deny", async () => {
|
|
const tool = createExecTool({
|
|
security: "deny",
|
|
ask: "off",
|
|
});
|
|
|
|
await expect(
|
|
tool.execute("call-4", {
|
|
command: "echo hello",
|
|
security: "full",
|
|
ask: "off",
|
|
}),
|
|
).rejects.toThrow(/exec denied/i);
|
|
});
|
|
});
|