mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 05:01:23 +00:00
refactor(exec): split host flows and harden safe-bin trust
This commit is contained in:
@@ -357,6 +357,7 @@ function evaluateSegments(
|
||||
allowlist: ExecAllowlistEntry[];
|
||||
safeBins: Set<string>;
|
||||
cwd?: string;
|
||||
trustedSafeBinDirs?: ReadonlySet<string>;
|
||||
skillBins?: Set<string>;
|
||||
autoAllowSkills?: boolean;
|
||||
},
|
||||
@@ -384,6 +385,7 @@ function evaluateSegments(
|
||||
resolution: segment.resolution,
|
||||
safeBins: params.safeBins,
|
||||
cwd: params.cwd,
|
||||
trustedSafeBinDirs: params.trustedSafeBinDirs,
|
||||
});
|
||||
const skillAllow =
|
||||
allowSkills && segment.resolution?.executableName
|
||||
@@ -408,6 +410,7 @@ export function evaluateExecAllowlist(params: {
|
||||
allowlist: ExecAllowlistEntry[];
|
||||
safeBins: Set<string>;
|
||||
cwd?: string;
|
||||
trustedSafeBinDirs?: ReadonlySet<string>;
|
||||
skillBins?: Set<string>;
|
||||
autoAllowSkills?: boolean;
|
||||
}): ExecAllowlistEvaluation {
|
||||
@@ -424,6 +427,7 @@ export function evaluateExecAllowlist(params: {
|
||||
allowlist: params.allowlist,
|
||||
safeBins: params.safeBins,
|
||||
cwd: params.cwd,
|
||||
trustedSafeBinDirs: params.trustedSafeBinDirs,
|
||||
skillBins: params.skillBins,
|
||||
autoAllowSkills: params.autoAllowSkills,
|
||||
});
|
||||
@@ -441,6 +445,7 @@ export function evaluateExecAllowlist(params: {
|
||||
allowlist: params.allowlist,
|
||||
safeBins: params.safeBins,
|
||||
cwd: params.cwd,
|
||||
trustedSafeBinDirs: params.trustedSafeBinDirs,
|
||||
skillBins: params.skillBins,
|
||||
autoAllowSkills: params.autoAllowSkills,
|
||||
});
|
||||
@@ -468,6 +473,7 @@ export function evaluateShellAllowlist(params: {
|
||||
safeBins: Set<string>;
|
||||
cwd?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
trustedSafeBinDirs?: ReadonlySet<string>;
|
||||
skillBins?: Set<string>;
|
||||
autoAllowSkills?: boolean;
|
||||
platform?: string | null;
|
||||
@@ -496,6 +502,7 @@ export function evaluateShellAllowlist(params: {
|
||||
allowlist: params.allowlist,
|
||||
safeBins: params.safeBins,
|
||||
cwd: params.cwd,
|
||||
trustedSafeBinDirs: params.trustedSafeBinDirs,
|
||||
skillBins: params.skillBins,
|
||||
autoAllowSkills: params.autoAllowSkills,
|
||||
});
|
||||
@@ -529,6 +536,7 @@ export function evaluateShellAllowlist(params: {
|
||||
allowlist: params.allowlist,
|
||||
safeBins: params.safeBins,
|
||||
cwd: params.cwd,
|
||||
trustedSafeBinDirs: params.trustedSafeBinDirs,
|
||||
skillBins: params.skillBins,
|
||||
autoAllowSkills: params.autoAllowSkills,
|
||||
});
|
||||
|
||||
@@ -517,7 +517,6 @@ describe("exec approvals safe bins", () => {
|
||||
});
|
||||
expect(ok).toBe(true);
|
||||
});
|
||||
|
||||
it("does not include sort/grep in default safeBins", () => {
|
||||
const defaults = resolveSafeBins(undefined);
|
||||
expect(defaults.has("jq")).toBe(true);
|
||||
@@ -582,6 +581,43 @@ describe("exec approvals safe bins", () => {
|
||||
expect(ok).toBe(false);
|
||||
expect(checkedExists).toBe(false);
|
||||
});
|
||||
|
||||
it("threads trusted safe-bin dirs through allowlist evaluation", () => {
|
||||
if (process.platform === "win32") {
|
||||
return;
|
||||
}
|
||||
const analysis = {
|
||||
ok: true as const,
|
||||
segments: [
|
||||
{
|
||||
raw: "jq .foo",
|
||||
argv: ["jq", ".foo"],
|
||||
resolution: {
|
||||
rawExecutable: "jq",
|
||||
resolvedPath: "/custom/bin/jq",
|
||||
executableName: "jq",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const denied = evaluateExecAllowlist({
|
||||
analysis,
|
||||
allowlist: [],
|
||||
safeBins: normalizeSafeBins(["jq"]),
|
||||
trustedSafeBinDirs: new Set(["/usr/bin"]),
|
||||
cwd: "/tmp",
|
||||
});
|
||||
expect(denied.allowlistSatisfied).toBe(false);
|
||||
|
||||
const allowed = evaluateExecAllowlist({
|
||||
analysis,
|
||||
allowlist: [],
|
||||
safeBins: normalizeSafeBins(["jq"]),
|
||||
trustedSafeBinDirs: new Set(["/custom/bin"]),
|
||||
cwd: "/tmp",
|
||||
});
|
||||
expect(allowed.allowlistSatisfied).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("exec approvals allowlist evaluation", () => {
|
||||
|
||||
@@ -54,4 +54,18 @@ describe("exec safe bin trust", () => {
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("uses startup PATH snapshot when pathEnv is omitted", () => {
|
||||
const originalPath = process.env.PATH;
|
||||
const injected = `/tmp/openclaw-path-injected-${Date.now()}`;
|
||||
const initial = getTrustedSafeBinDirs({ refresh: true });
|
||||
try {
|
||||
process.env.PATH = `${injected}${path.delimiter}${originalPath ?? ""}`;
|
||||
const refreshed = getTrustedSafeBinDirs({ refresh: true });
|
||||
expect(refreshed.has(path.resolve(injected))).toBe(false);
|
||||
expect([...refreshed].toSorted()).toEqual([...initial].toSorted());
|
||||
} finally {
|
||||
process.env.PATH = originalPath;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,6 +29,7 @@ type TrustedSafeBinCache = {
|
||||
};
|
||||
|
||||
let trustedSafeBinCache: TrustedSafeBinCache | null = null;
|
||||
const STARTUP_PATH_ENV = process.env.PATH ?? process.env.Path ?? "";
|
||||
|
||||
function normalizeTrustedDir(value: string): string | null {
|
||||
const trimmed = value.trim();
|
||||
@@ -74,7 +75,7 @@ export function getTrustedSafeBinDirs(
|
||||
} = {},
|
||||
): Set<string> {
|
||||
const delimiter = params.delimiter ?? path.delimiter;
|
||||
const pathEnv = params.pathEnv ?? process.env.PATH ?? process.env.Path ?? "";
|
||||
const pathEnv = params.pathEnv ?? STARTUP_PATH_ENV;
|
||||
const key = buildTrustedSafeBinCacheKey(pathEnv, delimiter);
|
||||
|
||||
if (!params.refresh && trustedSafeBinCache?.key === key) {
|
||||
|
||||
Reference in New Issue
Block a user