fix parsing of non-IPv4 hosts ends in a number in the URL polyfill

This commit is contained in:
Denis Pushkarev
2026-03-01 09:31:23 +02:00
parent 4b9a51f22c
commit 85fefea486
4 changed files with 37 additions and 7 deletions
+1
View File
@@ -42,6 +42,7 @@
- Fixed possible removal of unnecessary entries in `URLSearchParam.prototype.delete` polyfill with second argument
- Fixed an error in some cases of non-special URLs without a path in the `URL` polyfill
- Fixed some percent encode cases / character sets in the `URL` polyfill
- Fixed parsing of non-IPv4 hosts ends in a number in the `URL` polyfill
- Fixed some cases of `''` and `null` host handling in the `URL` polyfill
- Fixed host parsing with `hostname = host:port` in the `URL` polyfill
- Fixed host inheritance in some cases of file scheme in the `URL` polyfill
@@ -68,7 +68,24 @@ var TAB_AND_NEW_LINE = /[\t\n\r]/g;
// eslint-disable-next-line no-unassigned-vars -- expected `undefined` value
var EOF;
// https://url.spec.whatwg.org/#ipv4-number-parser
// https://url.spec.whatwg.org/#ends-in-a-number-checker
var endsInNumber = function (input) {
var parts = split(input, '.');
var last, hexPart;
if (parts[parts.length - 1] === '') {
if (parts.length === 1) return false;
parts.length--;
}
last = parts[parts.length - 1];
if (exec(DEC, last)) return true;
if (exec(HEX_START, last)) {
hexPart = stringSlice(last, 2);
return hexPart === '' || !!exec(HEX, hexPart);
}
return false;
};
// https://url.spec.whatwg.org/#concept-ipv4-parser
var parseIPv4 = function (input) {
var parts = split(input, '.');
var partsLength, numbers, index, part, radix, number, ipv4;
@@ -76,11 +93,11 @@ var parseIPv4 = function (input) {
parts.length--;
}
partsLength = parts.length;
if (partsLength > 4) return input;
if (partsLength > 4) return null;
numbers = [];
for (index = 0; index < partsLength; index++) {
part = parts[index];
if (part === '') return input;
if (part === '') return null;
radix = 10;
if (part.length > 1 && charAt(part, 0) === '0') {
radix = exec(HEX_START, part) ? 16 : 8;
@@ -89,7 +106,7 @@ var parseIPv4 = function (input) {
if (part === '') {
number = 0;
} else {
if (!exec(radix === 10 ? DEC : radix === 8 ? OCT : HEX, part)) return input;
if (!exec(radix === 10 ? DEC : radix === 8 ? OCT : HEX, part)) return null;
number = parseInt(part, radix);
}
push(numbers, number);
@@ -771,9 +788,13 @@ URLState.prototype = {
} else {
input = toASCII(input);
if (exec(FORBIDDEN_HOST_CODE_POINT, input)) return INVALID_HOST;
result = parseIPv4(input);
if (result === null) return INVALID_HOST;
this.host = result;
if (endsInNumber(input)) {
result = parseIPv4(input);
if (result === null) return INVALID_HOST;
this.host = result;
} else {
this.host = input;
}
}
},
// https://url.spec.whatwg.org/#cannot-have-a-username-password-port
+4
View File
@@ -39,8 +39,12 @@ QUnit.test('URL constructor', assert => {
assert.same(String(new URL('http://0300.168.0xF0')), 'http://192.168.0.240/');
assert.same(String(new URL('http://[20:0:0:1:0:0:0:ff]')), 'http://[20:0:0:1::ff]/');
// assert.same(String(new URL('http://257.168.0xF0')), 'http://257.168.0xf0/', 'incorrect IPv4 parsed as host'); // TypeError in Chrome and Safari
assert.throws(() => new URL('http://257.168.0xF0'), 'invalid IPv4: octet > 255');
assert.same(String(new URL('http://0300.168.0xG0')), 'http://0300.168.0xg0/', 'incorrect IPv4 parsed as host');
assert.throws(() => new URL('http://1.2.3.4.5/'), 'IPv4 with > 4 parts');
assert.throws(() => new URL('http://a.b.c.d.5/'), 'host ending in number with non-numeric parts');
assert.throws(() => new URL('http://foo.1/'), 'host ending in number with non-IPv4');
assert.same(String(new URL('file:///var/log/system.log')), 'file:///var/log/system.log', 'file scheme');
// Chromium ~ 145 on Windows works differently
+4
View File
@@ -40,8 +40,12 @@ QUnit.test('URL constructor', assert => {
assert.same(String(new URL('http://0300.168.0xF0')), 'http://192.168.0.240/');
assert.same(String(new URL('http://[20:0:0:1:0:0:0:ff]')), 'http://[20:0:0:1::ff]/');
// assert.same(String(new URL('http://257.168.0xF0')), 'http://257.168.0xf0/', 'incorrect IPv4 parsed as host'); // TypeError in Chrome and Safari
assert.throws(() => new URL('http://257.168.0xF0'), 'invalid IPv4: octet > 255');
assert.same(String(new URL('http://0300.168.0xG0')), 'http://0300.168.0xg0/', 'incorrect IPv4 parsed as host');
assert.throws(() => new URL('http://1.2.3.4.5/'), 'IPv4 with > 4 parts');
assert.throws(() => new URL('http://a.b.c.d.5/'), 'host ending in number with non-numeric parts');
assert.throws(() => new URL('http://foo.1/'), 'host ending in number with non-IPv4');
assert.same(String(new URL('file:///var/log/system.log')), 'file:///var/log/system.log', 'file scheme');
// Chromium ~ 145 on Windows works differently