Skip to content

Commit df31f71

Browse files
rexagodBridgeAR
authored andcommitted
http2: header field valid checks
checks validity for request, writeHead, and setHeader methods PR-URL: #33193 Refs: #29829 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Robert Nagy <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 2c0a4fa commit df31f71

File tree

4 files changed

+84
-2
lines changed

4 files changed

+84
-2
lines changed

Diff for: lib/internal/http2/compat.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,13 @@ const {
4646
hideStackFrames
4747
} = require('internal/errors');
4848
const { validateString } = require('internal/validators');
49-
const { kSocket, kRequest, kProxySocket } = require('internal/http2/util');
49+
const {
50+
kSocket,
51+
kRequest,
52+
kProxySocket,
53+
assertValidPseudoHeader
54+
} = require('internal/http2/util');
55+
const { _checkIsHttpToken: checkIsHttpToken } = require('_http_common');
5056

5157
const kBeginSend = Symbol('begin-send');
5258
const kState = Symbol('state');
@@ -590,6 +596,11 @@ class Http2ServerResponse extends Stream {
590596
return;
591597
}
592598

599+
if (name[0] === ':')
600+
assertValidPseudoHeader(name);
601+
else if (!checkIsHttpToken(name))
602+
this.destroy(new ERR_INVALID_HTTP_TOKEN('Header name', name));
603+
593604
this[kHeaders][name] = value;
594605
}
595606

Diff for: lib/internal/http2/core.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const {
99
MathMin,
1010
ObjectAssign,
1111
ObjectCreate,
12+
ObjectKeys,
1213
ObjectDefineProperty,
1314
ObjectPrototypeHasOwnProperty,
1415
Promise,
@@ -35,7 +36,10 @@ const tls = require('tls');
3536
const { URL } = require('url');
3637
const { setImmediate } = require('timers');
3738

38-
const { kIncomingMessage } = require('_http_common');
39+
const {
40+
kIncomingMessage,
41+
_checkIsHttpToken: checkIsHttpToken
42+
} = require('_http_common');
3943
const { kServerResponse } = require('_http_server');
4044
const JSStreamSocket = require('internal/js_stream_socket');
4145

@@ -88,6 +92,7 @@ const {
8892
ERR_INVALID_ARG_TYPE,
8993
ERR_INVALID_CALLBACK,
9094
ERR_INVALID_CHAR,
95+
ERR_INVALID_HTTP_TOKEN,
9196
ERR_INVALID_OPT_VALUE,
9297
ERR_OUT_OF_RANGE,
9398
ERR_SOCKET_CLOSED
@@ -108,6 +113,7 @@ const { onServerStream,
108113

109114
const {
110115
assertIsObject,
116+
assertValidPseudoHeader,
111117
assertValidPseudoHeaderResponse,
112118
assertValidPseudoHeaderTrailer,
113119
assertWithinRange,
@@ -1602,6 +1608,15 @@ class ClientHttp2Session extends Http2Session {
16021608

16031609
this[kUpdateTimer]();
16041610

1611+
if (headers !== null && headers !== undefined) {
1612+
for (const header of ObjectKeys(headers)) {
1613+
if (header[0] === ':') {
1614+
assertValidPseudoHeader(header);
1615+
} else if (header && !checkIsHttpToken(header))
1616+
this.destroy(new ERR_INVALID_HTTP_TOKEN('Header name', header));
1617+
}
1618+
}
1619+
16051620
assertIsObject(headers, 'headers');
16061621
assertIsObject(options, 'options');
16071622

Diff for: lib/internal/http2/util.js

+1
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,7 @@ function sessionName(type) {
595595

596596
module.exports = {
597597
assertIsObject,
598+
assertValidPseudoHeader,
598599
assertValidPseudoHeaderResponse,
599600
assertValidPseudoHeaderTrailer,
600601
assertWithinRange,
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto) { common.skip('missing crypto'); }
4+
const assert = require('assert');
5+
const http2 = require('http2');
6+
7+
const server1 = http2.createServer();
8+
9+
server1.listen(0, common.mustCall(() => {
10+
const session = http2.connect(`http://localhost:${server1.address().port}`);
11+
// Check for req headers
12+
session.request({ 'no underscore': 123 });
13+
session.on('error', common.mustCall((e) => {
14+
assert.strictEqual(e.code, 'ERR_INVALID_HTTP_TOKEN');
15+
server1.close();
16+
}));
17+
}));
18+
19+
const server2 = http2.createServer(common.mustCall((req, res) => {
20+
// check for setHeader
21+
res.setHeader('x y z', 123);
22+
res.end();
23+
}));
24+
25+
server2.listen(0, common.mustCall(() => {
26+
const session = http2.connect(`http://localhost:${server2.address().port}`);
27+
const req = session.request();
28+
req.on('error', common.mustCall((e) => {
29+
assert.strictEqual(e.code, 'ERR_HTTP2_STREAM_ERROR');
30+
session.close();
31+
server2.close();
32+
}));
33+
}));
34+
35+
const server3 = http2.createServer(common.mustCall((req, res) => {
36+
// check for writeHead
37+
assert.throws(common.mustCall(() => {
38+
res.writeHead(200, {
39+
'an invalid header': 123
40+
});
41+
}), {
42+
code: 'ERR_HTTP2_INVALID_STREAM'
43+
});
44+
res.end();
45+
}));
46+
47+
server3.listen(0, common.mustCall(() => {
48+
const session = http2.connect(`http://localhost:${server3.address().port}`);
49+
const req = session.request();
50+
req.on('error', common.mustCall((e) => {
51+
assert.strictEqual(e.code, 'ERR_HTTP2_STREAM_ERROR');
52+
server3.close();
53+
session.close();
54+
}));
55+
}));

0 commit comments

Comments
 (0)