















@@ -7,7 +7,11 @@ import {
77createTinyJpegBuffer,
88} from "../../test/helpers/image-fixtures.js";
99import { getImageMetadata } from "../media/image-ops.js";
10-import { sanitizeContentBlocksImages, sanitizeImageBlocks } from "./tool-images.js";
10+import {
11+sanitizeContentBlocksImages,
12+sanitizeImageBlocks,
13+sanitizeToolResultImages,
14+} from "./tool-images.js";
11151216describe("tool image sanitizing", () => {
1317const getImageBlock = (
@@ -117,6 +121,83 @@ describe("tool image sanitizing", () => {
117121expect(image.data).toBe(jpeg.toString("base64"));
118122});
119123124+it("preserves data and mimeType on no-resize path", async () => {
125+const png = createSolidPngBuffer(10, 10, { r: 0, g: 0, b: 255 });
126+const base64 = png.toString("base64");
127+128+const blocks = [{ type: "image" as const, data: base64, mimeType: "image/png" }];
129+const out = await sanitizeContentBlocksImages(blocks, "test");
130+expect(out).toHaveLength(1);
131+const image = getImageBlock(out);
132+expect(typeof image.data).toBe("string");
133+expect(image.data.length).toBeGreaterThan(0);
134+expect(typeof image.mimeType).toBe("string");
135+expect(image.mimeType).toBe("image/png");
136+});
137+138+it("preserves data and mimeType on resize path", async () => {
139+const png = createSolidPngBuffer(2600, 400, { r: 255, g: 0, b: 0 });
140+const base64 = png.toString("base64");
141+142+const blocks = [{ type: "image" as const, data: base64, mimeType: "image/png" }];
143+const out = await sanitizeContentBlocksImages(blocks, "test");
144+expect(out).toHaveLength(1);
145+const image = getImageBlock(out);
146+expect(typeof image.data).toBe("string");
147+expect(image.data.length).toBeGreaterThan(0);
148+expect(typeof image.mimeType).toBe("string");
149+}, 20_000);
150+151+it("converts image blocks with missing data/mimeType to text", async () => {
152+const blocks = [
153+{
154+type: "image" as const,
155+data: undefined as unknown as string,
156+mimeType: undefined as unknown as string,
157+},
158+];
159+const out = await sanitizeContentBlocksImages(blocks, "browser:screenshot");
160+expect(out).toHaveLength(1);
161+expect(out[0].type).toBe("text");
162+expect((out[0] as { type: "text"; text: string }).text).toContain("missing data or mimeType");
163+});
164+165+it("screenshot-shaped tool result round-trips with valid image block", async () => {
166+const png = createSolidPngBuffer(100, 100, { r: 0, g: 128, b: 0 });
167+const base64 = png.toString("base64");
168+169+const result = {
170+content: [{ type: "image" as const, data: base64, mimeType: "image/png" }],
171+details: { path: "/tmp/screenshot.png" },
172+};
173+const sanitized = await sanitizeToolResultImages(result, "browser:screenshot");
174+const imageBlock = sanitized.content.find((b) => b.type === "image");
175+expect(imageBlock).toBeDefined();
176+expect(typeof (imageBlock as { data: string }).data).toBe("string");
177+expect((imageBlock as { data: string }).data.length).toBeGreaterThan(0);
178+expect(typeof (imageBlock as { mimeType: string }).mimeType).toBe("string");
179+});
180+181+it("screenshot-shaped tool result with malformed image produces text fallback", async () => {
182+const result = {
183+content: [
184+{
185+type: "image" as const,
186+data: undefined as unknown as string,
187+mimeType: undefined as unknown as string,
188+},
189+],
190+details: {},
191+};
192+const sanitized = await sanitizeToolResultImages(result, "browser:screenshot");
193+const imageBlocks = sanitized.content.filter((b) => b.type === "image");
194+expect(imageBlocks).toHaveLength(0);
195+const textFallback = sanitized.content.find(
196+(b) => b.type === "text" && (b as { text: string }).text.includes("missing data or mimeType"),
197+);
198+expect(textFallback).toBeDefined();
199+});
200+120201it("drops malformed image base64 payloads", async () => {
121202// Invalid base64 is replaced with text so malformed payloads cannot smuggle
122203// attributes or script-like text through image blocks.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。