Files
zpan/shared/content-disposition.test.ts
saltbo fa136a5112 fix(upload): make Content-Disposition Latin-1 safe for non-ASCII filenames
Uploading a file with a non-ASCII name (Chinese, emoji, …) failed with
"Failed to execute 'setRequestHeader' on 'XMLHttpRequest': String contains
non ISO-8859-1 code point." The presigned PUT's signed Content-Disposition put
the raw filename in the plain `filename="..."` parameter, which the browser
then rejects when setting it as an XHR request header.

Add a shared attachmentContentDisposition() helper that keeps `filename=`
ASCII-only and carries the real name in `filename*=UTF-8''`, and route all
three construction sites through it so the signed and client-returned values
stay identical (the value is part of the SigV4 signature).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 18:01:13 -04:00

36 lines
1.4 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { attachmentContentDisposition } from './content-disposition'
const isLatin1 = (value: string) => [...value].every((ch) => ch.codePointAt(0)! <= 0xff)
describe('attachmentContentDisposition', () => {
it('keeps ASCII filenames intact', () => {
expect(attachmentContentDisposition('my file.jpg')).toBe(
'attachment; filename="my file.jpg"; filename*=UTF-8\'\'my%20file.jpg',
)
})
it('produces a header value that is safe for XMLHttpRequest.setRequestHeader (ISO-8859-1 only)', () => {
const header = attachmentContentDisposition('测试文档.docx')
expect(isLatin1(header)).toBe(true)
})
it('replaces non-ASCII chars in the plain filename and carries the real name in filename*', () => {
expect(attachmentContentDisposition('测试文档.docx')).toBe(
'attachment; filename="____.docx"; filename*=UTF-8\'\'%E6%B5%8B%E8%AF%95%E6%96%87%E6%A1%A3.docx',
)
})
it('handles emoji in filenames', () => {
const header = attachmentContentDisposition('🎉party.png')
expect(isLatin1(header)).toBe(true)
expect(header).toContain("filename*=UTF-8''")
})
it('neutralizes quotes and backslashes in the plain filename', () => {
expect(attachmentContentDisposition('a"b\\c.txt')).toBe(
'attachment; filename="a_b_c.txt"; filename*=UTF-8\'\'a%22b%5Cc.txt',
)
})
})