Skip to content

Commit 9e6fcbb

Browse files
TimothyGuMylesBorins
authored andcommitted
url: fix surrogate handling in encodeAuth()
Currently, the legacy URL stringifier miscalculates the offset of an extra surrogate, causing the high surrogate to be included unescaped: > url.format({ auth: '\uD83D\uDE00', hostname: 'a' }) '//%F0%9F%98%80�@A' PR-URL: #11387 Backport-of: #11161
1 parent da87459 commit 9e6fcbb

File tree

2 files changed

+42
-21
lines changed

2 files changed

+42
-21
lines changed

lib/url.js

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -948,44 +948,53 @@ function spliceOne(list, index) {
948948
var hexTable = new Array(256);
949949
for (var i = 0; i < 256; ++i)
950950
hexTable[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase();
951+
952+
// These characters do not need escaping:
953+
// ! - . _ ~
954+
// ' ( ) * :
955+
// digits
956+
// alpha (uppercase)
957+
// alpha (lowercase)
958+
const noEscapeAuth = [
959+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00 - 0x0F
960+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10 - 0x1F
961+
0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, // 0x20 - 0x2F
962+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 0x30 - 0x3F
963+
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 - 0x4F
964+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 0x50 - 0x5F
965+
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6F
966+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0 // 0x70 - 0x7F
967+
];
968+
951969
function encodeAuth(str) {
952970
// faster encodeURIComponent alternative for encoding auth uri components
953971
var out = '';
954972
var lastPos = 0;
955973
for (var i = 0; i < str.length; ++i) {
956974
var c = str.charCodeAt(i);
957975

958-
// These characters do not need escaping:
959-
// ! - . _ ~
960-
// ' ( ) * :
961-
// digits
962-
// alpha (uppercase)
963-
// alpha (lowercase)
964-
if (c === 0x21 || c === 0x2D || c === 0x2E || c === 0x5F || c === 0x7E ||
965-
(c >= 0x27 && c <= 0x2A) ||
966-
(c >= 0x30 && c <= 0x3A) ||
967-
(c >= 0x41 && c <= 0x5A) ||
968-
(c >= 0x61 && c <= 0x7A)) {
969-
continue;
970-
}
971-
972-
if (i - lastPos > 0)
973-
out += str.slice(lastPos, i);
974-
975-
lastPos = i + 1;
976-
977-
// Other ASCII characters
976+
// ASCII
978977
if (c < 0x80) {
978+
if (noEscapeAuth[c] === 1)
979+
continue;
980+
if (lastPos < i)
981+
out += str.slice(lastPos, i);
982+
lastPos = i + 1;
979983
out += hexTable[c];
980984
continue;
981985
}
982986

987+
if (lastPos < i)
988+
out += str.slice(lastPos, i);
989+
983990
// Multi-byte characters ...
984991
if (c < 0x800) {
992+
lastPos = i + 1;
985993
out += hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)];
986994
continue;
987995
}
988996
if (c < 0xD800 || c >= 0xE000) {
997+
lastPos = i + 1;
989998
out += hexTable[0xE0 | (c >> 12)] +
990999
hexTable[0x80 | ((c >> 6) & 0x3F)] +
9911000
hexTable[0x80 | (c & 0x3F)];
@@ -998,6 +1007,7 @@ function encodeAuth(str) {
9981007
c2 = str.charCodeAt(i) & 0x3FF;
9991008
else
10001009
c2 = 0;
1010+
lastPos = i + 1;
10011011
c = 0x10000 + (((c & 0x3FF) << 10) | c2);
10021012
out += hexTable[0xF0 | (c >> 18)] +
10031013
hexTable[0x80 | ((c >> 12) & 0x3F)] +

test/parallel/test-url.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,8 +891,19 @@ var parseTests = {
891891
pathname: '/*',
892892
path: '/*',
893893
href: 'https:///*'
894-
}
894+
},
895895

896+
// surrogate in auth
897+
'http://%F0%9F%98%[email protected]/': {
898+
href: 'http://%F0%9F%98%[email protected]/',
899+
slashes: true,
900+
protocol: 'http:',
901+
auth: '\uD83D\uDE00',
902+
host: 'www.example.com',
903+
hostname: 'www.example.com',
904+
pathname: '/',
905+
path: '/'
906+
}
896907
};
897908

898909
for (const u in parseTests) {

0 commit comments

Comments
 (0)