Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e08514e

Browse filesBrowse files
SRHerzogtargos
authored andcommitted
http: fix validation of "Link" header
Updated regex for "Link" header validation to better match the specification in RFC 8288 section 3. Does not check for valid URI format but handles the rest of the header more permissively than before. Alternative to another outstanding PR that disables validation entirely. Fixes: #46453 Refs: https://www.rfc-editor.org/rfc/rfc8288.html#section-3 Refs: #46464 PR-URL: #46466 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
1 parent e0d098b commit e08514e
Copy full SHA for e08514e

File tree

Expand file treeCollapse file tree

3 files changed

+54
-28
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

3 files changed

+54
-28
lines changed
Open diff view settings
Collapse file

‎lib/internal/validators.js‎

Copy file name to clipboardExpand all lines: lib/internal/validators.js
+9-1Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,15 @@ function validateUnion(value, name, union) {
459459
}
460460
}
461461

462-
const linkValueRegExp = /^(?:<[^>]*>;)\s*(?:rel=(")?[^;"]*\1;?)\s*(?:(?:as|anchor|title|crossorigin|disabled|fetchpriority|rel|referrerpolicy)=(")?[^;"]*\2)?$/;
462+
/*
463+
The rules for the Link header field are described here:
464+
https://www.rfc-editor.org/rfc/rfc8288.html#section-3
465+
466+
This regex validates any string surrounded by angle brackets
467+
(not necessarily a valid URI reference) followed by zero or more
468+
link-params separated by semicolons.
469+
*/
470+
const linkValueRegExp = /^(?:<[^>]*>)(?:\s*;\s*[^;"\s]+(?:=(")?[^;"\s]*\1)?)*$/;
463471

464472
/**
465473
* @param {any} value
Collapse file

‎test/parallel/test-http-early-hints-invalid-argument.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-http-early-hints-invalid-argument.js
+41-25Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,44 @@ const debug = require('node:util').debuglog('test');
66

77
const testResBody = 'response content\n';
88

9-
const server = http.createServer(common.mustCall((req, res) => {
10-
debug('Server sending early hints...');
11-
res.writeEarlyHints('bad argument type');
12-
13-
debug('Server sending full response...');
14-
res.end(testResBody);
15-
}));
16-
17-
server.listen(0, common.mustCall(() => {
18-
const req = http.request({
19-
port: server.address().port, path: '/'
20-
});
21-
22-
req.end();
23-
debug('Client sending request...');
24-
25-
req.on('information', common.mustNotCall());
26-
27-
process.on('uncaughtException', (err) => {
28-
debug(`Caught an exception: ${JSON.stringify(err)}`);
29-
if (err.name === 'AssertionError') throw err;
30-
assert.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE');
31-
process.exit(0);
32-
});
33-
}));
9+
{
10+
const server = http.createServer(common.mustCall((req, res) => {
11+
debug('Server sending early hints...');
12+
assert.throws(() => {
13+
res.writeEarlyHints('bad argument type');
14+
}, (err) => err.code === 'ERR_INVALID_ARG_TYPE');
15+
16+
assert.throws(() => {
17+
res.writeEarlyHints({
18+
link: '</>; '
19+
});
20+
}, (err) => err.code === 'ERR_INVALID_ARG_VALUE');
21+
22+
assert.throws(() => {
23+
res.writeEarlyHints({
24+
link: 'rel=preload; </scripts.js>'
25+
});
26+
}, (err) => err.code === 'ERR_INVALID_ARG_VALUE');
27+
28+
assert.throws(() => {
29+
res.writeEarlyHints({
30+
link: 'invalid string'
31+
});
32+
}, (err) => err.code === 'ERR_INVALID_ARG_VALUE');
33+
34+
debug('Server sending full response...');
35+
res.end(testResBody);
36+
server.close();
37+
}));
38+
39+
server.listen(0, common.mustCall(() => {
40+
const req = http.request({
41+
port: server.address().port, path: '/'
42+
});
43+
44+
req.end();
45+
debug('Client sending request...');
46+
47+
req.on('information', common.mustNotCall());
48+
}));
49+
}
Collapse file

‎test/parallel/test-http-early-hints.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-http-early-hints.js
+4-2Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ const testResBody = 'response content\n';
5858
res.writeEarlyHints({
5959
link: [
6060
'</styles.css>; rel=preload; as=style',
61-
'</scripts.js>; rel=preload; as=script',
61+
'</scripts.js>; crossorigin; rel=preload; as=script',
62+
'</scripts.js>; rel=preload; as=script; crossorigin',
6263
]
6364
});
6465

@@ -75,7 +76,8 @@ const testResBody = 'response content\n';
7576
req.on('information', common.mustCall((res) => {
7677
assert.strictEqual(
7778
res.headers.link,
78-
'</styles.css>; rel=preload; as=style, </scripts.js>; rel=preload; as=script'
79+
'</styles.css>; rel=preload; as=style, </scripts.js>; crossorigin; ' +
80+
'rel=preload; as=script, </scripts.js>; rel=preload; as=script; crossorigin'
7981
);
8082
}));
8183

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.