evOo-local/packages/adapters/src/desktop-sandbox-paths.test.ts
Cursor Agent a91fc1cb98 fix(desktop): deny shell cwd outside bot computer home
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>
2026-09-22 06:21:08 +00:00

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");
});
});