Skip to content

Commit 84e7388

Browse files
TrottRafaelGSS
authored andcommitted
url: improve port validation
If a port is not a number, throw rather than treating the `:` that delineates the port as part of the path. This is consistent with WHATWG URL and also mitigates hostname-spoofing. Concerns about hostname-spoofing were raised and presented in excellent detail by pyozzi-toss ([email protected]/Security-Tech Team in Toss). PR-URL: #45012 Reviewed-By: Rafael Gonzaga <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent d47f832 commit 84e7388

File tree

3 files changed

+18
-18
lines changed

3 files changed

+18
-18
lines changed

lib/url.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) {
383383

384384
// validate a little.
385385
if (!ipv6Hostname) {
386-
rest = getHostname(this, rest, hostname);
386+
rest = getHostname(this, rest, hostname, url);
387387
}
388388

389389
if (this.hostname.length > hostnameMaxLen) {
@@ -502,7 +502,7 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) {
502502
return this;
503503
};
504504

505-
function getHostname(self, rest, hostname) {
505+
function getHostname(self, rest, hostname, url) {
506506
for (let i = 0; i < hostname.length; ++i) {
507507
const code = hostname.charCodeAt(i);
508508
const isValid = (code !== CHAR_FORWARD_SLASH &&
@@ -512,6 +512,10 @@ function getHostname(self, rest, hostname) {
512512
code !== CHAR_COLON);
513513

514514
if (!isValid) {
515+
// If leftover starts with :, then it represents an invalid port.
516+
if (hostname.charCodeAt(i) === 58) {
517+
throw new ERR_INVALID_URL(url);
518+
}
515519
self.hostname = hostname.slice(0, i);
516520
return `/${hostname.slice(i)}${rest}`;
517521
}

test/parallel/test-url-parse-format.js

-16
Original file line numberDiff line numberDiff line change
@@ -865,22 +865,6 @@ const parseTests = {
865865
href: 'http://a%0D%22%20%09%0A%3C\'b:b@c/%0D%0Ad/e?f'
866866
},
867867

868-
// Git urls used by npm
869-
'git+ssh://[email protected]:npm/npm': {
870-
protocol: 'git+ssh:',
871-
slashes: true,
872-
auth: 'git',
873-
host: 'github.com',
874-
port: null,
875-
hostname: 'github.com',
876-
hash: null,
877-
search: null,
878-
query: null,
879-
pathname: '/:npm/npm',
880-
path: '/:npm/npm',
881-
href: 'git+ssh://[email protected]/:npm/npm'
882-
},
883-
884868
'https://*': {
885869
protocol: 'https:',
886870
slashes: true,

test/parallel/test-url-parse-invalid-input.js

+12
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,15 @@ if (common.hasIntl) {
7474
(e) => e.code === 'ERR_INVALID_URL',
7575
'parsing http://\u00AD/bad.com/');
7676
}
77+
78+
{
79+
const badURLs = [
80+
'https://evil.com:.example.com',
81+
'git+ssh://[email protected]:npm/npm',
82+
];
83+
badURLs.forEach((badURL) => {
84+
assert.throws(() => { url.parse(badURL); },
85+
(e) => e.code === 'ERR_INVALID_URL',
86+
`parsing ${badURL}`);
87+
});
88+
}

0 commit comments

Comments
 (0)