fix(security): harden exec wrapper allowlist execution parity

This commit is contained in:
Peter Steinberger
2026-02-24 01:51:33 +00:00
parent 5eb72ab769
commit a1c4bf07c6
12 changed files with 289 additions and 65 deletions

View File

@@ -5,6 +5,7 @@ import { makePathEnv, makeTempDir } from "./exec-approvals-test-helpers.js";
import {
analyzeArgvCommand,
analyzeShellCommand,
buildEnforcedShellCommand,
buildSafeBinsShellCommand,
evaluateExecAllowlist,
evaluateShellAllowlist,
@@ -130,6 +131,27 @@ describe("exec approvals safe shell command builder", () => {
// SafeBins segment is fully quoted and pinned to its resolved absolute path.
expect(res.command).toMatch(/'[^']*\/head' '-n' '5'/);
});
it("enforces canonical planned argv for every approved segment", () => {
if (process.platform === "win32") {
return;
}
const analysis = analyzeShellCommand({
command: "env rg -n needle",
cwd: "/tmp",
env: { PATH: "/usr/bin:/bin" },
platform: process.platform,
});
expect(analysis.ok).toBe(true);
const res = buildEnforcedShellCommand({
command: "env rg -n needle",
segments: analysis.segments,
platform: process.platform,
});
expect(res.ok).toBe(true);
expect(res.command).toMatch(/'(?:[^']*\/)?rg' '-n' 'needle'/);
expect(res.command).not.toContain("'env'");
});
});
describe("exec approvals command resolution", () => {
@@ -202,7 +224,7 @@ describe("exec approvals command resolution", () => {
}
});
it("unwraps env wrapper argv to resolve the effective executable", () => {
it("unwraps transparent env wrapper argv to resolve the effective executable", () => {
const dir = makeTempDir();
const binDir = path.join(dir, "bin");
fs.mkdirSync(binDir, { recursive: true });
@@ -212,7 +234,7 @@ describe("exec approvals command resolution", () => {
fs.chmodSync(exe, 0o755);
const resolution = resolveCommandResolutionFromArgv(
["/usr/bin/env", "FOO=bar", "rg", "-n", "needle"],
["/usr/bin/env", "rg", "-n", "needle"],
undefined,
makePathEnv(binDir),
);
@@ -220,6 +242,18 @@ describe("exec approvals command resolution", () => {
expect(resolution?.executableName).toBe(exeName);
});
it("blocks semantic env wrappers from allowlist/safeBins auto-resolution", () => {
const resolution = resolveCommandResolutionFromArgv([
"/usr/bin/env",
"FOO=bar",
"rg",
"-n",
"needle",
]);
expect(resolution?.policyBlocked).toBe(true);
expect(resolution?.rawExecutable).toBe("/usr/bin/env");
});
it("unwraps env wrapper with shell inner executable", () => {
const resolution = resolveCommandResolutionFromArgv(["/usr/bin/env", "bash", "-lc", "echo hi"]);
expect(resolution?.rawExecutable).toBe("bash");