refactor(security): dedupe shell env probe and add path regression test
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ExecApprovalsResolved } from "../infra/exec-approvals.js";
|
||||
import { captureEnv } from "../test-utils/env.js";
|
||||
@@ -67,7 +70,7 @@ describe("exec PATH login shell merge", () => {
|
||||
let envSnapshot: ReturnType<typeof captureEnv>;
|
||||
|
||||
beforeEach(() => {
|
||||
envSnapshot = captureEnv(["PATH"]);
|
||||
envSnapshot = captureEnv(["PATH", "SHELL"]);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -112,6 +115,43 @@ describe("exec PATH login shell merge", () => {
|
||||
|
||||
expect(shellPathMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not apply login-shell PATH when probe rejects unregistered absolute SHELL", async () => {
|
||||
if (isWin) {
|
||||
return;
|
||||
}
|
||||
process.env.PATH = "/usr/bin";
|
||||
const shellDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-shell-env-"));
|
||||
const unregisteredShellPath = path.join(shellDir, "unregistered-shell");
|
||||
fs.writeFileSync(unregisteredShellPath, '#!/bin/sh\nexec /bin/sh "$@"\n', {
|
||||
encoding: "utf8",
|
||||
mode: 0o755,
|
||||
});
|
||||
process.env.SHELL = unregisteredShellPath;
|
||||
|
||||
try {
|
||||
const shellPathMock = vi.mocked(getShellPathFromLoginShell);
|
||||
shellPathMock.mockClear();
|
||||
shellPathMock.mockImplementation((opts) =>
|
||||
opts.env.SHELL?.trim() === unregisteredShellPath ? null : "/custom/bin:/opt/bin",
|
||||
);
|
||||
|
||||
const tool = createExecTool({ host: "gateway", security: "full", ask: "off" });
|
||||
const result = await tool.execute("call1", { command: "echo $PATH" });
|
||||
const entries = normalizePathEntries(result.content.find((c) => c.type === "text")?.text);
|
||||
|
||||
expect(entries).toEqual(["/usr/bin"]);
|
||||
expect(shellPathMock).toHaveBeenCalledTimes(1);
|
||||
expect(shellPathMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
env: process.env,
|
||||
timeoutMs: 1234,
|
||||
}),
|
||||
);
|
||||
} finally {
|
||||
fs.rmSync(shellDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("exec host env validation", () => {
|
||||
|
||||
Reference in New Issue
Block a user