From 1c7f6d76e58359daddf38a6e636dd801ffd9c61b Mon Sep 17 00:00:00 2001 From: Jay Date: Sat, 18 Apr 2026 15:45:42 +0200 Subject: [PATCH] fix: enforce max body length when max redirects is 0 (#10753) --- lib/adapters/http.js | 36 ++++++++++++++- tests/unit/adapters/http.test.js | 76 ++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/lib/adapters/http.js b/lib/adapters/http.js index 9412c273..0e8062e2 100755 --- a/lib/adapters/http.js +++ b/lib/adapters/http.js @@ -967,7 +967,41 @@ export default isHttpAdapterSupported && } }); - data.pipe(req); + // Enforce maxBodyLength for streamed uploads on the native http/https + // transport (maxRedirects === 0); follow-redirects enforces it on the + // other path. See GHSA-5c9x-8gcm-mpgx. + let uploadStream = data; + if (config.maxBodyLength > -1 && config.maxRedirects === 0) { + const limit = config.maxBodyLength; + let bytesSent = 0; + uploadStream = stream.pipeline( + [ + data, + new stream.Transform({ + transform(chunk, _enc, cb) { + bytesSent += chunk.length; + if (bytesSent > limit) { + return cb( + new AxiosError( + 'Request body larger than maxBodyLength limit', + AxiosError.ERR_BAD_REQUEST, + config, + req + ) + ); + } + cb(null, chunk); + }, + }), + ], + utils.noop + ); + uploadStream.on('error', (err) => { + if (!req.destroyed) req.destroy(err); + }); + } + + uploadStream.pipe(req); } else { data && req.write(data); req.end(); diff --git a/tests/unit/adapters/http.test.js b/tests/unit/adapters/http.test.js index 3b492f29..7c2a7b94 100644 --- a/tests/unit/adapters/http.test.js +++ b/tests/unit/adapters/http.test.js @@ -988,6 +988,82 @@ describe('supports http with nodejs', () => { } }); + it('should enforce maxBodyLength for streamed uploads with maxRedirects: 0 (GHSA-5c9x-8gcm-mpgx)', async () => { + let bytesReceived = 0; + const server = await startHTTPServer( + (req, res) => { + req.on('data', (chunk) => { + bytesReceived += chunk.length; + }); + req.on('end', () => { + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify({ received: bytesReceived })); + }); + }, + { port: SERVER_PORT } + ); + + try { + const size = 2 * 1024 * 1024; + const buf = Buffer.alloc(size, 0x61); + const source = stream.Readable.from([buf]); + + await assert.rejects( + axios.post(`http://localhost:${server.address().port}/`, source, { + maxBodyLength: 1024, + maxRedirects: 0, + headers: { 'Content-Type': 'application/octet-stream' }, + }), + (error) => { + assert.strictEqual(error.message, 'Request body larger than maxBodyLength limit'); + return true; + } + ); + + assert.ok( + bytesReceived <= 1024 * 4, + `server should not receive full payload; got ${bytesReceived}` + ); + } finally { + await stopHTTPServer(server); + } + }); + + it('should allow streamed uploads under maxBodyLength with maxRedirects: 0', async () => { + let bytesReceived = 0; + const server = await startHTTPServer( + (req, res) => { + req.on('data', (chunk) => { + bytesReceived += chunk.length; + }); + req.on('end', () => { + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify({ received: bytesReceived })); + }); + }, + { port: SERVER_PORT } + ); + + try { + const payload = Buffer.alloc(512, 0x62); + const source = stream.Readable.from([payload]); + + const response = await axios.post( + `http://localhost:${server.address().port}/`, + source, + { + maxBodyLength: 1024, + maxRedirects: 0, + headers: { 'Content-Type': 'application/octet-stream' }, + } + ); + + assert.strictEqual(response.data.received, payload.length); + } finally { + await stopHTTPServer(server); + } + }); + it('should properly support default max body length (follow-redirects as well)', async () => { // Taken from follow-redirects defaults. const followRedirectsMaxBodyDefaults = 10 * 1024 * 1024;