From 465ef9b3f5bdbbaf99f94877d1bd3b62720f3d2f Mon Sep 17 00:00:00 2001 From: Aiji Uejima Date: Tue, 8 Mar 2022 13:16:58 +0900 Subject: [PATCH] fix: invalid character error in cookie serialize --- contributors.yml | 1 + .../__tests__/cookies-test.ts | 8 ++++++++ packages/remix-server-runtime/cookies.ts | 20 +++++++++++++++++-- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/contributors.yml b/contributors.yml index 86da800ad5d..2cae173ed9a 100644 --- a/contributors.yml +++ b/contributors.yml @@ -4,6 +4,7 @@ - abotsi - ahbruns - ahmedeldessouki +- aiji42 - airjp73 - airondumael - Alarid diff --git a/packages/remix-server-runtime/__tests__/cookies-test.ts b/packages/remix-server-runtime/__tests__/cookies-test.ts index 2b6552b21f8..3285ae0ad89 100644 --- a/packages/remix-server-runtime/__tests__/cookies-test.ts +++ b/packages/remix-server-runtime/__tests__/cookies-test.ts @@ -52,6 +52,14 @@ describe("cookies", () => { expect(value).toMatchInlineSnapshot(`"hello michael"`); }); + it("parses/serializes string values containing non-ascii characters", async () => { + let cookie = createCookie("my-cookie"); + let setCookie = await cookie.serialize("日本語"); + let value = await cookie.parse(getCookieFromSetCookie(setCookie)); + + expect(value).toBe("日本語"); + }); + it("fails to parses signed string values with invalid signature", async () => { let cookie = createCookie("my-cookie", { secrets: ["secret1"], diff --git a/packages/remix-server-runtime/cookies.ts b/packages/remix-server-runtime/cookies.ts index 4c86f65182f..4331a8f8a2b 100644 --- a/packages/remix-server-runtime/cookies.ts +++ b/packages/remix-server-runtime/cookies.ts @@ -166,13 +166,29 @@ async function decodeCookieValue( } function encodeData(value: any): string { - return btoa(JSON.stringify(value)); + return btoa(toBinary(JSON.stringify(value))); } function decodeData(value: string): any { try { - return JSON.parse(atob(value)); + return JSON.parse(fromBinary(atob(value))); } catch (error) { return {}; } } + +function toBinary(value: string): string { + const codeUnits = new Uint16Array(value.length); + for (let i = 0; i < codeUnits.length; i++) { + codeUnits[i] = value.charCodeAt(i); + } + return String.fromCharCode(...new Uint8Array(codeUnits.buffer)); +} + +function fromBinary(value: string): string { + const bytes = new Uint8Array(value.length); + for (let i = 0; i < bytes.length; i++) { + bytes[i] = value.charCodeAt(i); + } + return String.fromCharCode(...new Uint16Array(bytes.buffer)); +}