From 76d5f93047d32bc69367099a195e872d0453329f Mon Sep 17 00:00:00 2001 From: s1gr1d <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 23 Sep 2026 16:30:24 +0200 Subject: [PATCH] fix(core): Support joined set-cookie headers --- packages/core/src/utils/cookie.ts | 13 ++++++- packages/core/test/lib/utils/cookie.test.ts | 34 +++++++++++++++++++ .../data-collection/filterCookies.test.ts | 10 ++++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/packages/core/src/utils/cookie.ts b/packages/core/src/utils/cookie.ts index fb4a8cb7e928..787cf4b862e5 100644 --- a/packages/core/src/utils/cookie.ts +++ b/packages/core/src/utils/cookie.ts @@ -46,7 +46,9 @@ export function parseCookieHeader(value: string | string[], headerName: 'cookie' if (typeof headerValue !== 'string') { return []; } - return headerName === 'set-cookie' ? [headerValue.split(';')[0]!] : headerValue.split(';'); + return headerName === 'set-cookie' + ? splitJoinedSetCookieHeader(headerValue).map(cookie => cookie.split(';')[0]!) + : headerValue.split(';'); }); return ( @@ -66,6 +68,15 @@ export function parseCookieHeader(value: string | string[], headerName: 'cookie' ); } +/** + * No SDK path reads a joined header today, but `Headers.get('set-cookie')` and `xhr.getResponseHeader()` + * join repeated `Set-Cookie` headers with ", ". A "," only starts a new cookie when a "name=" follows + * before the next ";" or ",", so the "," in `Expires=Wed, 21 Oct 2026 07:28:00 GMT` does not split. + */ +function splitJoinedSetCookieHeader(headerValue: string): string[] { + return headerValue.split(/,(?=[^;=,]*=)/); +} + /** * Converts cookie pairs to a record with decoded values. The first cookie of a name wins. * diff --git a/packages/core/test/lib/utils/cookie.test.ts b/packages/core/test/lib/utils/cookie.test.ts index 495041e72397..c7a4ba8136ed 100644 --- a/packages/core/test/lib/utils/cookie.test.ts +++ b/packages/core/test/lib/utils/cookie.test.ts @@ -122,6 +122,40 @@ describe('parseCookieHeader', () => { expect(parseCookieHeader('; HttpOnly', 'set-cookie')).toEqual([]); }); + it.each([ + [ + 'sid=s3cr3t; Path=/, theme=dark; Path=/', + [ + ['sid', 's3cr3t'], + ['theme', 'dark'], + ], + ], + [ + 'sid=s3cr3t; Expires=Wed, 21 Oct 2026 07:28:00 GMT; Path=/, theme=dark', + [ + ['sid', 's3cr3t'], + ['theme', 'dark'], + ], + ], + [ + 'sid=s3cr3t; Expires=Wed, 21 Oct 2026 07:28:00 GMT, theme=dark', + [ + ['sid', 's3cr3t'], + ['theme', 'dark'], + ], + ], + ['sid=s3cr3t; Expires=Wed, 21 Oct 2026 07:28:00 GMT', [['sid', 's3cr3t']]], + [ + 'sid=s3cr3t,theme=dark', + [ + ['sid', 's3cr3t'], + ['theme', 'dark'], + ], + ], + ])('splits headers joined with "," in %j', (header, expected) => { + expect(parseCookieHeader(header, 'set-cookie')).toEqual(expected); + }); + it('returns one pair per header value', () => { expect(parseCookieHeader(['theme=dark; HttpOnly', 'sid=s3cr3t; Secure'], 'set-cookie')).toEqual([ ['theme', 'dark'], diff --git a/packages/core/test/lib/utils/data-collection/filterCookies.test.ts b/packages/core/test/lib/utils/data-collection/filterCookies.test.ts index f686e0b46e4f..2b6935f06b5b 100644 --- a/packages/core/test/lib/utils/data-collection/filterCookies.test.ts +++ b/packages/core/test/lib/utils/data-collection/filterCookies.test.ts @@ -122,6 +122,16 @@ describe('filterCookies', () => { ).toEqual({ theme: 'dark' }); }); + it('reads each cookie of several Set-Cookie headers joined with ","', () => { + expect( + filterCookies( + 'sid=s3cr3t; Expires=Wed, 21 Oct 2026 07:28:00 GMT; Path=/, theme=dark; Path=/', + true, + 'set-cookie', + ), + ).toEqual({ sid: '[Filtered]', theme: 'dark' }); + }); + it('filters the token of a nameless cookie', () => { expect(filterCookies('y7Uu0Rk2QpLmXv3; HttpOnly; Secure', true, 'set-cookie')).toEqual({ '': '[Filtered]' }); });