diff --git a/lib/internals/querystring.js b/lib/internals/querystring.js index 35e6fd0..b01ba04 100644 --- a/lib/internals/querystring.js +++ b/lib/internals/querystring.js @@ -78,10 +78,16 @@ function encodeString(str) { throw new Error("URI malformed"); } - const c2 = str.charCodeAt(i) & 0x3ff; + const c2 = str.charCodeAt(i); + + // The second code unit must be a low surrogate (0xDC00–0xDFFF), + // otherwise the input is not a well-formed UTF-16 string. + if ((c2 & 0xfc00) !== 0xdc00) { + throw new Error("URI malformed"); + } lastPos = i + 1; - c = 0x10000 + (((c & 0x3ff) << 10) | c2); + c = 0x10000 + (((c & 0x3ff) << 10) | (c2 & 0x3ff)); out += hexTable[0xf0 | (c >> 18)] + hexTable[0x80 | ((c >> 12) & 0x3f)] + diff --git a/test/stringify.test.ts b/test/stringify.test.ts index dd3980a..9e24a18 100644 --- a/test/stringify.test.ts +++ b/test/stringify.test.ts @@ -76,6 +76,10 @@ test("invalid surrogate pair should throw", () => { assert.throws(() => qs.stringify({ foo: "\udc00" }), "URI malformed"); }); +test("high surrogate followed by non-surrogate should throw", () => { + assert.throws(() => qs.stringify({ foo: "a\ud800b" }), "URI malformed"); +}); + test("should omit nested values", () => { const f = qs.stringify({ a: "b",