diff --git a/lib/web/fetch/formdata-parser.js b/lib/web/fetch/formdata-parser.js index a4622ae10a3..c29a26fc34d 100644 --- a/lib/web/fetch/formdata-parser.js +++ b/lib/web/fetch/formdata-parser.js @@ -76,6 +76,8 @@ function multipartFormDataParser (input, mimeType) { const boundary = Buffer.from(`--${boundaryString}`, 'utf8') + const crlfDashBoundary = Buffer.from(`\r\n--${boundaryString}`, 'utf8') + // 3. Let entry list be an empty entry list. const entryList = [] @@ -140,15 +142,14 @@ function multipartFormDataParser (input, mimeType) { let body // 5.8. Body loop: While position is not past the end of input: - // TODO: the steps here are completely wrong { - const boundaryIndex = input.indexOf(boundary.subarray(2), position.position) + const boundaryIndex = input.indexOf(crlfDashBoundary, position.position) if (boundaryIndex === -1) { throw parsingError('expected boundary after body') } - body = input.subarray(position.position, boundaryIndex - 4) + body = input.subarray(position.position, boundaryIndex) position.position += body.length diff --git a/test/busboy/formdata-boundary-token.js b/test/busboy/formdata-boundary-token.js new file mode 100644 index 00000000000..8e7bbdade9d --- /dev/null +++ b/test/busboy/formdata-boundary-token.js @@ -0,0 +1,36 @@ +'use strict' + +const { test } = require('node:test') +const { Request } = require('../..') + +test('multipart formdata body containing the boundary token', async (t) => { + t.plan(2) + + const boundary = '----formdata-undici-0.6204674738279623' + // Both values embed the bare boundary token. It only marks a real delimiter + // when it forms a "\r\n--boundary" line, so these bytes must stay in the body. + const field = `a${boundary}b` + const file = `--${boundary}--` + + const request = new Request('http://localhost', { + method: 'POST', + headers: { + 'Content-Type': `multipart/form-data; boundary=${boundary}` + }, + body: + `--${boundary}\r\n` + + 'Content-Disposition: form-data; name="field"\r\n' + + '\r\n' + + `${field}\r\n` + + `--${boundary}\r\n` + + 'Content-Disposition: form-data; name="file"; filename="f.txt"\r\n' + + 'Content-Type: text/plain\r\n' + + '\r\n' + + `${file}\r\n` + + `--${boundary}--` + }) + + const form = await request.formData() + t.assert.strictEqual(form.get('field'), field) + t.assert.strictEqual(await form.get('file').text(), file) +})