


























@@ -43,6 +43,18 @@ function createImageSourceLimits(allowedMimes: string[], allowUrl = false) {
4343};
4444}
454546+function createFileSourceLimits(allowedMimes: string[], allowUrl = false) {
47+return {
48+ allowUrl,
49+allowedMimes: new Set(allowedMimes),
50+maxBytes: 1024 * 1024,
51+maxChars: 100,
52+maxRedirects: 0,
53+timeoutMs: allowUrl ? 1000 : 1,
54+pdf: { maxPages: 1, maxPixels: 1, minTextChars: 1 },
55+};
56+}
57+4658function mockUrlFetchResponse(params: {
4759source: Parameters<typeof extractImageContentFromSource>[0];
4860fetchedUrl?: string;
@@ -267,6 +279,56 @@ describe("fetchWithGuard", () => {
267279});
268280});
269281282+describe("input file MIME sniffing", () => {
283+it("rejects base64 files whose bytes sniff as an unsupported image despite a text media type", async () => {
284+detectMimeMock.mockResolvedValueOnce("image/png");
285+286+await expect(
287+extractFileContentFromSource({
288+source: {
289+type: "base64",
290+data: Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]).toString("base64"),
291+mediaType: "text/plain",
292+filename: "note.txt",
293+},
294+limits: createFileSourceLimits(["text/plain", "application/pdf"]),
295+}),
296+).rejects.toThrow("Unsupported file MIME type: image/png");
297+});
298+299+it("rejects URL files whose bytes sniff as an unsupported image despite a text content-type", async () => {
300+mockUrlFetchResponse({
301+source: { type: "url", url: "https://example.com/note.txt", mediaType: "text/plain" },
302+fetchedContentType: "text/plain",
303+fetchedBody: Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
304+});
305+detectMimeMock.mockResolvedValueOnce("image/png");
306+307+await expect(
308+extractFileContentFromSource({
309+source: { type: "url", url: "https://example.com/note.txt", mediaType: "text/plain" },
310+limits: createFileSourceLimits(["text/plain", "application/pdf"], true),
311+}),
312+).rejects.toThrow("Unsupported file MIME type: image/png");
313+});
314+315+it("rejects generic zip bytes mislabeled as text", async () => {
316+detectMimeMock.mockResolvedValueOnce("application/zip");
317+318+await expect(
319+extractFileContentFromSource({
320+source: {
321+type: "base64",
322+data: Buffer.from("PK\u0003\u0004fake-zip").toString("base64"),
323+mediaType: "text/plain",
324+filename: "notes.txt",
325+},
326+limits: createFileSourceLimits(["text/plain"]),
327+}),
328+).rejects.toThrow("Unsupported file MIME type: application/zip");
329+});
330+});
331+270332describe("base64 size guards", () => {
271333it.each([
272334{
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。