From cb10b45e95ed2517f8ede5a4b75ef336369e045e Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 9 Jun 2026 12:11:16 +0100 Subject: [PATCH] fix: encode attachment filename header to support non-ASCII characters --- apps/web/src/pages/api/upload/attachment.ts | 9 ++++++++- apps/web/src/pages/api/upload/avatar.ts | 9 ++++++++- apps/web/src/views/card/components/AttachmentUpload.tsx | 4 ++-- apps/web/src/views/settings/components/Avatar.tsx | 2 +- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/apps/web/src/pages/api/upload/attachment.ts b/apps/web/src/pages/api/upload/attachment.ts index 7c52d1c6..bdd1cfe0 100644 --- a/apps/web/src/pages/api/upload/attachment.ts +++ b/apps/web/src/pages/api/upload/attachment.ts @@ -67,8 +67,15 @@ export default withRateLimit( return res.status(400).json({ error: "File too large" }); } - const originalFilenameHeader = + const rawFilenameHeader = (req.headers["x-original-filename"] as string | undefined) ?? "file"; + const originalFilenameHeader = (() => { + try { + return decodeURIComponent(rawFilenameHeader); + } catch { + return rawFilenameHeader; + } + })(); const sanitizedFilename = originalFilenameHeader .replace(/[^a-zA-Z0-9._-]/g, "_") diff --git a/apps/web/src/pages/api/upload/avatar.ts b/apps/web/src/pages/api/upload/avatar.ts index ac7074f3..dbd8885c 100644 --- a/apps/web/src/pages/api/upload/avatar.ts +++ b/apps/web/src/pages/api/upload/avatar.ts @@ -64,8 +64,15 @@ export default withRateLimit( return res.status(400).json({ error: "File too large" }); } - const originalFilenameHeader = + const rawFilenameHeader = (req.headers["x-original-filename"] as string | undefined) ?? "file"; + const originalFilenameHeader = (() => { + try { + return decodeURIComponent(rawFilenameHeader); + } catch { + return rawFilenameHeader; + } + })(); const sanitizedFilename = originalFilenameHeader .replace(/[^a-zA-Z0-9._-]/g, "_") diff --git a/apps/web/src/views/card/components/AttachmentUpload.tsx b/apps/web/src/views/card/components/AttachmentUpload.tsx index 3547bbc1..08d48488 100644 --- a/apps/web/src/views/card/components/AttachmentUpload.tsx +++ b/apps/web/src/views/card/components/AttachmentUpload.tsx @@ -1,4 +1,5 @@ import { t } from "@lingui/core/macro"; +import { env } from "next-runtime-env"; import { useRef, useState } from "react"; import { HiOutlinePaperClip } from "react-icons/hi"; import { HiCheckBadge } from "react-icons/hi2"; @@ -7,7 +8,6 @@ import { twMerge } from "tailwind-merge"; import Button from "~/components/Button"; import { useModal } from "~/providers/modal"; import { usePopup } from "~/providers/popup"; -import { env } from "next-runtime-env"; import { api } from "~/utils/api"; import { invalidateCard } from "~/utils/cardInvalidation"; @@ -30,7 +30,7 @@ export function AttachmentUpload({ cardPublicId }: { cardPublicId: string }) { method: "POST", headers: { "Content-Type": file.type, - "x-original-filename": file.name, + "x-original-filename": encodeURIComponent(file.name), }, body: file, }, diff --git a/apps/web/src/views/settings/components/Avatar.tsx b/apps/web/src/views/settings/components/Avatar.tsx index d1e24e0b..6a6062b4 100644 --- a/apps/web/src/views/settings/components/Avatar.tsx +++ b/apps/web/src/views/settings/components/Avatar.tsx @@ -172,7 +172,7 @@ export default function Avatar({ method: "POST", headers: { "Content-Type": blob.type, - "x-original-filename": fileName, + "x-original-filename": encodeURIComponent(fileName), }, body: blob, },