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

@@ -20,6 +20,10 @@ describe("handleSystemRunInvoke mac app exec host routing", () => {
async function runSystemInvoke(params: {
preferMacAppExecHost: boolean;
runViaResponse?: ExecHostResponse | null;
command?: string[];
security?: "full" | "allowlist";
ask?: "off" | "on-miss" | "always";
approved?: boolean;
}) {
const runCommand = vi.fn(async () => ({
success: true,
@@ -37,8 +41,8 @@ describe("handleSystemRunInvoke mac app exec host routing", () => {
await handleSystemRunInvoke({
client: {} as never,
params: {
command: ["echo", "ok"],
approved: true,
command: params.command ?? ["echo", "ok"],
approved: params.approved ?? false,
sessionKey: "agent:main:main",
},
skillBins: {
@@ -46,8 +50,8 @@ describe("handleSystemRunInvoke mac app exec host routing", () => {
},
execHostEnforced: false,
execHostFallbackAllowed: true,
resolveExecSecurity: () => "full",
resolveExecAsk: () => "off",
resolveExecSecurity: () => params.security ?? "full",
resolveExecAsk: () => params.ask ?? "off",
isCmdExeInvocation: () => false,
sanitizeEnv: () => undefined,
runCommand,
@@ -112,4 +116,35 @@ describe("handleSystemRunInvoke mac app exec host routing", () => {
}),
);
});
it("runs canonical argv in allowlist mode for transparent env wrappers", async () => {
const { runCommand, sendInvokeResult } = await runSystemInvoke({
preferMacAppExecHost: false,
security: "allowlist",
command: ["env", "tr", "a", "b"],
});
expect(runCommand).toHaveBeenCalledWith(["tr", "a", "b"], undefined, undefined, undefined);
expect(sendInvokeResult).toHaveBeenCalledWith(
expect.objectContaining({
ok: true,
}),
);
});
it("denies semantic env wrappers in allowlist mode", async () => {
const { runCommand, sendInvokeResult } = await runSystemInvoke({
preferMacAppExecHost: false,
security: "allowlist",
command: ["env", "FOO=bar", "tr", "a", "b"],
});
expect(runCommand).not.toHaveBeenCalled();
expect(sendInvokeResult).toHaveBeenCalledWith(
expect.objectContaining({
ok: false,
error: expect.objectContaining({
message: expect.stringContaining("allowlist miss"),
}),
}),
);
});
});