Files
openclaw/src/infra/exec-inline-eval.test.ts
Jacob Tomlinson 7bd2761b92 Exec approvals: detect command carriers in strict inline eval (#57842)
* Exec approvals: detect command carriers in strict inline eval

* Exec approvals: cover carrier option edge cases

* Exec approvals: cover make and find carriers

* Exec approvals: catch attached eval flags

* Exec approvals: keep sed -E out of inline eval

* Exec approvals: treat sed in-place flags as optional
2026-03-31 10:58:17 +01:00

83 lines
4.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
describeInterpreterInlineEval,
detectInterpreterInlineEvalArgv,
isInterpreterLikeAllowlistPattern,
} from "./exec-inline-eval.js";
describe("exec inline eval detection", () => {
it.each([
{ argv: ["python3", "-c", "print('hi')"], expected: "python3 -c" },
{ argv: ["/usr/bin/node", "--eval", "console.log('hi')"], expected: "node --eval" },
{ argv: ["perl", "-E", "say 1"], expected: "perl -e" },
{ argv: ["osascript", "-e", "beep"], expected: "osascript -e" },
{ argv: ["awk", "BEGIN { print 1 }"], expected: "awk inline program" },
{ argv: ["gawk", "-F", ",", "{print $1}", "data.csv"], expected: "gawk inline program" },
] as const)("detects interpreter eval flags for %j", ({ argv, expected }) => {
const hit = detectInterpreterInlineEvalArgv([...argv]);
expect(hit).not.toBeNull();
expect(describeInterpreterInlineEval(hit!)).toBe(expected);
});
it.each([
{ argv: ["awk", 'BEGIN{system("id")}', "/dev/null"], expected: "awk inline program" },
{
argv: ["awk", "-F", ",", 'BEGIN{system("id")}', "/dev/null"],
expected: "awk inline program",
},
{ argv: ["gawk", "-e", 'BEGIN{system("id")}', "/dev/null"], expected: "gawk -e" },
{
argv: ["gawk", "-f", "library.awk", '--source=BEGIN{system("id")}', "/dev/null"],
expected: "gawk --source",
},
{ argv: ["find", ".", "-exec", "id", "{}", ";"], expected: "find -exec" },
{ argv: ["find", "--", ".", "-exec", "id", "{}", ";"], expected: "find -exec" },
{ argv: ["find", ".", "-ok", "id", "{}", ";"], expected: "find -ok" },
{ argv: ["find", ".", "-okdir", "id", "{}", ";"], expected: "find -okdir" },
{ argv: ["xargs", "id"], expected: "xargs inline command" },
{ argv: ["xargs", "-I", "{}", "sh", "-c", "id"], expected: "xargs inline command" },
{ argv: ["xargs", "--replace", "id"], expected: "xargs inline command" },
{ argv: ["make", "-f", "evil.mk"], expected: "make -f" },
{ argv: ["make", "-E", "$(shell id)"], expected: "make -E" },
{ argv: ["make", "-E$(shell id)"], expected: "make -E" },
{ argv: ["make", "--eval=$(shell id)"], expected: "make --eval" },
{ argv: ["sed", "s/.*/id/e", "/dev/null"], expected: "sed inline program" },
{ argv: ["gsed", "-e", "s/.*/id/e", "/dev/null"], expected: "gsed -e" },
{ argv: ["sed", "-es/.*/id/e", "/dev/null"], expected: "sed -e" },
] as const)("detects command carriers for %j", ({ argv, expected }) => {
const hit = detectInterpreterInlineEvalArgv([...argv]);
expect(hit).not.toBeNull();
expect(describeInterpreterInlineEval(hit!)).toBe(expected);
});
it("ignores normal script execution", () => {
expect(detectInterpreterInlineEvalArgv(["python3", "script.py"])).toBeNull();
expect(detectInterpreterInlineEvalArgv(["node", "script.js"])).toBeNull();
expect(detectInterpreterInlineEvalArgv(["awk", "-f", "script.awk", "data.csv"])).toBeNull();
expect(detectInterpreterInlineEvalArgv(["find", ".", "-name", "*.ts"])).toBeNull();
expect(detectInterpreterInlineEvalArgv(["xargs", "-0"])).toBeNull();
expect(detectInterpreterInlineEvalArgv(["make", "test"])).toBeNull();
expect(detectInterpreterInlineEvalArgv(["sed", "-f", "script.sed", "input.txt"])).toBeNull();
expect(
detectInterpreterInlineEvalArgv(["sed", "-i", "-f", "script.sed", "input.txt"]),
).toBeNull();
expect(
detectInterpreterInlineEvalArgv(["sed", "-E", "-f", "script.sed", "input.txt"]),
).toBeNull();
});
it("matches interpreter-like allowlist patterns", () => {
expect(isInterpreterLikeAllowlistPattern("/usr/bin/python3")).toBe(true);
expect(isInterpreterLikeAllowlistPattern("**/node")).toBe(true);
expect(isInterpreterLikeAllowlistPattern("/usr/bin/awk")).toBe(true);
expect(isInterpreterLikeAllowlistPattern("**/gawk")).toBe(true);
expect(isInterpreterLikeAllowlistPattern("/usr/bin/mawk")).toBe(true);
expect(isInterpreterLikeAllowlistPattern("nawk")).toBe(true);
expect(isInterpreterLikeAllowlistPattern("**/find")).toBe(true);
expect(isInterpreterLikeAllowlistPattern("xargs.exe")).toBe(true);
expect(isInterpreterLikeAllowlistPattern("/usr/bin/gmake")).toBe(true);
expect(isInterpreterLikeAllowlistPattern("**/gsed")).toBe(true);
expect(isInterpreterLikeAllowlistPattern("/usr/bin/rg")).toBe(false);
});
});