Skip to content

Commit 5e9c777

Browse files
authored
fix(core): Support joined set-cookie headers (split them) (#24659)
Follow-up to this discussion: #24536 (comment)
1 parent 48deb1c commit 5e9c777

3 files changed

Lines changed: 56 additions & 1 deletion

File tree

‎packages/core/src/utils/cookie.ts‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ export function parseCookieHeader(value: string | string[], headerName: 'cookie'
4646
if (typeof headerValue !== 'string') {
4747
return [];
4848
}
49-
return headerName === 'set-cookie' ? [headerValue.split(';')[0]!] : headerValue.split(';');
49+
return headerName === 'set-cookie'
50+
? splitJoinedSetCookieHeader(headerValue).map(cookie => cookie.split(';')[0]!)
51+
: headerValue.split(';');
5052
});
5153

5254
return (
@@ -66,6 +68,15 @@ export function parseCookieHeader(value: string | string[], headerName: 'cookie'
6668
);
6769
}
6870

71+
/**
72+
* No SDK path reads a joined header today, but `Headers.get('set-cookie')` and `xhr.getResponseHeader()`
73+
* join repeated `Set-Cookie` headers with ", ". A "," only starts a new cookie when a "name=" follows
74+
* before the next ";" or ",", so the "," in `Expires=Wed, 21 Oct 2026 07:28:00 GMT` does not split.
75+
*/
76+
function splitJoinedSetCookieHeader(headerValue: string): string[] {
77+
return headerValue.split(/,(?=[^;=,]*=)/);
78+
}
79+
6980
/**
7081
* Converts cookie pairs to a record with decoded values. The first cookie of a name wins.
7182
*

‎packages/core/test/lib/utils/cookie.test.ts‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,40 @@ describe('parseCookieHeader', () => {
122122
expect(parseCookieHeader('; HttpOnly', 'set-cookie')).toEqual([]);
123123
});
124124

125+
it.each([
126+
[
127+
'sid=s3cr3t; Path=/, theme=dark; Path=/',
128+
[
129+
['sid', 's3cr3t'],
130+
['theme', 'dark'],
131+
],
132+
],
133+
[
134+
'sid=s3cr3t; Expires=Wed, 21 Oct 2026 07:28:00 GMT; Path=/, theme=dark',
135+
[
136+
['sid', 's3cr3t'],
137+
['theme', 'dark'],
138+
],
139+
],
140+
[
141+
'sid=s3cr3t; Expires=Wed, 21 Oct 2026 07:28:00 GMT, theme=dark',
142+
[
143+
['sid', 's3cr3t'],
144+
['theme', 'dark'],
145+
],
146+
],
147+
['sid=s3cr3t; Expires=Wed, 21 Oct 2026 07:28:00 GMT', [['sid', 's3cr3t']]],
148+
[
149+
'sid=s3cr3t,theme=dark',
150+
[
151+
['sid', 's3cr3t'],
152+
['theme', 'dark'],
153+
],
154+
],
155+
])('splits headers joined with "," in %j', (header, expected) => {
156+
expect(parseCookieHeader(header, 'set-cookie')).toEqual(expected);
157+
});
158+
125159
it('returns one pair per header value', () => {
126160
expect(parseCookieHeader(['theme=dark; HttpOnly', 'sid=s3cr3t; Secure'], 'set-cookie')).toEqual([
127161
['theme', 'dark'],

‎packages/core/test/lib/utils/data-collection/filterCookies.test.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,16 @@ describe('filterCookies', () => {
122122
).toEqual({ theme: 'dark' });
123123
});
124124

125+
it('reads each cookie of several Set-Cookie headers joined with ","', () => {
126+
expect(
127+
filterCookies(
128+
'sid=s3cr3t; Expires=Wed, 21 Oct 2026 07:28:00 GMT; Path=/, theme=dark; Path=/',
129+
true,
130+
'set-cookie',
131+
),
132+
).toEqual({ sid: '[Filtered]', theme: 'dark' });
133+
});
134+
125135
it('filters the token of a nameless cookie', () => {
126136
expect(filterCookies('y7Uu0Rk2QpLmXv3; HttpOnly; Secure', true, 'set-cookie')).toEqual({ '': '[Filtered]' });
127137
});

0 commit comments

Comments
 (0)