Files
openclaw/src/media/read-capability.test.ts
zhang-guiping 608fa52c80 fix(media): keep explicit workspace roots scoped
Fixes MEDIA delivery for agent workspaces named `workspace-*` by carrying the explicit resolved workspace directory into scoped outbound media local roots. The unscoped default local media boundary remains closed for `workspace-*` sibling directories.

Proof:
- node scripts/run-vitest.mjs src/media/read-capability.test.ts src/media/local-media-access.test.ts
- pnpm exec oxfmt --write --threads=1 src/media/read-capability.ts src/media/read-capability.test.ts src/media/local-media-access.test.ts
- node scripts/run-vitest.mjs src/media/read-capability.test.ts src/media/local-media-access.test.ts src/auto-reply/reply/reply-media-paths.test.ts
- /Users/steipete/Projects/agent-scripts/skills/autoreview/scripts/autoreview --mode branch --base origin/main

Fixes #86879.
2026-05-27 05:24:07 +01:00

167 lines
4.8 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/types.js";
import { getDefaultMediaLocalRoots } from "./local-roots.js";
import { resolveAgentScopedOutboundMediaAccess } from "./read-capability.js";
vi.mock("../channels/plugins/index.js", () => ({
getChannelPlugin: () => undefined,
}));
describe("resolveAgentScopedOutboundMediaAccess", () => {
afterEach(() => {
vi.unstubAllEnvs();
});
it("preserves caller-provided workspaceDir from mediaAccess", () => {
const result = resolveAgentScopedOutboundMediaAccess({
cfg: {} as OpenClawConfig,
mediaAccess: { workspaceDir: "/tmp/media-workspace" },
});
expect(Object.keys(result)).toStrictEqual(["localRoots", "readFile", "workspaceDir"]);
expect(result.localRoots).toStrictEqual([
...getDefaultMediaLocalRoots(),
"/tmp/media-workspace",
]);
expect(typeof result.readFile).toBe("function");
expect(result.workspaceDir).toBe("/tmp/media-workspace");
});
it("prefers explicit workspaceDir over mediaAccess.workspaceDir", () => {
const result = resolveAgentScopedOutboundMediaAccess({
cfg: {} as OpenClawConfig,
workspaceDir: "/tmp/explicit-workspace",
mediaAccess: { workspaceDir: "/tmp/media-workspace" },
});
expect(Object.keys(result)).toStrictEqual(["localRoots", "readFile", "workspaceDir"]);
expect(result.localRoots).toStrictEqual([
...getDefaultMediaLocalRoots(),
"/tmp/explicit-workspace",
]);
expect(typeof result.readFile).toBe("function");
expect(result.workspaceDir).toBe("/tmp/explicit-workspace");
});
it("keeps explicit workspaceDir in localRoots when agent id is unavailable", () => {
const workspaceDir = "/tmp/openclaw-home/workspace-xiaoqian";
const result = resolveAgentScopedOutboundMediaAccess({
cfg: {
tools: {
fs: { workspaceOnly: true },
},
} as OpenClawConfig,
workspaceDir,
mediaSources: [`${workspaceDir}/report.html`],
});
expect(result.localRoots).toContain(workspaceDir);
expect(result.workspaceDir).toBe(workspaceDir);
});
it("does not enable host reads when sender group policy denies read", () => {
const cfg: OpenClawConfig = {
tools: {
allow: ["read"],
},
channels: {
requestchat: {
groups: {
ops: {
toolsBySender: {
"id:attacker": {
deny: ["read"],
},
},
},
},
},
},
};
const result = resolveAgentScopedOutboundMediaAccess({
cfg,
sessionKey: "agent:main:requestchat:group:ops",
mediaSources: ["/Users/peter/Pictures/photo.png"],
// Production call sites set messageProvider: undefined when sessionKey is present;
// resolveGroupToolPolicy derives channel from the session key instead.
requesterSenderId: "attacker",
});
expect(result.readFile).toBeUndefined();
expect(result.localRoots).not.toContain("/Users/peter/Pictures");
});
it("keeps host reads enabled when sender group policy allows read", () => {
const cfg: OpenClawConfig = {
tools: {
allow: ["read"],
},
channels: {
requestchat: {
groups: {
ops: {
toolsBySender: {
"id:trusted-user": {
allow: ["read"],
},
},
},
},
},
},
};
const result = resolveAgentScopedOutboundMediaAccess({
cfg,
sessionKey: "agent:main:requestchat:group:ops",
mediaSources: ["/Users/peter/Pictures/photo.png"],
requesterSenderId: "trusted-user",
});
expect(result.readFile).toBeTypeOf("function");
expect(result.localRoots).toContain("/Users/peter/Pictures");
});
it("keeps host reads enabled when no group policy applies", () => {
const result = resolveAgentScopedOutboundMediaAccess({
cfg: {
tools: {
allow: ["read"],
},
} as OpenClawConfig,
messageProvider: "requestchat",
requesterSenderId: "trusted-user",
});
expect(result.readFile).toBeTypeOf("function");
});
it("keeps host reads enabled for DM sender when no group context exists", () => {
const result = resolveAgentScopedOutboundMediaAccess({
cfg: {
tools: {
allow: ["read"],
},
channels: {
requestchat: {
groups: {
ops: {
toolsBySender: {
"id:dm-sender": {
deny: ["read"],
},
},
},
},
},
},
} as OpenClawConfig,
messageProvider: "requestchat",
requesterSenderId: "dm-sender",
});
expect(result.readFile).toBeTypeOf("function");
});
});