diff --git a/CHANGELOG.md b/CHANGELOG.md index c8dd9f91..bfe1b0f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## 2.0.2 + +- Fix [CVE-2025-7338](https://www.cve.org/CVERecord?id=CVE-2025-7338) ([GHSA-fjgf-rc76-4x9p](https://github.com/expressjs/multer/security/advisories/GHSA-fjgf-rc76-4x9p)) + + ## 2.0.1 - Fix [CVE-2025-48997](https://www.cve.org/CVERecord?id=CVE-2025-48997) ([GHSA-g5hg-p3ph-g8qg](https://github.com/expressjs/multer/security/advisories/GHSA-g5hg-p3ph-g8qg)) diff --git a/lib/make-middleware.js b/lib/make-middleware.js index dc3483b4..f79168b7 100644 --- a/lib/make-middleware.js +++ b/lib/make-middleware.js @@ -101,6 +101,15 @@ function makeMiddleware (setup) { // handle files busboy.on('file', function (fieldname, fileStream, { filename, encoding, mimeType }) { + var pendingWritesIncremented = false + + fileStream.on('error', function (err) { + if (pendingWritesIncremented) { + pendingWrites.decrement() + } + abortWithError(err) + }) + if (fieldname == null) return abortWithCode('MISSING_FIELD_NAME') // filename is not required (https://tools.ietf.org/html/rfc1867) but if @@ -134,6 +143,7 @@ function makeMiddleware (setup) { } var aborting = false + pendingWritesIncremented = true pendingWrites.increment() Object.defineProperty(file, 'stream', { @@ -142,11 +152,6 @@ function makeMiddleware (setup) { value: fileStream }) - fileStream.on('error', function (err) { - pendingWrites.decrement() - abortWithError(err) - }) - fileStream.on('limit', function () { aborting = true abortWithCode('LIMIT_FILE_SIZE', fieldname) diff --git a/package.json b/package.json index a5d3ac73..de464f55 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "multer", "description": "Middleware for handling `multipart/form-data`.", - "version": "2.0.1", + "version": "2.0.2", "contributors": [ "Hage Yaapa (http://www.hacksparrow.com)", "Jaret Pfluger ", diff --git a/test/express-integration.js b/test/express-integration.js index ff1757b4..6860f4f6 100644 --- a/test/express-integration.js +++ b/test/express-integration.js @@ -196,4 +196,46 @@ describe('Express Integration', function () { req.write(body) req.end() }) + + it('should not crash on malformed multipart body with bad boundary', function (done) { + var upload = multer() + + app.post('/upload3', upload.single('image'), function (req, res) { + res.status(500).end('Request should not be processed') + }) + + app.use(function (err, req, res, next) { + assert.strictEqual(err.message, 'Unexpected end of form') + res.status(200).end('Correct error') + }) + + var boundary = '----FormBoundary' + var body = [ + '------FormBoundary', + 'Content-Disposition: form-data; name="image"; filename=""', + 'Content-Type: application/octet-stream', + '', + '', // empty content + '------FormBoundar' // intentionally malformed final boundary (missing 'y') + ].join('\r\n') + + var options = { + hostname: 'localhost', + port, + path: '/upload3', + method: 'POST', + headers: { + 'Content-Type': 'multipart/form-data; boundary=' + boundary, + 'Content-Length': Buffer.byteLength(body) + } + } + + var req = http.request(options, (res) => { + assert.strictEqual(res.statusCode, 200) + done() + }) + + req.write(body) + req.end() + }) }) diff --git a/test/no-filename.js b/test/no-filename.js index 5ed55ced..9de101f8 100644 --- a/test/no-filename.js +++ b/test/no-filename.js @@ -27,9 +27,9 @@ describe('File with no filename', function () { parser(req, null, function (err) { onFinished(req, function () { assert.ifError(err) - assert.equal(req.files.length, 1) - assert.equal(req.files[0].fieldname, 'fileField') - assert.equal(req.files[0].buffer.toString(), 'foo') + assert.strict.equal(req.files.length, 1) + assert.strict.equal(req.files[0].fieldname, 'fileField') + assert.strict.equal(req.files[0].buffer.toString(), 'foo') done() }) })