fix(files): RFC 5987 encode Content-Disposition filenames (#4713)

This commit is contained in:
Waleed
2026-05-21 18:58:56 -07:00
committed by GitHub
parent 21c956cf97
commit 543d2faa70
4 changed files with 8 additions and 5 deletions
+3 -2
View File
@@ -13,6 +13,7 @@ import { USE_BLOB_STORAGE } from '@/lib/uploads/config'
import { downloadFile } from '@/lib/uploads/core/storage-service'
import { getFileMetadataById } from '@/lib/uploads/server/metadata'
import { verifyFileAccess } from '@/app/api/files/authorization'
import { encodeFilenameForHeader } from '@/app/api/files/utils'
const logger = createLogger('FilesExportAPI')
@@ -95,7 +96,7 @@ export const GET = withRouteHandler(
status: 200,
headers: {
'Content-Type': 'text/markdown; charset=utf-8',
'Content-Disposition': `attachment; filename="${mdName}"`,
'Content-Disposition': `attachment; ${encodeFilenameForHeader(mdName)}`,
'Content-Length': String(mdBytes.length),
},
})
@@ -158,7 +159,7 @@ export const GET = withRouteHandler(
status: 200,
headers: {
'Content-Type': 'application/zip',
'Content-Disposition': `attachment; filename="${zipName}"`,
'Content-Disposition': `attachment; ${encodeFilenameForHeader(zipName)}`,
'Content-Length': String(zipBuffer.length),
},
})
+1 -1
View File
@@ -191,7 +191,7 @@ function getSecureFileHeaders(filename: string, originalContentType: string) {
}
}
function encodeFilenameForHeader(storageKey: string): string {
export function encodeFilenameForHeader(storageKey: string): string {
const filename = storageKey.split('/').pop() || storageKey
const hasNonAscii = /[^\x00-\x7F]/.test(filename)
@@ -21,6 +21,7 @@ import { parseRequest } from '@/lib/api/server'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { exportFolderToZip, sanitizePathSegment } from '@/lib/workflows/operations/import-export'
import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils'
import { encodeFilenameForHeader } from '@/app/api/files/utils'
import { withAdminAuthParams } from '@/app/api/v1/admin/middleware'
import {
internalErrorResponse,
@@ -242,7 +243,7 @@ export const GET = withRouteHandler(
status: 200,
headers: {
'Content-Type': 'application/zip',
'Content-Disposition': `attachment; filename="${filename}"`,
'Content-Disposition': `attachment; ${encodeFilenameForHeader(filename)}`,
'Content-Length': arrayBuffer.byteLength.toString(),
},
})
@@ -21,6 +21,7 @@ import { parseRequest } from '@/lib/api/server'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { exportWorkspaceToZip, sanitizePathSegment } from '@/lib/workflows/operations/import-export'
import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils'
import { encodeFilenameForHeader } from '@/app/api/files/utils'
import { withAdminAuthParams } from '@/app/api/v1/admin/middleware'
import {
internalErrorResponse,
@@ -162,7 +163,7 @@ export const GET = withRouteHandler(
status: 200,
headers: {
'Content-Type': 'application/zip',
'Content-Disposition': `attachment; filename="${filename}"`,
'Content-Disposition': `attachment; ${encodeFilenameForHeader(filename)}`,
'Content-Length': arrayBuffer.byteLength.toString(),
},
})