-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
There is a case of malformed requests that can take down a full nodejs app, due to an uncaught error
event thrown by busboy
Using the latest 1.4.5-lts.1
from npm, the following code results in a full crash of the app (tested on both node 14 and node 18)
const express = require('express')
const multer = require('multer')
const http = require('http')
const upload = multer({ dest: 'uploads/' })
const port = 8888
const app = express()
app.post('/upload', upload.single('file'), function (req, res) {
res.send({})
})
app.listen(port, () => {
console.log(`Listening on port ${port}`)
const boundary = 'AaB03x'
const body = [
'--' + boundary,
'Content-Disposition: form-data; name="file"; filename="test.txt"',
'Content-Type: text/plain',
'',
'test without end boundary'
].join('\r\n')
const options = {
hostname: 'localhost',
port,
path: '/upload',
method: 'POST',
headers: {
'content-type': 'multipart/form-data; boundary=' + boundary,
'content-length': body.length,
}
}
const req = http.request(options, (res) => {
console.log(res.statusCode)
})
req.on('error', (err) => {
console.error(err)
})
req.write(body)
req.end()
})
Result:
# node multer-bug.js
Listening on port 8888
events.js:377
throw er; // Unhandled 'error' event
^
Error: Unexpected end of form
at Multipart._final (/Users/max/src/tests/node_modules/busboy/lib/types/multipart.js:588:17)
at callFinal (internal/streams/writable.js:610:10)
at processTicksAndRejections (internal/process/task_queues.js:82:21)
Emitted 'error' event on Multipart instance at:
at emitErrorNT (internal/streams/destroy.js:106:8)
at emitErrorCloseNT (internal/streams/destroy.js:74:3)
at processTicksAndRejections (internal/process/task_queues.js:82:21) {
storageErrors: []
}
# back to prompt
The crash doesn't happen as soon as process.on('uncaughtException')
is set, which probably explains why this test passes https://github.com/expressjs/multer/blob/lts/test/error-handling.js#L226-L250 with the same request.
This is not happening with multer 1.4.3 (and busboy pre-1.0)
I tracked this down to the call to busboy.removeAllListeners
in https://github.com/expressjs/multer/blob/lts/lib/make-middleware.js#L40-L46 happening before busboy
emits another error
event (which I think comes from the _destroy
call).
Since I feel like this removeAllListeners
is mostly for cleanup/mem-leak prevention, I tried wrapping the removeAllListeners
call with process.nextTick
, and it does eliminate the issue.
I'm opening a PR with this fix, but I am unable to adjust the tests since they don't fail with the exact same payload