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>
88 lines
3.5 KiB
TypeScript
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",
|
|
);
|
|
});
|
|
});
|