Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions lib/web/fetch/formdata-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,19 +261,27 @@ function parseContentDispositionAttribute (input, position) {
position
)

// Check for utf-8'' prefix (case insensitive)
const charsetSeparator = headerValue.indexOf(0x27)
const languageSeparator = charsetSeparator === -1
? -1
: headerValue.indexOf(0x27, charsetSeparator + 1)

if (charsetSeparator === -1 || languageSeparator === -1) {
throw parsingError('expected charset and language separators in extended value')
}

if (
charsetSeparator !== 5 ||
(headerValue[0] !== 0x75 && headerValue[0] !== 0x55) || // u or U
(headerValue[1] !== 0x74 && headerValue[1] !== 0x54) || // t or T
(headerValue[2] !== 0x66 && headerValue[2] !== 0x46) || // f or F
headerValue[3] !== 0x2d || // -
headerValue[4] !== 0x38 // 8
) {
throw parsingError('unknown encoding, expected utf-8\'\'')
throw parsingError('unknown charset, expected utf-8')
}

// Skip utf-8'' and decode the rest
value = decodeURIComponent(decoder.decode(headerValue.subarray(7)))
value = decodeURIComponent(decoder.decode(headerValue.subarray(languageSeparator + 1)))
} else if (input[position.position] === 0x22 /* " */) {
// Quoted string
position.position++ // skip opening quote
Expand Down
33 changes: 33 additions & 0 deletions test/busboy/issue-4661.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,36 @@ test('filename* with percent-encoded UTF-8 bytes is decoded', async (t) => {
t.assert.ok(file instanceof File)
t.assert.strictEqual(file.name, '\u20AC rates.txt')
})

test('filename* with a language tag is parsed correctly', async (t) => {
for (const [filename, expectedName] of [
["UTF-8'en'%E2%82%AC.txt", '\u20AC.txt'],
["utf-8'de'%C3%A4.txt", '\u00E4.txt']
]) {
const request = createFilenameRequest(filename)
const fd = await request.formData()
const file = fd.get('Abc')
t.assert.ok(file instanceof File)
t.assert.strictEqual(file.name, expectedName)
}
})

test('filename* without both separators is rejected', async (t) => {
for (const filename of ['UTF-8file.txt', "UTF-8'en.txt"]) {
const request = createFilenameRequest(filename)
await t.assert.rejects(request.formData(), TypeError)
}
})

function createFilenameRequest (filename) {
return new Request('http://localhost', {
method: 'POST',
headers: { 'Content-Type': contentType },
body:
`--${boundary}\r\n` +
`Content-Disposition: form-data; name="Abc"; filename*=${filename}\r\n` +
'\r\n' +
'Hello\r\n' +
`--${boundary}--`
})
}
Loading