From ab5ea156a32440b98fc7594ef8e0a579d535be66 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 10:49:46 -0300 Subject: [PATCH] fix(server): Remove RFC 2616 separator chars from Content-Disposition filename (#291) --- .../src/modules/filesystem/controller.ts | 20 +++++++++++++++---- .../src/providers/s3-storage.provider.ts | 20 +++++++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/apps/server/src/modules/filesystem/controller.ts b/apps/server/src/modules/filesystem/controller.ts index 2ed31fd..f85d883 100644 --- a/apps/server/src/modules/filesystem/controller.ts +++ b/apps/server/src/modules/filesystem/controller.ts @@ -12,6 +12,20 @@ export class FilesystemController { private chunkManager = ChunkManager.getInstance(); private memoryManager = DownloadMemoryManager.getInstance(); + /** + * Check if a character is valid in an HTTP token (RFC 2616) + * Tokens can contain: alphanumeric and !#$%&'*+-.^_`|~ + * Must exclude separators: ()<>@,;:\"/[]?={} and space/tab + */ + private isTokenChar(char: string): boolean { + const code = char.charCodeAt(0); + // Basic ASCII range check + if (code < 33 || code > 126) return false; + // Exclude separator characters per RFC 2616 + const separators = '()<>@,;:\\"/[]?={} \t'; + return !separators.includes(char); + } + private encodeFilenameForHeader(filename: string): string { if (!filename || filename.trim() === "") { return 'attachment; filename="download"'; @@ -36,12 +50,10 @@ export class FilesystemController { return 'attachment; filename="download"'; } + // Create ASCII-safe version with only valid token characters const asciiSafe = sanitized .split("") - .filter((char) => { - const code = char.charCodeAt(0); - return code >= 32 && code <= 126; - }) + .filter((char) => this.isTokenChar(char)) .join(""); if (asciiSafe && asciiSafe.trim()) { diff --git a/apps/server/src/providers/s3-storage.provider.ts b/apps/server/src/providers/s3-storage.provider.ts index 3378a61..94c3b98 100644 --- a/apps/server/src/providers/s3-storage.provider.ts +++ b/apps/server/src/providers/s3-storage.provider.ts @@ -14,6 +14,20 @@ export class S3StorageProvider implements StorageProvider { } } + /** + * Check if a character is valid in an HTTP token (RFC 2616) + * Tokens can contain: alphanumeric and !#$%&'*+-.^_`|~ + * Must exclude separators: ()<>@,;:\"/[]?={} and space/tab + */ + private isTokenChar(char: string): boolean { + const code = char.charCodeAt(0); + // Basic ASCII range check + if (code < 33 || code > 126) return false; + // Exclude separator characters per RFC 2616 + const separators = '()<>@,;:\\"/[]?={} \t'; + return !separators.includes(char); + } + /** * Safely encode filename for Content-Disposition header */ @@ -41,12 +55,10 @@ export class S3StorageProvider implements StorageProvider { return 'attachment; filename="download"'; } + // Create ASCII-safe version with only valid token characters const asciiSafe = sanitized .split("") - .filter((char) => { - const code = char.charCodeAt(0); - return code >= 32 && code <= 126; - }) + .filter((char) => this.isTokenChar(char)) .join(""); if (asciiSafe && asciiSafe.trim()) {