evOo-local/packages/adapters/src/computer-support.test.ts
Cursor Agent 8ae36763a7 fix(api): scope Team Computer bot homes per member in shared Spaces
Team mode shares one disk per Space. RPC and executor paths under
bots/<otherBotId>/ were reachable with any member-owned entry bot,
leaking another member's workspace files while same-user cross-bot
access stays intentional.

Add path guards, filter bot listings, regression tests.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-09-22 06:20:49 +00:00

88 lines
3.5 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
assertTeamShellCommandAllowed,
assertTeamWorkspacePathAllowed,
displayBotWorkspacePath,
filterTeamWorkspaceEntries,
resolveBotWorkspaceCwd,
resolveBotWorkspacePath,
teamBotWorkspaceDirectory,
teamWorkspaceTargetBotId,
} from "./computer-support.js";
describe("Team Computer bot folders", () => {
it("uses the bot folder for relative paths and cwd", () => {
expect(teamBotWorkspaceDirectory("bot-1")).toBe("bots/bot-1");
expect(resolveBotWorkspacePath("team", "bot-1", "notes/result.txt")).toBe(
"bots/bot-1/notes/result.txt",
);
expect(resolveBotWorkspacePath("team", "bot-1", "")).toBe("bots/bot-1");
expect(resolveBotWorkspaceCwd("team", "bot-1", undefined)).toBe("bots/bot-1");
expect(resolveBotWorkspaceCwd("team", "bot-1", "projects/site")).toBe(
"bots/bot-1/projects/site",
);
expect(resolveBotWorkspaceCwd("team", "bot-1", "/Users/me/project")).toBe("/Users/me/project");
});
it("keeps shared, bot-root, and absolute paths at the Team root", () => {
expect(resolveBotWorkspacePath("team", "bot-1", "shared/brief.md")).toBe("shared/brief.md");
expect(resolveBotWorkspacePath("team", "bot-1", "bots/bot-2/result.md")).toBe(
"bots/bot-2/result.md",
);
expect(resolveBotWorkspacePath("team", "bot-1", "/root-note.md")).toBe("root-note.md");
expect(resolveBotWorkspacePath("team", "bot-1", "/shared/brief.md")).toBe("shared/brief.md");
});
it("presents the current bot folder as its home without hiding Team paths", () => {
expect(displayBotWorkspacePath("team", "bot-1", "notes", "bots/bot-1/notes/result.txt")).toBe(
"notes/result.txt",
);
expect(displayBotWorkspacePath("team", "bot-1", "shared", "shared/brief.md")).toBe(
"shared/brief.md",
);
expect(displayBotWorkspacePath("team", "bot-1", "bots", "bots/bot-1")).toBe("bots/bot-1");
expect(displayBotWorkspacePath("team", "bot-1", "/", "bots/bot-1")).toBe("bots/bot-1");
expect(
displayBotWorkspacePath("team", "bot-1", "bots/bot-1", "bots/bot-1/notes/result.txt"),
).toBe("bots/bot-1/notes/result.txt");
});
it("scopes Team bot homes to the caller's bots in shared Spaces", () => {
const allowed = new Set(["bot-1", "bot-2"]);
expect(teamWorkspaceTargetBotId("bots/bot-2/notes/result.txt")).toBe("bot-2");
expect(teamWorkspaceTargetBotId("shared/brief.md")).toBeNull();
expect(() =>
assertTeamWorkspacePathAllowed("bots/bot-2/notes/result.txt", allowed),
).not.toThrow();
expect(() => assertTeamWorkspacePathAllowed("bots/bot-3/secret.txt", allowed)).toThrow(
/escapes the bot computer workspace/,
);
expect(() => assertTeamShellCommandAllowed("cat bots/bot-3/secret.txt", allowed)).toThrow(
/escapes the bot computer workspace/,
);
expect(
filterTeamWorkspaceEntries(
[
{ path: "bots/bot-1", name: "bot-1" },
{ path: "bots/bot-3", name: "bot-3" },
{ path: "shared", name: "shared" },
],
allowed,
),
).toEqual([
{ path: "bots/bot-1", name: "bot-1" },
{ path: "shared", name: "shared" },
]);
});
it("leaves Private Computer paths unchanged", () => {
expect(resolveBotWorkspacePath("dedicated", "bot-1", "notes/result.txt")).toBe(
"notes/result.txt",
);
expect(resolveBotWorkspaceCwd("dedicated", "bot-1", undefined)).toBeUndefined();
expect(displayBotWorkspacePath("dedicated", "bot-1", "notes", "notes/result.txt")).toBe(
"notes/result.txt",
);
});
});