Remove hostRoots from this-mac desktop sandbox so shell execution stays inside the bot computer workspace, matching container path rules. Add resolveDesktopExecuteCwd to map virtual /home/rakazo paths onto the local home and reject host-absolute cwd requests via containment checks. Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
84 lines
3.1 KiB
TypeScript
84 lines
3.1 KiB
TypeScript
import { mkdtempSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import { afterAll, describe, expect, it } from "vitest";
|
|
import {
|
|
isAllowedDesktopPath,
|
|
normalizeDesktopWorkspacePath,
|
|
resolveDesktopExecuteCwd,
|
|
} from "./desktop-sandbox-paths.js";
|
|
|
|
describe("desktop sandbox path rules", () => {
|
|
const home = mkdtempSync(path.join(tmpdir(), "rakazo-desktop-home-"));
|
|
|
|
afterAll(() => {
|
|
rmSync(home, { recursive: true, force: true });
|
|
});
|
|
|
|
it("resolves shell cwd inside the computer home", () => {
|
|
expect(resolveDesktopExecuteCwd(undefined, home)).toBe(home);
|
|
expect(resolveDesktopExecuteCwd(".", home)).toBe(home);
|
|
expect(resolveDesktopExecuteCwd("/home/rakazo", home)).toBe(home);
|
|
expect(resolveDesktopExecuteCwd("notes/result.txt", home)).toBe(
|
|
path.join(home, "notes/result.txt"),
|
|
);
|
|
expect(resolveDesktopExecuteCwd("/home/rakazo/notes/result.txt", home)).toBe(
|
|
path.join(home, "notes/result.txt"),
|
|
);
|
|
});
|
|
|
|
it("denies host-absolute shell cwd outside the computer home", () => {
|
|
const outside = path.join(tmpdir(), "outside");
|
|
const cwd = resolveDesktopExecuteCwd(outside, home);
|
|
expect(isAllowedDesktopPath(cwd, [home])).toBe(false);
|
|
expect(isAllowedDesktopPath(resolveDesktopExecuteCwd("/etc", home), [home])).toBe(false);
|
|
});
|
|
|
|
it("rejects traversal in shell cwd requests", () => {
|
|
expect(() => resolveDesktopExecuteCwd("../outside", home)).toThrow(/escapes/i);
|
|
expect(() => resolveDesktopExecuteCwd("/home/rakazo/../outside", home)).toThrow(/escapes/i);
|
|
});
|
|
|
|
it("compares Windows roots case-insensitively without accepting siblings or other drives", () => {
|
|
const roots = ["C:\\Users\\Owner\\Rakazo\\bot"];
|
|
|
|
expect(
|
|
isAllowedDesktopPath("c:\\users\\owner\\rakazo\\BOT\\notes.txt", roots, path.win32),
|
|
).toBe(true);
|
|
expect(
|
|
isAllowedDesktopPath("C:\\Users\\Owner\\Rakazo\\bot-other\\notes.txt", roots, path.win32),
|
|
).toBe(false);
|
|
expect(isAllowedDesktopPath("D:\\notes.txt", roots, path.win32)).toBe(false);
|
|
});
|
|
|
|
it("handles explicitly allowed UNC roots without accepting sibling shares", () => {
|
|
const roots = ["\\\\server\\share\\rakazo\\bot"];
|
|
|
|
expect(
|
|
isAllowedDesktopPath("\\\\SERVER\\share\\rakazo\\BOT\\notes.txt", roots, path.win32),
|
|
).toBe(true);
|
|
expect(
|
|
isAllowedDesktopPath("\\\\server\\share-other\\rakazo\\bot\\notes.txt", roots, path.win32),
|
|
).toBe(false);
|
|
});
|
|
|
|
it.each([
|
|
"C:\\outside.txt",
|
|
"C:outside.txt",
|
|
"\\\\server\\share\\outside.txt",
|
|
"\\\\?\\C:\\outside.txt",
|
|
"notes.txt:secret",
|
|
"NUL.txt",
|
|
"COM1.log",
|
|
"trailing-dot.",
|
|
"trailing-space ",
|
|
])("rejects ambiguous or special Windows path %s", (requested) => {
|
|
expect(() => normalizeDesktopWorkspacePath(requested, "win32")).toThrow(/escapes/i);
|
|
});
|
|
|
|
it("keeps ordinary portable paths and the virtual root syntax", () => {
|
|
expect(normalizeDesktopWorkspacePath("/notes/result.txt", "win32")).toBe("notes/result.txt");
|
|
expect(normalizeDesktopWorkspacePath("notes/result.txt", "linux")).toBe("notes/result.txt");
|
|
});
|
|
});
|