import { describe, it, expect } from "vitest"; import { parseContentDispositionFilename, sanitizeFilename } from "./auth-fetch"; describe("parseContentDispositionFilename", () => { it("returns null for an empty header", () => { expect(parseContentDispositionFilename("")).toBeNull(); }); it("returns null when no filename directive is present", () => { expect(parseContentDispositionFilename("attachment")).toBeNull(); expect(parseContentDispositionFilename("inline; modification-date=foo")).toBeNull(); }); describe("basic form", () => { it("extracts a quoted filename", () => { expect(parseContentDispositionFilename('attachment; filename="invoice.pdf"')).toBe( "invoice.pdf", ); }); it("extracts an unquoted token filename", () => { expect(parseContentDispositionFilename("attachment; filename=invoice.pdf")).toBe( "invoice.pdf", ); }); it("preserves spaces inside a quoted value", () => { expect( parseContentDispositionFilename('attachment; filename="invoice with spaces.pdf"'), ).toBe("invoice with spaces.pdf"); }); it("matches case-insensitively", () => { expect(parseContentDispositionFilename('attachment; FileName="invoice.pdf"')).toBe( "invoice.pdf", ); }); it("tolerates whitespace around `=`", () => { expect(parseContentDispositionFilename('attachment; filename = "invoice.pdf"')).toBe( "invoice.pdf", ); }); }); describe("RFC 5987 extended form", () => { it("decodes a UTF-8 value with no language", () => { expect( parseContentDispositionFilename("attachment; filename*=UTF-8''invoice%20Jan.pdf"), ).toBe("invoice Jan.pdf"); }); it("decodes a UTF-8 value with a language tag", () => { expect( parseContentDispositionFilename("attachment; filename*=UTF-8'en'invoice%20Jan.pdf"), ).toBe("invoice Jan.pdf"); }); it("decodes non-ASCII characters", () => { // %E2%82%AC is the Euro sign. expect( parseContentDispositionFilename("attachment; filename*=UTF-8''price%20%E2%82%AC.pdf"), ).toBe("price €.pdf"); }); it("matches the charset case-insensitively", () => { expect( parseContentDispositionFilename("attachment; filename*=utf-8''invoice%20Jan.pdf"), ).toBe("invoice Jan.pdf"); }); it("prefers the extended form when both forms are present (RFC 6266)", () => { expect( parseContentDispositionFilename( "attachment; filename=\"fallback.pdf\"; filename*=UTF-8''invoice%20Jan.pdf", ), ).toBe("invoice Jan.pdf"); }); it("falls back to the basic form when the charset is non-UTF-8", () => { expect( parseContentDispositionFilename( "attachment; filename=\"basic.pdf\"; filename*=ISO-8859-1''invoice.pdf", ), ).toBe("basic.pdf"); }); it("returns null when the only filename uses a non-UTF-8 charset", () => { expect( parseContentDispositionFilename("attachment; filename*=ISO-8859-1''invoice.pdf"), ).toBeNull(); }); it("falls back to the basic form when extended percent-encoding is malformed", () => { // `%E0%` is a truncated escape — decodeURIComponent will throw. expect( parseContentDispositionFilename( "attachment; filename=\"basic.pdf\"; filename*=UTF-8''broken%E0%", ), ).toBe("basic.pdf"); }); }); }); describe("sanitizeFilename", () => { it("returns null for nullish input", () => { expect(sanitizeFilename(null)).toBeNull(); expect(sanitizeFilename("")).toBeNull(); }); it("returns the input verbatim when already safe", () => { expect(sanitizeFilename("invoice.pdf")).toBe("invoice.pdf"); }); it("trims surrounding whitespace", () => { expect(sanitizeFilename(" invoice.pdf ")).toBe("invoice.pdf"); }); it("strips POSIX path prefixes", () => { expect(sanitizeFilename("../etc/passwd")).toBe("passwd"); expect(sanitizeFilename("/tmp/invoice.pdf")).toBe("invoice.pdf"); expect(sanitizeFilename("a/b/c/invoice.pdf")).toBe("invoice.pdf"); }); it("strips Windows path prefixes", () => { expect(sanitizeFilename("C:\\Users\\me\\invoice.pdf")).toBe("invoice.pdf"); expect(sanitizeFilename("..\\..\\invoice.pdf")).toBe("invoice.pdf"); }); it("returns null when the final segment is `.` or `..`", () => { expect(sanitizeFilename(".")).toBeNull(); expect(sanitizeFilename("..")).toBeNull(); expect(sanitizeFilename("a/b/.")).toBeNull(); expect(sanitizeFilename("a/b/..")).toBeNull(); }); it("returns null when only path separators are supplied", () => { expect(sanitizeFilename("/")).toBeNull(); expect(sanitizeFilename("\\")).toBeNull(); expect(sanitizeFilename("///")).toBeNull(); }); it("preserves dots inside a normal filename", () => { expect(sanitizeFilename("invoice.v2.final.pdf")).toBe("invoice.v2.final.pdf"); }); it("preserves non-ASCII characters", () => { expect(sanitizeFilename("invoice €.pdf")).toBe("invoice €.pdf"); expect(sanitizeFilename("請求書.pdf")).toBe("請求書.pdf"); }); });