Skip to content

Commit

Permalink
url: fix surrogate handling in encodeAuth()
Browse files Browse the repository at this point in the history
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
  • Loading branch information
TimothyGu authored and MylesBorins committed Mar 9, 2017
1 parent da87459 commit 9e6fcbb
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 21 deletions.
50 changes: 30 additions & 20 deletions lib/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -948,44 +948,53 @@ function spliceOne(list, index) {
var hexTable = new Array(256);
for (var i = 0; i < 256; ++i)
hexTable[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase();

// These characters do not need escaping:
// ! - . _ ~
// ' ( ) * :
// digits
// alpha (uppercase)
// alpha (lowercase)
const noEscapeAuth = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00 - 0x0F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10 - 0x1F
0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, // 0x20 - 0x2F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 0x30 - 0x3F
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 - 0x4F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 0x50 - 0x5F
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0 // 0x70 - 0x7F
];

function encodeAuth(str) {
// faster encodeURIComponent alternative for encoding auth uri components
var out = '';
var lastPos = 0;
for (var i = 0; i < str.length; ++i) {
var c = str.charCodeAt(i);

// These characters do not need escaping:
// ! - . _ ~
// ' ( ) * :
// digits
// alpha (uppercase)
// alpha (lowercase)
if (c === 0x21 || c === 0x2D || c === 0x2E || c === 0x5F || c === 0x7E ||
(c >= 0x27 && c <= 0x2A) ||
(c >= 0x30 && c <= 0x3A) ||
(c >= 0x41 && c <= 0x5A) ||
(c >= 0x61 && c <= 0x7A)) {
continue;
}

if (i - lastPos > 0)
out += str.slice(lastPos, i);

lastPos = i + 1;

// Other ASCII characters
// ASCII
if (c < 0x80) {
if (noEscapeAuth[c] === 1)
continue;
if (lastPos < i)
out += str.slice(lastPos, i);
lastPos = i + 1;
out += hexTable[c];
continue;
}

if (lastPos < i)
out += str.slice(lastPos, i);

// Multi-byte characters ...
if (c < 0x800) {
lastPos = i + 1;
out += hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)];
continue;
}
if (c < 0xD800 || c >= 0xE000) {
lastPos = i + 1;
out += hexTable[0xE0 | (c >> 12)] +
hexTable[0x80 | ((c >> 6) & 0x3F)] +
hexTable[0x80 | (c & 0x3F)];
Expand All @@ -998,6 +1007,7 @@ function encodeAuth(str) {
c2 = str.charCodeAt(i) & 0x3FF;
else
c2 = 0;
lastPos = i + 1;
c = 0x10000 + (((c & 0x3FF) << 10) | c2);
out += hexTable[0xF0 | (c >> 18)] +
hexTable[0x80 | ((c >> 12) & 0x3F)] +
Expand Down
13 changes: 12 additions & 1 deletion test/parallel/test-url.js
Original file line number Diff line number Diff line change
Expand Up @@ -891,8 +891,19 @@ var parseTests = {
pathname: '/*',
path: '/*',
href: 'https:///*'
}
},

// surrogate in auth
'http://%F0%9F%98%[email protected]/': {
href: 'http://%F0%9F%98%[email protected]/',
slashes: true,
protocol: 'http:',
auth: '\uD83D\uDE00',
host: 'www.example.com',
hostname: 'www.example.com',
pathname: '/',
path: '/'
}
};

for (const u in parseTests) {
Expand Down

0 comments on commit 9e6fcbb

Please sign in to comment.