diff --git a/lib/internal/validators.js b/lib/internal/validators.js index 8127bd66fe3609..d86370176a8aa8 100644 --- a/lib/internal/validators.js +++ b/lib/internal/validators.js @@ -459,7 +459,15 @@ function validateUnion(value, name, union) { } } -const linkValueRegExp = /^(?:<[^>]*>;)\s*(?:rel=(")?[^;"]*\1;?)\s*(?:(?:as|anchor|title|crossorigin|disabled|fetchpriority|rel|referrerpolicy)=(")?[^;"]*\2)?$/; +/* + The rules for the Link header field are described here: + https://www.rfc-editor.org/rfc/rfc8288.html#section-3 + + This regex validates any string surrounded by angle brackets + (not necessarily a valid URI reference) followed by zero or more + link-params separated by semicolons. +*/ +const linkValueRegExp = /^(?:<[^>]*>)(?:\s*;\s*[^;"\s]+(?:=(")?[^;"\s]*\1)?)*$/; /** * @param {any} value diff --git a/test/parallel/test-http-early-hints-invalid-argument.js b/test/parallel/test-http-early-hints-invalid-argument.js index 2cf29666bef11d..f776bcafa40ed3 100644 --- a/test/parallel/test-http-early-hints-invalid-argument.js +++ b/test/parallel/test-http-early-hints-invalid-argument.js @@ -6,28 +6,44 @@ const debug = require('node:util').debuglog('test'); const testResBody = 'response content\n'; -const server = http.createServer(common.mustCall((req, res) => { - debug('Server sending early hints...'); - res.writeEarlyHints('bad argument type'); - - debug('Server sending full response...'); - res.end(testResBody); -})); - -server.listen(0, common.mustCall(() => { - const req = http.request({ - port: server.address().port, path: '/' - }); - - req.end(); - debug('Client sending request...'); - - req.on('information', common.mustNotCall()); - - process.on('uncaughtException', (err) => { - debug(`Caught an exception: ${JSON.stringify(err)}`); - if (err.name === 'AssertionError') throw err; - assert.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE'); - process.exit(0); - }); -})); +{ + const server = http.createServer(common.mustCall((req, res) => { + debug('Server sending early hints...'); + assert.throws(() => { + res.writeEarlyHints('bad argument type'); + }, (err) => err.code === 'ERR_INVALID_ARG_TYPE'); + + assert.throws(() => { + res.writeEarlyHints({ + link: '; ' + }); + }, (err) => err.code === 'ERR_INVALID_ARG_VALUE'); + + assert.throws(() => { + res.writeEarlyHints({ + link: 'rel=preload; ' + }); + }, (err) => err.code === 'ERR_INVALID_ARG_VALUE'); + + assert.throws(() => { + res.writeEarlyHints({ + link: 'invalid string' + }); + }, (err) => err.code === 'ERR_INVALID_ARG_VALUE'); + + debug('Server sending full response...'); + res.end(testResBody); + server.close(); + })); + + server.listen(0, common.mustCall(() => { + const req = http.request({ + port: server.address().port, path: '/' + }); + + req.end(); + debug('Client sending request...'); + + req.on('information', common.mustNotCall()); + })); +} diff --git a/test/parallel/test-http-early-hints.js b/test/parallel/test-http-early-hints.js index 7183d9516f6dda..67affdffddd95e 100644 --- a/test/parallel/test-http-early-hints.js +++ b/test/parallel/test-http-early-hints.js @@ -58,7 +58,8 @@ const testResBody = 'response content\n'; res.writeEarlyHints({ link: [ '; rel=preload; as=style', - '; rel=preload; as=script', + '; crossorigin; rel=preload; as=script', + '; rel=preload; as=script; crossorigin', ] }); @@ -75,7 +76,8 @@ const testResBody = 'response content\n'; req.on('information', common.mustCall((res) => { assert.strictEqual( res.headers.link, - '; rel=preload; as=style, ; rel=preload; as=script' + '; rel=preload; as=style, ; crossorigin; ' + + 'rel=preload; as=script, ; rel=preload; as=script; crossorigin' ); }));