Restify Version: 12.0.0
Node.js Version: 24.13.0
Expected behaviour
A request with a malformed Host header should not crash the server.
Actual behaviour
The process exits on a request with a malformed Host header.
TypeError: Invalid URL
at new URL (node:internal/url:828:25)
at IncomingMessage.getUrl (node_modules/restify/lib/request.js:472:23)
at Router.lookup (node_modules/restify/lib/router.js:81:24)
at Server._runRoute (node_modules/restify/lib/server.js:1115:36)
at Server._afterPre (node_modules/restify/lib/server.js:1097:10)
code: 'ERR_INVALID_URL',
input: 'http://foo|bar/x'
restify 11.1.0 is not affected.
Repro case
// server.js — npm i restify@12.0.0 && node server.js
const restify = require('restify');
const server = restify.createServer();
server.get('/x', function(req, res, next) {
res.send({ ok: 1 });
return next();
});
server.listen(8401, '127.0.0.1');
This request kills it:
# malformed Host header
curl -H 'Host: foo|bar' http://127.0.0.1:8401/x
Cause
Request.prototype.getUrl
builds a WHATWG URL from two client-supplied inputs — the Host header as the authority,
and the request target:
var base = protocol + (this.headers.host || 'localhost');
this._url = this.url.charAt(0) === '/'
? new URL(base + this.url)
: new URL(this.url, base);
Node's HTTP parser validates neither as a URL, so either can make new URL() throw.
Router.lookup
calls req.getUrl().pathname on every request, reached from
Server._runRoute.
Nothing on that path catches, and createServer defaults handleUncaughtExceptions to
false, so the exception is uncaught and the process exits.
Introduced in 12.0.0 by #1996. Before that, getUrl() was url.parse(this.url), which is
path-only and never reads the Host header.
Are you willing and able to fix this?
Yes — I have a patch and two regression tests ready, and will open a PR.
Restify Version: 12.0.0
Node.js Version: 24.13.0
Expected behaviour
A request with a malformed
Hostheader should not crash the server.Actual behaviour
The process exits on a request with a malformed
Hostheader.restify 11.1.0 is not affected.
Repro case
This request kills it:
Cause
Request.prototype.getUrlbuilds a WHATWG
URLfrom two client-supplied inputs — theHostheader as the authority,and the request target:
Node's HTTP parser validates neither as a URL, so either can make
new URL()throw.Router.lookupcalls
req.getUrl().pathnameon every request, reached fromServer._runRoute.Nothing on that path catches, and
createServerdefaultshandleUncaughtExceptionstofalse, so the exception is uncaught and the process exits.Introduced in 12.0.0 by #1996. Before that,
getUrl()wasurl.parse(this.url), which ispath-only and never reads the
Hostheader.Are you willing and able to fix this?
Yes — I have a patch and two regression tests ready, and will open a PR.