From 6b86177ebe4f3cda807a77fd9004a368fe456bce Mon Sep 17 00:00:00 2001 From: "maxime.abehserra" Date: Tue, 12 Nov 2024 19:19:47 +0100 Subject: [PATCH 1/2] fix: content-disposition filename regexp --- src/metadata.ts | 2 +- test/metadata.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/metadata.ts b/src/metadata.ts index 7f40eb5..19bd3ca 100644 --- a/src/metadata.ts +++ b/src/metadata.ts @@ -21,7 +21,7 @@ export function normalizeMetadata(input?: File | Response | BufferLike | StreamL } if (input instanceof Response) { const contentDisposition = input.headers.get("content-disposition") - const filename = contentDisposition && contentDisposition.match(/;\s*filename\*?=["']?(.*?)["']?$/i) + const filename = contentDisposition && contentDisposition.match(/filename=['"]?([^'";]+)['"]?(?:;|$)/i) const urlName = filename && filename[1] || input.url && new URL(input.url).pathname.split("/").findLast(Boolean) const decoded = urlName && decodeURIComponent(urlName) // @ts-ignore allow coercion from null to zero diff --git a/test/metadata.test.ts b/test/metadata.test.ts index 8508b4e..398c49c 100644 --- a/test/metadata.test.ts +++ b/test/metadata.test.ts @@ -14,7 +14,7 @@ Deno.test("normalizeMetadata needs a filename along Responses with insufficient Deno.test("normalizeMetadata guesses filename from Content-Disposition", () => { const metadata = normalizeMetadata(new Response("four", { - headers: { "content-disposition": "attachment; filename=test.txt" } + headers: { "content-disposition": "attachment; filename=test.txt; size=0" } })) assertEquals(metadata, { uncompressedSize: 0n, encodedName, nameIsBuffer: false }) }) From bda1d4ed41f0ffc48ffa19220299d5204d2fe18c Mon Sep 17 00:00:00 2001 From: "maxime.abehserra" Date: Wed, 13 Nov 2024 08:58:14 +0100 Subject: [PATCH 2/2] Fix: Content-disposition filename matcher - improve regexp to work with white space + non latin filename in utf8 --- src/metadata.ts | 2 +- test/metadata.test.ts | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/metadata.ts b/src/metadata.ts index 19bd3ca..fc6145d 100644 --- a/src/metadata.ts +++ b/src/metadata.ts @@ -21,7 +21,7 @@ export function normalizeMetadata(input?: File | Response | BufferLike | StreamL } if (input instanceof Response) { const contentDisposition = input.headers.get("content-disposition") - const filename = contentDisposition && contentDisposition.match(/filename=['"]?([^'";]+)['"]?(?:;|$)/i) + const filename = contentDisposition && contentDisposition.match(/;\s*filename\*?\s*=\s*(?:UTF-\d+''|)["']?([^;"'\r\n]*)["']?(?:;|$)/i); const urlName = filename && filename[1] || input.url && new URL(input.url).pathname.split("/").findLast(Boolean) const decoded = urlName && decodeURIComponent(urlName) // @ts-ignore allow coercion from null to zero diff --git a/test/metadata.test.ts b/test/metadata.test.ts index 398c49c..605b65c 100644 --- a/test/metadata.test.ts +++ b/test/metadata.test.ts @@ -19,6 +19,14 @@ Deno.test("normalizeMetadata guesses filename from Content-Disposition", () => { assertEquals(metadata, { uncompressedSize: 0n, encodedName, nameIsBuffer: false }) }) +Deno.test("normalizeMetadata guesses filename from non latin Content-Disposition", () => { + const metadata = normalizeMetadata(new Response("four", { + headers: { "content-disposition": "attachment; filename* = UTF-8''%CF%8C%CE%BD%CE%BF%CE%BC%CE%B1%20%CE%B1%CF%81%CF%87%CE%B5%CE%AF%CE%BF%CF%85.txt" } + })) + assertEquals(metadata, { uncompressedSize: 0n,encodedName: new TextEncoder().encode("όνομα αρχείου.txt"), nameIsBuffer: false }) +}) + + Deno.test("normalizeMetadata guesses filename from a Response URL", () => { const response = Object.create(Response.prototype, { url: { get() { return "https://example.com/path/test.txt" } },