diff options
author | Matteo Collina <hello@matteocollina.com> | 2018-08-21 17:26:51 +0200 |
---|---|---|
committer | Rod Vagg <rod@vagg.org> | 2018-11-27 15:07:09 +1100 |
commit | 93dba83fb0fb46ee2ea87163f435392490b4d59b (patch) | |
tree | cce8102e93a8757b4af6b09921286d167bd0977c /test | |
parent | add20f373cfe0ab5358f0c3d8b7837ad2ee1be8c (diff) | |
download | node-new-93dba83fb0fb46ee2ea87163f435392490b4d59b.tar.gz |
deps,http: http_parser set max header size to 8KB
CVE-2018-12121
PR-URL: https://github.com/nodejs-private/node-private/pull/143
Ref: https://github.com/nodejs-private/security/issues/139
Ref: https://github.com/nodejs-private/http-parser-private/pull/2
Reviewed-By: Anatoli Papirovski <apapirovski@mac.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rod Vagg <rod@vagg.org>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Diffstat (limited to 'test')
-rw-r--r-- | test/parallel/test-http-max-headers-count.js | 6 | ||||
-rw-r--r-- | test/sequential/test-http-max-http-headers.js | 155 |
2 files changed, 158 insertions, 3 deletions
diff --git a/test/parallel/test-http-max-headers-count.js b/test/parallel/test-http-max-headers-count.js index 05f4f774c2..9fcfe316e3 100644 --- a/test/parallel/test-http-max-headers-count.js +++ b/test/parallel/test-http-max-headers-count.js @@ -28,14 +28,14 @@ let requests = 0; let responses = 0; const headers = {}; -const N = 2000; +const N = 100; for (let i = 0; i < N; ++i) { headers[`key${i}`] = i; } const maxAndExpected = [ // for server [50, 50], - [1500, 1500], + [1500, 102], [0, N + 2] // Host and Connection ]; let max = maxAndExpected[requests][0]; @@ -56,7 +56,7 @@ server.maxHeadersCount = max; server.listen(0, function() { const maxAndExpected = [ // for client [20, 20], - [1200, 1200], + [1200, 103], [0, N + 3] // Connection, Date and Transfer-Encoding ]; doRequest(); diff --git a/test/sequential/test-http-max-http-headers.js b/test/sequential/test-http-max-http-headers.js new file mode 100644 index 0000000000..ae76142a4f --- /dev/null +++ b/test/sequential/test-http-max-http-headers.js @@ -0,0 +1,155 @@ +'use strict'; + +const assert = require('assert'); +const common = require('../common'); +const http = require('http'); +const net = require('net'); +const MAX = 8 * 1024; // 8KB + +// Verify that we cannot receive more than 8KB of headers. + +function once(cb) { + let called = false; + return () => { + if (!called) { + called = true; + cb(); + } + }; +} + +function finished(client, callback) { + 'abort error end'.split(' ').forEach((e) => { + client.on(e, once(() => setImmediate(callback))); + }); +} + +function fillHeaders(headers, currentSize, valid = false) { + headers += 'a'.repeat(MAX - headers.length - 3); + // Generate valid headers + if (valid) { + // TODO(mcollina): understand why -9 is needed instead of -1 + headers = headers.slice(0, -9); + } + return headers + '\r\n\r\n'; +} + +const timeout = common.platformTimeout(10); + +function writeHeaders(socket, headers) { + const array = []; + + // this is off from 1024 so that \r\n does not get split + const chunkSize = 1000; + let last = 0; + + for (let i = 0; i < headers.length / chunkSize; i++) { + const current = (i + 1) * chunkSize; + array.push(headers.slice(last, current)); + last = current; + } + + // safety check we are chunking correctly + assert.strictEqual(array.join(''), headers); + + next(); + + function next() { + if (socket.write(array.shift())) { + if (array.length === 0) { + socket.end(); + } else { + setTimeout(next, timeout); + } + } else { + socket.once('drain', next); + } + } +} + +function test1() { + let headers = + 'HTTP/1.1 200 OK\r\n' + + 'Content-Length: 0\r\n' + + 'X-CRASH: '; + + // OK, Content-Length, 0, X-CRASH, aaa... + const currentSize = 2 + 14 + 1 + 7; + headers = fillHeaders(headers, currentSize); + + const server = net.createServer((sock) => { + sock.once('data', (chunk) => { + writeHeaders(sock, headers); + sock.resume(); + }); + }); + + server.listen(0, common.mustCall(() => { + const port = server.address().port; + const client = http.get({ port: port }, common.mustNotCall(() => {})); + + client.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'HPE_HEADER_OVERFLOW'); + server.close(); + setImmediate(test2); + })); + })); +} + +const test2 = common.mustCall(() => { + let headers = + 'GET / HTTP/1.1\r\n' + + 'Host: localhost\r\n' + + 'Agent: node\r\n' + + 'X-CRASH: '; + + // /, Host, localhost, Agent, node, X-CRASH, a... + const currentSize = 1 + 4 + 9 + 5 + 4 + 7; + headers = fillHeaders(headers, currentSize); + + const server = http.createServer(common.mustNotCall()); + + server.on('clientError', common.mustCall((err) => { + assert.strictEqual(err.code, 'HPE_HEADER_OVERFLOW'); + })); + + server.listen(0, common.mustCall(() => { + const client = net.connect(server.address().port); + client.on('connect', () => { + writeHeaders(client, headers); + client.resume(); + }); + + finished(client, common.mustCall((err) => { + server.close(); + setImmediate(test3); + })); + })); +}); + +const test3 = common.mustCall(() => { + let headers = + 'GET / HTTP/1.1\r\n' + + 'Host: localhost\r\n' + + 'Agent: node\r\n' + + 'X-CRASH: '; + + // /, Host, localhost, Agent, node, X-CRASH, a... + const currentSize = 1 + 4 + 9 + 5 + 4 + 7; + headers = fillHeaders(headers, currentSize, true); + + const server = http.createServer(common.mustCall((req, res) => { + res.end('hello world'); + setImmediate(server.close.bind(server)); + })); + + server.listen(0, common.mustCall(() => { + const client = net.connect(server.address().port); + client.on('connect', () => { + writeHeaders(client, headers); + client.resume(); + }); + })); +}); + +test1(); |