diff --git a/src/plugins/multipart.js b/src/plugins/multipart.js index 1cd9c6bf..e7ff0a6e 100644 --- a/src/plugins/multipart.js +++ b/src/plugins/multipart.js @@ -119,7 +119,9 @@ function createInitMultipart(boundary) { if (ctx.name === "partData") { part.transferBuffer += ctx.buffer .slice(ctx.start, ctx.end) - .toString("ascii"); + .toString("ascii") + .replaceAll("\r", "") + .replaceAll("\n", ""); /* four bytes (chars) in base64 converts to three bytes in binary diff --git a/test/standalone/content-transfer-encoding.test.js b/test/standalone/content-transfer-encoding.test.js index 567fe994..d638284c 100644 --- a/test/standalone/content-transfer-encoding.test.js +++ b/test/standalone/content-transfer-encoding.test.js @@ -1,6 +1,7 @@ import { join } from "node:path"; import { createServer, request } from "node:http"; import { strictEqual } from "node:assert"; +import { PassThrough, Writable } from "node:stream"; import formidable from "../../src/index.js"; @@ -60,3 +61,35 @@ test("content transfer encoding", (done) => { req.end(body); }); }); + +test("base64 transfer encoding ignores line breaks", async () => { + const body = + "--foo\r\n" + + 'Content-Disposition: form-data; name="file"; filename="file"\r\n' + + "Content-Type: application/octet-stream\r\n" + + "Content-Transfer-Encoding: base64\r\n" + + "\r\n" + + "YWJj\r\nZGVm\r\nZ2hp\r\namts\r\n" + + "--foo--\r\n"; + const req = new PassThrough(); + req.headers = { + "content-type": "multipart/form-data; boundary=foo", + "content-length": Buffer.byteLength(body), + }; + const chunks = []; + const form = formidable({ + fileWriteStreamHandler: () => + new Writable({ + write(chunk, _, done) { + chunks.push(chunk); + done(); + }, + }), + }); + + const parsed = form.parse(req); + req.end(body); + await parsed; + + strictEqual(Buffer.concat(chunks).toString(), "abcdefghijkl"); +});