From 1e3bf11cfacc78df5b2180de6bb6dbfbe1ea3b5d Mon Sep 17 00:00:00 2001 From: Pranshu Srivastava Date: Sun, 26 Apr 2020 12:55:53 +0530 Subject: [PATCH] http2: allow only valid chars in headers Refs: https://github.com/nodejs/node/issues/29829 --- lib/internal/http2/compat.js | 15 ++++++++-- test/parallel/test-http2-invalid-headers.js | 32 +++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-http2-invalid-headers.js diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 3abe9ba2ac6f82..e1788215c67fb2 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -40,6 +40,7 @@ const { ERR_HTTP2_STATUS_INVALID, ERR_INVALID_ARG_VALUE, ERR_INVALID_CALLBACK, + ERR_INVALID_CHAR, ERR_INVALID_HTTP_TOKEN, ERR_STREAM_WRITE_AFTER_END }, @@ -47,6 +48,10 @@ const { } = require('internal/errors'); const { validateString } = require('internal/validators'); const { kSocket, kRequest, kProxySocket } = require('internal/http2/util'); +const { _checkIsHttpToken: checkIsHttpToken, + _checkInvalidHeaderChar: checkInvalidHeaderChar +} = require('_http_common'); +const debug = require('internal/util/debuglog').debuglog('http2'); const kBeginSend = Symbol('begin-send'); const kState = Symbol('state'); @@ -67,15 +72,19 @@ let statusConnectionHeaderWarned = false; // close as possible to the current require('http') API const assertValidHeader = hideStackFrames((name, value) => { - if (name === '' || typeof name !== 'string') { - throw new ERR_INVALID_HTTP_TOKEN('Header name', name); - } if (isPseudoHeader(name)) { throw new ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED(); } + if (name === '' || typeof name !== 'string' || !checkIsHttpToken(name)) { + throw new ERR_INVALID_HTTP_TOKEN('Header name', name); + } if (value === undefined || value === null) { throw new ERR_HTTP2_INVALID_HEADER_VALUE(value, name); } + if (checkInvalidHeaderChar(value)) { + debug('Header "%s" contains invalid characters', name); + throw new ERR_INVALID_CHAR('header content', name); + } if (!isConnectionHeaderAllowed(name, value)) { connectionHeaderMessageWarn(); } diff --git a/test/parallel/test-http2-invalid-headers.js b/test/parallel/test-http2-invalid-headers.js new file mode 100644 index 00000000000000..e0324a93051568 --- /dev/null +++ b/test/parallel/test-http2-invalid-headers.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) { common.skip('missing crypto'); } +const h2 = require('http2'); +const assert = require('assert'); + +const server = h2.createServer(common.mustCall((req, res) => { + assert.throws(() => { + res.setHeader('foo⠊Set-Cookie', 'foo=bar'); + }, { code: 'ERR_INVALID_HTTP_TOKEN' }); + assert.throws(() => { + res.writeHead(200, { 'foo⠊Set-Cookie': 'foo=bar' }); + }, { code: 'ERR_INVALID_HTTP_TOKEN' }); + assert.throws(() => { + res.setHeader('Set-Cookie', 'foo=barഊഊ'); + }, { code: 'ERR_INVALID_CHAR' }); + assert.throws(() => { + res.writeHead(200, { + 'Set-Cookie': 'foo=barഊഊ' + }); + }, { code: 'ERR_INVALID_CHAR' }); + res.end(); +})); + +server.listen(0, common.mustCall(() => { + const session = h2.connect(`http://localhost:${server.address().port}`); + const req = session.request(); + req.on('end', () => { + session.close(); + server.close(); + }); +}));