fix(security): block env depth-overflow approval bypass

This commit is contained in:
Peter Steinberger
2026-02-25 00:13:33 +00:00
parent 1970a1e9e5
commit 57c9a18180
4 changed files with 137 additions and 0 deletions

View File

@@ -348,4 +348,99 @@ describe("handleSystemRunInvoke mac app exec host routing", () => {
}),
);
});
it("denies nested env shell payloads when wrapper depth is exceeded", async () => {
if (process.platform === "win32") {
return;
}
const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-env-depth-overflow-"));
const previousOpenClawHome = process.env.OPENCLAW_HOME;
const marker = path.join(tempHome, "pwned.txt");
process.env.OPENCLAW_HOME = tempHome;
saveExecApprovals({
version: 1,
defaults: {
security: "allowlist",
ask: "on-miss",
askFallback: "deny",
},
agents: {
main: {
allowlist: [{ pattern: "/usr/bin/env" }],
},
},
});
const runCommand = vi.fn(async () => {
fs.writeFileSync(marker, "executed");
return {
success: true,
stdout: "local-ok",
stderr: "",
timedOut: false,
truncated: false,
exitCode: 0,
error: null,
};
});
const sendInvokeResult = vi.fn(async () => {});
const sendNodeEvent = vi.fn(async () => {});
try {
await handleSystemRunInvoke({
client: {} as never,
params: {
command: [
"/usr/bin/env",
"/usr/bin/env",
"/usr/bin/env",
"/usr/bin/env",
"/usr/bin/env",
"/bin/sh",
"-c",
`echo PWNED > ${marker}`,
],
sessionKey: "agent:main:main",
},
skillBins: {
current: async () => [],
},
execHostEnforced: false,
execHostFallbackAllowed: true,
resolveExecSecurity: () => "allowlist",
resolveExecAsk: () => "on-miss",
isCmdExeInvocation: () => false,
sanitizeEnv: () => undefined,
runCommand,
runViaMacAppExecHost: vi.fn(async () => null),
sendNodeEvent,
buildExecEventPayload: (payload) => payload,
sendInvokeResult,
sendExecFinishedEvent: vi.fn(async () => {}),
preferMacAppExecHost: false,
});
} finally {
if (previousOpenClawHome === undefined) {
delete process.env.OPENCLAW_HOME;
} else {
process.env.OPENCLAW_HOME = previousOpenClawHome;
}
fs.rmSync(tempHome, { recursive: true, force: true });
}
expect(runCommand).not.toHaveBeenCalled();
expect(fs.existsSync(marker)).toBe(false);
expect(sendNodeEvent).toHaveBeenCalledWith(
expect.anything(),
"exec.denied",
expect.objectContaining({ reason: "approval-required" }),
);
expect(sendInvokeResult).toHaveBeenCalledWith(
expect.objectContaining({
ok: false,
error: expect.objectContaining({
message: "SYSTEM_RUN_DENIED: approval required",
}),
}),
);
});
});