Files
moltbot/src/agents/bash-tools.exec.script-preflight.test.ts
2026-02-22 20:04:51 +00:00

94 lines
3.2 KiB
TypeScript

import fs from "node:fs/promises";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { withTempDir } from "../test-utils/temp-dir.js";
import { createExecTool } from "./bash-tools.exec.js";
const isWin = process.platform === "win32";
const describeNonWin = isWin ? describe.skip : describe;
describeNonWin("exec script preflight", () => {
it("blocks shell env var injection tokens in python scripts before execution", async () => {
await withTempDir("openclaw-exec-preflight-", async (tmp) => {
const pyPath = path.join(tmp, "bad.py");
await fs.writeFile(
pyPath,
[
"import json",
"# model accidentally wrote shell syntax:",
"payload = $DM_JSON",
"print(payload)",
].join("\n"),
"utf-8",
);
const tool = createExecTool({ host: "gateway", security: "full", ask: "off" });
await expect(
tool.execute("call1", {
command: "python bad.py",
workdir: tmp,
}),
).rejects.toThrow(/exec preflight: detected likely shell variable injection \(\$DM_JSON\)/);
});
});
it("blocks obvious shell-as-js output before node execution", async () => {
await withTempDir("openclaw-exec-preflight-", async (tmp) => {
const jsPath = path.join(tmp, "bad.js");
await fs.writeFile(
jsPath,
['NODE "$TMPDIR/hot.json"', "console.log('hi')"].join("\n"),
"utf-8",
);
const tool = createExecTool({ host: "gateway", security: "full", ask: "off" });
await expect(
tool.execute("call1", {
command: "node bad.js",
workdir: tmp,
}),
).rejects.toThrow(
/exec preflight: (detected likely shell variable injection|JS file starts with shell syntax)/,
);
});
});
it("skips preflight when script token is quoted and unresolved by fast parser", async () => {
await withTempDir("openclaw-exec-preflight-", async (tmp) => {
const jsPath = path.join(tmp, "bad.js");
await fs.writeFile(jsPath, "const value = $DM_JSON;", "utf-8");
const tool = createExecTool({ host: "gateway", security: "full", ask: "off" });
const result = await tool.execute("call-quoted", {
command: 'node "bad.js"',
workdir: tmp,
});
const text = result.content.find((block) => block.type === "text")?.text ?? "";
expect(text).not.toMatch(/exec preflight:/);
});
});
it("skips preflight file reads for script paths outside the workdir", async () => {
await withTempDir("openclaw-exec-preflight-parent-", async (parent) => {
const outsidePath = path.join(parent, "outside.js");
const workdir = path.join(parent, "workdir");
await fs.mkdir(workdir, { recursive: true });
await fs.writeFile(outsidePath, "const value = $DM_JSON;", "utf-8");
const tool = createExecTool({ host: "gateway", security: "full", ask: "off" });
const result = await tool.execute("call-outside", {
command: "node ../outside.js",
workdir,
});
const text = result.content.find((block) => block.type === "text")?.text ?? "";
expect(text).not.toMatch(/exec preflight:/);
});
});
});