From d1cedf2368faf115a6252e9e4f8f5ddb4ee90b39 Mon Sep 17 00:00:00 2001 From: Rodney Rehm Date: Sun, 1 Oct 2017 14:55:38 +0200 Subject: [PATCH] fix(parse): add URI.preventInvalidHostname to make hostname validation optional - #345, 352, #354, #355 --- src/URI.js | 42 +++++++++++++++++++++++++++++++----------- test/test.js | 14 ++++++++++++++ test/test_jim.js | 4 ++-- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/URI.js b/src/URI.js index 0d659d61..f896ff4b 100644 --- a/src/URI.js +++ b/src/URI.js @@ -201,10 +201,15 @@ query: null, fragment: null, // state + preventInvalidHostname: URI.preventInvalidHostname, duplicateQueryParameters: URI.duplicateQueryParameters, escapeQuerySpace: URI.escapeQuerySpace }; }; + // state: throw on invalid hostname + // see https://github.com/medialize/URI.js/pull/345 + // and https://github.com/medialize/URI.js/issues/354 + URI.preventInvalidHostname = false; // state: allow duplicate query parameters (a=1&a=1) URI.duplicateQueryParameters = false; // state: replaces + with %20 (space in query strings) @@ -485,7 +490,9 @@ URI.parse = function(string, parts) { var pos; if (!parts) { - parts = {}; + parts = { + preventInvalidHostname: URI.preventInvalidHostname + }; } // [protocol"://"[username[":"password]"@"]hostname[":"port]"/"?][path]["?"querystring]["#"fragment] @@ -538,6 +545,10 @@ return parts; }; URI.parseHost = function(string, parts) { + if (!string) { + string = ''; + } + // Copy chrome, IE, opera backslash-handling behavior. // Back slashes before the query string get converted to forward slashes // See: https://github.com/joyent/node/blob/386fd24f49b0e9d1a8a076592a404168faeecc34/lib/url.js#L115-L124 @@ -585,7 +596,9 @@ string = '/' + string; } - URI.ensureValidHostname(parts.hostname, parts.protocol); + if (parts.preventInvalidHostname) { + URI.ensureValidHostname(parts.hostname, parts.protocol); + } if (parts.port) { URI.ensureValidPort(parts.port); @@ -1312,16 +1325,15 @@ var _hostname = p.hostname; p.protocol = function(v, build) { - if (v !== undefined) { - if (v) { - // accept trailing :// - v = v.replace(/:(\/\/)?$/, ''); + if (v) { + // accept trailing :// + v = v.replace(/:(\/\/)?$/, ''); - if (!v.match(URI.protocol_expression)) { - throw new TypeError('Protocol "' + v + '" contains characters other than [A-Z0-9.+-] or doesn\'t start with [A-Z]'); - } + if (!v.match(URI.protocol_expression)) { + throw new TypeError('Protocol "' + v + '" contains characters other than [A-Z0-9.+-] or doesn\'t start with [A-Z]'); } } + return _protocol.call(this, v, build); }; p.scheme = p.protocol; @@ -1352,15 +1364,18 @@ } if (v !== undefined) { - var x = {}; + var x = { preventInvalidHostname: this._parts.preventInvalidHostname }; var res = URI.parseHost(v, x); if (res !== '/') { throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-]'); } v = x.hostname; - URI.ensureValidHostname(v, this._parts.protocol); + if (this._parts.preventInvalidHostname) { + URI.ensureValidHostname(v, this._parts.protocol); + } } + return _hostname.call(this, v, build); }; @@ -2300,6 +2315,11 @@ }; // state + p.preventInvalidHostname = function(v) { + this._parts.preventInvalidHostname = !!v; + return this; + }; + p.duplicateQueryParameters = function(v) { this._parts.duplicateQueryParameters = !!v; return this; diff --git a/test/test.js b/test/test.js index 385cf4ff..fe62c01c 100644 --- a/test/test.js +++ b/test/test.js @@ -140,9 +140,15 @@ }, TypeError, "throws TypeError"); }); test('function URI(string) with protocol and without hostname should throw', function () { + new URI('http://'); + + URI.preventInvalidHostname = true; raises(function () { new URI('http://'); }, TypeError, "throws TypeError"); + + URI.preventInvalidHostname = false; + new URI('http://'); }); test('new URI(string, string)', function() { // see http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#constructor @@ -251,9 +257,17 @@ u.hostname('foo\\bar.com'); }, TypeError, 'Failing backslash detection in hostname'); + // instance does not fall back to global setting + URI.preventInvalidHostname = true; + u.hostname(''); + u.hostname(null); + URI.preventInvalidHostname = false; + + u.preventInvalidHostname(true); raises(function() { u.hostname(''); }, TypeError, "Trying to set an empty hostname with http(s) protocol throws a TypeError"); + raises(function() { u.hostname(null); }, TypeError, "Trying to set hostname to null with http(s) protocol throws a TypeError"); diff --git a/test/test_jim.js b/test/test_jim.js index 86204c29..cdb27624 100644 --- a/test/test_jim.js +++ b/test/test_jim.js @@ -146,7 +146,7 @@ domain = "com"; for (j=0; j<3; j++) { //start new subdomain - domain = "." + domain; + domain = "." + domain; for (i=0; i<70; i++) { domain = "a" + domain; } @@ -155,4 +155,4 @@ equals(u.hostname() == domain, true, "set domain() with 70-character subdomain not valid domainname"); }); */ -})(); \ No newline at end of file +})();