diff --git a/src/infra/outbound/message-action-params.test.ts b/src/infra/outbound/message-action-params.test.ts index d1395540ae90..1570f3ae79bf 100644 --- a/src/infra/outbound/message-action-params.test.ts +++ b/src/infra/outbound/message-action-params.test.ts @@ -248,6 +248,68 @@ describe("message action media helpers", () => { } }); + maybeIt.each([ + "mediaUrl", + "media_url", + "path", + "filePath", + "file_path", + "fileUrl", + "file_url", + "url", + ])("rejects an out-of-sandbox %s hidden behind valid attachment media", async (shadowKey) => { + const sandboxRoot = await fs.mkdtemp(path.join(os.tmpdir(), "msg-params-shadow-sandbox-")); + const outsideRoot = await fs.mkdtemp(path.join(os.tmpdir(), "msg-params-shadow-host-")); + try { + await expect( + normalizeSandboxMediaParams({ + args: { + attachments: [ + { + media: "/workspace/allowed.png", + [shadowKey]: path.join(outsideRoot, "restricted.png"), + }, + ], + }, + mediaPolicy: { + mode: "sandbox", + sandboxRoot, + }, + structuredAttachments: "all", + }), + ).rejects.toThrow(/escapes sandbox root/i); + } finally { + await fs.rm(sandboxRoot, { recursive: true, force: true }); + await fs.rm(outsideRoot, { recursive: true, force: true }); + } + }); + + maybeIt("normalizes every allowed source in one structured attachment", async () => { + const sandboxRoot = await fs.mkdtemp(path.join(os.tmpdir(), "msg-params-multi-source-")); + try { + const attachment: Record = { + media: "/workspace/allowed.png", + file_path: "/workspace/allowed-file.png", + }; + + await normalizeSandboxMediaParams({ + args: { attachments: [attachment] }, + mediaPolicy: { + mode: "sandbox", + sandboxRoot, + }, + structuredAttachments: "all", + }); + + expect(attachment).toEqual({ + media: path.join(sandboxRoot, "allowed.png"), + file_path: path.join(sandboxRoot, "allowed-file.png"), + }); + } finally { + await fs.rm(sandboxRoot, { recursive: true, force: true }); + } + }); + it("collects host media source hints from the shared media-source key set", () => { expect( collectActionMediaSourceHints( diff --git a/src/infra/outbound/message-action-params.ts b/src/infra/outbound/message-action-params.ts index 3c2fc858abe4..4a507e58f54e 100644 --- a/src/infra/outbound/message-action-params.ts +++ b/src/infra/outbound/message-action-params.ts @@ -147,7 +147,6 @@ export function collectAttachmentSources( readToolStringParam(item, "contentType") ?? readToolStringParam(item, "mimeType"), filename: readToolStringParam(item, "filename") ?? readToolStringParam(item, "name"), }); - break; } } return sources;