mirror of https://github.com/openclaw/openclaw.git
fix(sandbox): resolve pinned fs helper python without PATH (#58573)
This commit is contained in:
parent
a37c66906c
commit
b86f5d5ea4
|
|
@ -1,4 +1,5 @@
|
|||
import { spawnSync } from "node:child_process";
|
||||
import { existsSync } from "node:fs";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
|
@ -6,6 +7,7 @@ import { withTempDir } from "../../test-helpers/temp-dir.js";
|
|||
import {
|
||||
buildPinnedWritePlan,
|
||||
SANDBOX_PINNED_MUTATION_PYTHON,
|
||||
SANDBOX_PINNED_MUTATION_PYTHON_CANDIDATES,
|
||||
} from "./fs-bridge-mutation-helper.js";
|
||||
|
||||
function runMutation(args: string[], input?: string) {
|
||||
|
|
@ -16,7 +18,7 @@ function runMutation(args: string[], input?: string) {
|
|||
});
|
||||
}
|
||||
|
||||
function runWritePlan(args: string[], input?: string) {
|
||||
function runWritePlan(args: string[], input?: string, env?: NodeJS.ProcessEnv) {
|
||||
const plan = buildPinnedWritePlan({
|
||||
check: {
|
||||
target: {
|
||||
|
|
@ -38,13 +40,18 @@ function runWritePlan(args: string[], input?: string) {
|
|||
mkdir: args[4] === "1",
|
||||
});
|
||||
|
||||
return spawnSync("sh", ["-c", plan.script, "openclaw-sandbox-fs", ...(plan.args ?? [])], {
|
||||
return spawnSync("/bin/sh", ["-c", plan.script, "openclaw-sandbox-fs", ...(plan.args ?? [])], {
|
||||
input,
|
||||
encoding: "utf8",
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
env,
|
||||
});
|
||||
}
|
||||
|
||||
const hasAbsolutePythonCandidate = SANDBOX_PINNED_MUTATION_PYTHON_CANDIDATES.some((candidate) =>
|
||||
existsSync(candidate),
|
||||
);
|
||||
|
||||
describe("sandbox pinned mutation helper", () => {
|
||||
it("writes through a pinned directory fd", async () => {
|
||||
await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => {
|
||||
|
|
@ -116,6 +123,29 @@ describe("sandbox pinned mutation helper", () => {
|
|||
},
|
||||
);
|
||||
|
||||
it.runIf(process.platform !== "win32" && hasAbsolutePythonCandidate)(
|
||||
"finds an absolute python when the write plan runs with an empty PATH",
|
||||
async () => {
|
||||
await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => {
|
||||
const workspace = path.join(root, "workspace");
|
||||
await fs.mkdir(workspace, { recursive: true });
|
||||
|
||||
const result = runWritePlan(
|
||||
["write", workspace, "nested/deeper", "note.txt", "1"],
|
||||
"hello",
|
||||
{
|
||||
PATH: "",
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
await expect(
|
||||
fs.readFile(path.join(workspace, "nested", "deeper", "note.txt"), "utf8"),
|
||||
).resolves.toBe("hello");
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it.runIf(process.platform !== "win32")(
|
||||
"rejects symlink-parent writes instead of materializing a temp file outside the mount",
|
||||
async () => {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,13 @@ import type {
|
|||
} from "./fs-bridge-path-safety.js";
|
||||
import type { SandboxFsCommandPlan } from "./fs-bridge-shell-command-plans.js";
|
||||
|
||||
export const SANDBOX_PINNED_MUTATION_PYTHON_CANDIDATES = [
|
||||
"/usr/bin/python3",
|
||||
"/usr/local/bin/python3",
|
||||
"/opt/homebrew/bin/python3",
|
||||
"/bin/python3",
|
||||
] as const;
|
||||
|
||||
export const SANDBOX_PINNED_MUTATION_PYTHON = [
|
||||
"import errno",
|
||||
"import os",
|
||||
|
|
@ -276,6 +283,8 @@ export const SANDBOX_PINNED_MUTATION_PYTHON = [
|
|||
" raise RuntimeError('unknown sandbox mutation operation: ' + operation)",
|
||||
].join("\n");
|
||||
|
||||
const SANDBOX_PINNED_MUTATION_PYTHON_SHELL_LITERAL = `'${SANDBOX_PINNED_MUTATION_PYTHON.replaceAll("'", `'\\''`)}'`;
|
||||
|
||||
function buildPinnedMutationPlan(params: {
|
||||
args: string[];
|
||||
checks: PathSafetyCheck[];
|
||||
|
|
@ -286,9 +295,18 @@ function buildPinnedMutationPlan(params: {
|
|||
// Feed the helper source over fd 3 so stdin stays available for write payload bytes.
|
||||
script: [
|
||||
"set -eu",
|
||||
"python3 /dev/fd/3 \"$@\" 3<<'PY'",
|
||||
SANDBOX_PINNED_MUTATION_PYTHON,
|
||||
"PY",
|
||||
"python_cmd=''",
|
||||
...SANDBOX_PINNED_MUTATION_PYTHON_CANDIDATES.map(
|
||||
(candidate) =>
|
||||
`if [ -z "$python_cmd" ] && [ -x '${candidate}' ]; then python_cmd='${candidate}'; fi`,
|
||||
),
|
||||
'if [ -z "$python_cmd" ]; then python_cmd=$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true); fi',
|
||||
'if [ -z "$python_cmd" ]; then',
|
||||
" echo >&2 'sandbox pinned mutation helper requires python3 or python'",
|
||||
" exit 127",
|
||||
"fi",
|
||||
`python_script=${SANDBOX_PINNED_MUTATION_PYTHON_SHELL_LITERAL}`,
|
||||
'exec "$python_cmd" -c "$python_script" "$@"',
|
||||
].join("\n"),
|
||||
args: params.args,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue