From 2d5971c88482d010af2dc14d4fc1c254dfee3284 Mon Sep 17 00:00:00 2001 From: kilisamemarisaaa <1798456934@qq.com> Date: Mon, 31 Aug 2026 12:30:28 +0800 Subject: [PATCH 1/2] fix: ignore epilogue after multipart parser finish --- lib/types/multipart.js | 5 ++++ test/types-multipart-coverage.test.js | 36 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/lib/types/multipart.js b/lib/types/multipart.js index 401f8af..7f23fe1 100644 --- a/lib/types/multipart.js +++ b/lib/types/multipart.js @@ -272,6 +272,11 @@ function Multipart (boy, cfg) { } Multipart.prototype.write = function (chunk, cb) { + if (!this.parser.writable) { + cb() + return + } + const r = this.parser.write(chunk) if (r && !this._pause) { cb() diff --git a/test/types-multipart-coverage.test.js b/test/types-multipart-coverage.test.js index 6ea0f3d..478ff61 100644 --- a/test/types-multipart-coverage.test.js +++ b/test/types-multipart-coverage.test.js @@ -49,6 +49,42 @@ test('multipart parser accepts prototype property names', async (t) => { } }) +test('multipart parser ignores an epilogue written after the closing boundary', { timeout: 1000 }, async (t) => { + const boundary = 'epilogueboundary' + const body = buildBody([[ + 'Content-Disposition: form-data; name="field"', + '', + 'value' + ]], boundary) + const busboy = new Busboy({ + headers: { 'content-type': 'multipart/form-data; boundary=' + boundary } + }) + const fields = [] + let finishes = 0 + busboy.on('field', (key, value) => fields.push([key, value])) + const finished = new Promise((resolve, reject) => { + busboy.once('error', reject) + busboy.once('finish', () => { + finishes++ + resolve() + }) + }) + + busboy.write(Buffer.from(body, 'utf8')) + await new Promise(resolve => setImmediate(resolve)) + await new Promise((resolve, reject) => { + busboy.write(Buffer.from('epilogue', 'utf8'), error => { + if (error) reject(error) + else resolve() + }) + }) + busboy.end() + + await finished + t.assert.deepStrictEqual(fields, [['field', 'value']]) + t.assert.strictEqual(finishes, 1) +}) + test('multipart parser rejects bare CR or LF in disposition parameters', async (t) => { const boundary = 'barecrlf' const dispositionValues = [ From 79db1682ea27333c422c266c81ad54234dbe1cf6 Mon Sep 17 00:00:00 2001 From: kilisamemarisaaa <1798456934@qq.com> Date: Mon, 31 Aug 2026 12:56:54 +0800 Subject: [PATCH 2/2] fix: recognize epilogue by closing boundary --- lib/types/multipart.js | 2 +- test/types-multipart-coverage.test.js | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/types/multipart.js b/lib/types/multipart.js index 7f23fe1..5544f6e 100644 --- a/lib/types/multipart.js +++ b/lib/types/multipart.js @@ -272,7 +272,7 @@ function Multipart (boy, cfg) { } Multipart.prototype.write = function (chunk, cb) { - if (!this.parser.writable) { + if (this.parser._finished) { cb() return } diff --git a/test/types-multipart-coverage.test.js b/test/types-multipart-coverage.test.js index 478ff61..1c0e77e 100644 --- a/test/types-multipart-coverage.test.js +++ b/test/types-multipart-coverage.test.js @@ -59,12 +59,17 @@ test('multipart parser ignores an epilogue written after the closing boundary', const busboy = new Busboy({ headers: { 'content-type': 'multipart/form-data; boundary=' + boundary } }) + const events = [] const fields = [] let finishes = 0 - busboy.on('field', (key, value) => fields.push([key, value])) + busboy.on('field', (key, value) => { + events.push('field') + fields.push([key, value]) + }) const finished = new Promise((resolve, reject) => { busboy.once('error', reject) - busboy.once('finish', () => { + busboy.on('finish', () => { + events.push('finish') finishes++ resolve() }) @@ -75,13 +80,18 @@ test('multipart parser ignores an epilogue written after the closing boundary', await new Promise((resolve, reject) => { busboy.write(Buffer.from('epilogue', 'utf8'), error => { if (error) reject(error) - else resolve() + else { + events.push('epilogue write') + resolve() + } }) }) busboy.end() await finished + await new Promise(resolve => setImmediate(resolve)) t.assert.deepStrictEqual(fields, [['field', 'value']]) + t.assert.deepStrictEqual(events, ['field', 'epilogue write', 'finish']) t.assert.strictEqual(finishes, 1) })