mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-03 10:14:07 +00:00
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.
167 lines
4.8 KiB
TypeScript
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");
|
|
});
|
|
});
|