fix: harden sandbox media reads against TOCTOU escapes

This commit is contained in:
Peter Steinberger
2026-03-02 01:03:40 +00:00
parent 4320cde91d
commit c823a85302
12 changed files with 223 additions and 27 deletions

View File

@@ -3,8 +3,11 @@ import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { createTrackedTempDirs } from "../test-utils/tracked-temp-dirs.js";
import {
createRootScopedReadFile,
SafeOpenError,
openFileWithinRoot,
readFileWithinRoot,
readPathWithinRoot,
readLocalFileSafely,
writeFileWithinRoot,
} from "./fs-safe.js";
@@ -70,6 +73,37 @@ describe("fs-safe", () => {
).rejects.toMatchObject({ code: "outside-workspace" });
});
it("reads a file within root", async () => {
const root = await tempDirs.make("openclaw-fs-safe-root-");
await fs.writeFile(path.join(root, "inside.txt"), "inside");
const result = await readFileWithinRoot({
rootDir: root,
relativePath: "inside.txt",
});
expect(result.buffer.toString("utf8")).toBe("inside");
expect(result.realPath).toContain("inside.txt");
expect(result.stat.size).toBe(6);
});
it("reads an absolute path within root via readPathWithinRoot", async () => {
const root = await tempDirs.make("openclaw-fs-safe-root-");
const insidePath = path.join(root, "absolute.txt");
await fs.writeFile(insidePath, "absolute");
const result = await readPathWithinRoot({
rootDir: root,
filePath: insidePath,
});
expect(result.buffer.toString("utf8")).toBe("absolute");
});
it("creates a root-scoped read callback", async () => {
const root = await tempDirs.make("openclaw-fs-safe-root-");
const insidePath = path.join(root, "scoped.txt");
await fs.writeFile(insidePath, "scoped");
const readScoped = createRootScopedReadFile({ rootDir: root });
await expect(readScoped(insidePath)).resolves.toEqual(Buffer.from("scoped"));
});
it.runIf(process.platform !== "win32")("blocks symlink escapes under root", async () => {
const root = await tempDirs.make("openclaw-fs-safe-root-");
const outside = await tempDirs.make("openclaw-fs-safe-outside-");