From 41d078d2d4386c3cce3a010f11f612242a96ec4c Mon Sep 17 00:00:00 2001 From: Abhishek Date: Sun, 29 Sep 2024 16:32:23 +0530 Subject: [PATCH 1/3] fix double decoding of cookie value --- packages/cookies/src/serialize.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/cookies/src/serialize.ts b/packages/cookies/src/serialize.ts index b9a3e1e1..8eeb624b 100644 --- a/packages/cookies/src/serialize.ts +++ b/packages/cookies/src/serialize.ts @@ -1,5 +1,13 @@ import type { RequestCookie, ResponseCookie } from './types' +function maybeDecodeURIComponent(s: string) { + try { + return decodeURIComponent(s) + } catch { + return s + } +} + export function stringifyCookie(c: ResponseCookie | RequestCookie): string { const attrs = [ 'path' in c && c.path && `Path=${c.path}`, @@ -19,7 +27,9 @@ export function stringifyCookie(c: ResponseCookie | RequestCookie): string { ].filter(Boolean) const stringified = `${c.name}=${encodeURIComponent(c.value ?? '')}` - return attrs.length === 0 ? stringified : `${stringified}; ${attrs.join('; ')}` + return attrs.length === 0 + ? stringified + : `${stringified}; ${attrs.join('; ')}` } /** Parse a `Cookie` header value */ @@ -72,7 +82,9 @@ export function parseSetCookie(setCookie: string): undefined | ResponseCookie { ) const cookie: ResponseCookie = { name, - value: decodeURIComponent(value), + // parseCookie already decoded the value, so if the value contains special chars + // decoding it again will cause problems + value: maybeDecodeURIComponent(value), domain, ...(expires && { expires: new Date(expires) }), ...(httponly && { httpOnly: true }), From a294091a70512190f2806db4e8c4498899eed1f0 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Sun, 29 Sep 2024 16:32:43 +0530 Subject: [PATCH 2/3] add test case to set cookies with special chars --- packages/cookies/test/response-cookies.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/cookies/test/response-cookies.test.ts b/packages/cookies/test/response-cookies.test.ts index 8c341bdd..9bc47d93 100644 --- a/packages/cookies/test/response-cookies.test.ts +++ b/packages/cookies/test/response-cookies.test.ts @@ -262,3 +262,16 @@ test('splitting multiple set-cookie', () => { expect(cookies2.get('foo')?.value).toBe(undefined) expect(cookies2.get('fooz')?.value).toBe('barz') }) + +test('cookie with special chars', () => { + const headers = new Headers() + const specialChars = 'bar 50%!@#$%^&*()_+' + headers.set( + 'set-cookie', + `foo=${JSON.stringify({ 'val': encodeURIComponent(specialChars) })}`, + ) + const cookies = new ResponseCookies(headers) + expect(cookies.getAll()).toEqual([ + { name: 'foo', value: `{"val":"${specialChars}"}` }, + ]) +}) From 6dcc952c1f9fed22d8b61c5cd29eac91e5a81c39 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Mon, 30 Sep 2024 20:48:50 +0530 Subject: [PATCH 3/3] rm unnecessary json.stringify from test --- packages/cookies/test/response-cookies.test.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/cookies/test/response-cookies.test.ts b/packages/cookies/test/response-cookies.test.ts index 9bc47d93..f5547dad 100644 --- a/packages/cookies/test/response-cookies.test.ts +++ b/packages/cookies/test/response-cookies.test.ts @@ -266,12 +266,7 @@ test('splitting multiple set-cookie', () => { test('cookie with special chars', () => { const headers = new Headers() const specialChars = 'bar 50%!@#$%^&*()_+' - headers.set( - 'set-cookie', - `foo=${JSON.stringify({ 'val': encodeURIComponent(specialChars) })}`, - ) + headers.set('set-cookie', `foo=${encodeURIComponent(specialChars)}`) const cookies = new ResponseCookies(headers) - expect(cookies.getAll()).toEqual([ - { name: 'foo', value: `{"val":"${specialChars}"}` }, - ]) + expect(cookies.getAll()).toEqual([{ name: 'foo', value: specialChars }]) })