Skip to content

Consolidate the two cookie parsers parseCookieString and getCookieAttachments - #24525

Closed
Dextheking1 wants to merge 1 commit into
getsentry:developfrom
Dextheking1:fix/consolidate-cookie-parsers
Closed

Dextheking1 wants to merge 1 commit into
getsentry:developfrom
Dextheking1:fix/consolidate-cookie-parsers

Conversation

@Dextheking1

Copy link
Copy Markdown

Summary

Fixes #24501.

parseCookie (event cookie records via filterCookies) and parseCookieHeader (span attributes via httpHeadersToSpanAttributes) disagreed on three things: nameless segments, Set-Cookie attributes, and URL-decoding/quote-stripping. This consolidates them into a single parseCookiePairs(value, setCookie) in packages/core/src/utils/cookie.ts that returns ordered [name, value][] pairs:

  • Set-Cookie mode parses only the segment before the first ;; otherwise every ;-separated segment is a pair.
  • Known Set-Cookie attributes (expires, max-age, domain, path, secure, httponly, samesite, partitioned, case-insensitive) are dropped by name in cookie mode — they are metadata, not cookies.
  • Segments without = surface as [, segment]; values are unquoted and URL-decoded (with a safe fallback for malformed escapes).
  • requestdata.ts gets a local parseCookieRecord helper preserving the exact old parseCookie semantics for event request data (first-wins record, nameless dropped).

Tests

  • filterCookies.test.ts: flipped the two intended-behavior cases with their exact expectations from the fix(core): Apply the sensitive denylist to cookie headers and configured fetch headers #24090 branch (sid=1; Max-Age=3600; Path=/{sid: '[Filtered]'}; Expires/Domain case → {theme: 'dark'}), plus a nameless-drop case.
  • request.test.ts: two span-attribute expectations updated for the intended change (auth_required; HttpOnly['[Filtered]']), plus a new URL-decode/unquote span test.
  • cookie.test.ts: 17 tests (ported cases + new Set-Cookie/attribute/nameless/array coverage).
  • Results: 195/195 pass across cookie.test.ts (17), filterCookies.test.ts (14), request.test.ts (89), requestdata.test.ts (75).

Note: the it.fails tests from #24090 do not exist on develop yet (they live only in that still-open PR), so they were re-implemented here with the same expectations — small rebase conflict in filterCookies.test.ts is possible if #24090 merges first.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d673c09. Configure here.

Comment thread packages/core/src/utils/cookie.ts
Comment thread packages/core/src/utils/cookie.ts Outdated
Replaces the diverging `parseCookie` (utils/cookie.ts) and the private
`parseCookieHeader` (utils/request.ts) with a single parser returning
ordered `[name, value]` pairs, as requested in the linked issue.

The consolidated parser keeps `parseCookie`'s splitting, quote-stripping
and URL-decoding, gains `parseCookieHeader`'s `Set-Cookie` mode and array
input, and additionally:
- returns nameless segments as `['', segment]` (dropped by `filterCookies`,
  always `[Filtered]` in span attributes),
- splits on `;` with or without a trailing space,
- drops known `Set-Cookie` attribute names (`Max-Age`, `Path`, `Expires`,
  `Domain`, ...) in cookie mode so they are no longer reported as cookies.

Fixes getsentry#24501

Co-Authored-By: Muse Spark <noreply@meta.com>
@Dextheking1
Dextheking1 force-pushed the fix/consolidate-cookie-parsers branch from d673c09 to c0801ce Compare September 20, 2026 10:51
@Dextheking1

Copy link
Copy Markdown
Author

Both findings addressed:

Reparse corruption — real, and it was in requestdata.ts, not the parser. processSegmentSpan rebuilt a cookie string out of already-parsed values (${name}=${value}).join('; ')) and passed it back through httpHeadersToSpanAttributes, which re-parsed it. A value containing % got decoded a second time, and quotes got stripped again.

I extracted the filtering into filterCookiePairs(pairs, behavior) in utils/request.ts and had requestdata.ts call it with the [name, value] entries directly, so no string round trip happens. Added a regression test (does not double-decode cookies already parsed from the request) that fails on the old code: a cookie value of %20 came back as a literal space.

Non-null assertion — replaced headerValue.split(';')[0]! with split(';', 1), which returns a single-element array without needing the assertion.

parseCookiePairs still splits on every ; in Cookie mode; that's inherent to the header format, so a raw ; inside a value can't round trip through it. The fix removes the second pass, which is where the actual corruption happened.

@s1gr1d

s1gr1d commented Sep 21, 2026

Copy link
Copy Markdown
Member

Thanks for the contribution!

This PR was created before #24090 got merged and it would have been the requirement to build on top of it. Closing this PR now in favor of this one: #24536

I added you to the contributor list.

@s1gr1d s1gr1d closed this Sep 21, 2026
@Dextheking1 Dextheking1 changed the title Consolidate the two cookie parsers parseCookie and parseCookieHeader Consolidate the two cookie parsers parseCookieString and getCookieAttachments Sep 21, 2026
s1gr1d added a commit that referenced this pull request Sep 22, 2026
`parseCookie` (used for event cookie records) and `parseCookieHeader`
(used for span attributes) had a different implementation for nameless
segments, `Set-Cookie` attributes, and decoding.

`parseCookieHeader` is the new parser for both and returns ordered
`[name, value]` pairs, with a `set-cookie` mode that ignores cookie
attributes (like Max-Age).

`Set-Cookie` attributes (`Path`, `Domain`, `Max-Age`, ...) are dropped.
They can carry PII and are not cookies.

### Changes for event attributes

| Case | Input | Before | After |
| --- | --- | --- | --- |
| `Set-Cookie` attributes (e.g. Max-Age) | `filterCookies('sid=1;
Max-Age=3600; Path=/', true, 'set-cookie')` | `{ sid: '[Filtered]',
'Max-Age': '3600', Path: '/' }` | `{ sid: '[Filtered]' }` |
| Nameless cookie, `=token` form | `filterCookies('=s3cr3t; theme=dark',
true, 'cookie')` | `{ '': 's3cr3t', theme: 'dark' }`, so the token leaks
| `{ '': '[Filtered]', theme: 'dark' }` |
| Nameless cookie, bare token | `filterCookies('s3cr3t; theme=dark',
true, 'cookie')` | `{ theme: 'dark' }`, the token is dropped | `{ '':
'[Filtered]', theme: 'dark' }` |
| No cookie at all | `filterCookies(';;;', true, 'cookie')` |
`'[Filtered]'` | `{}` |

### What stays the same

| Case | Input | Event record | Span attribute |
| --- | --- | --- | --- |
| Encoded value | `email=jane%40example.com` | `{ email:
'jane@example.com' }` (decoded) | `['email=jane%40example.com']` (raw,
as sent) |
| Repeated name | `lang=en; lang=de` | `{ lang: 'en' }` (first wins) |
`['lang=en', 'lang=de']` |
| Header with no cookie | `;;;` | `{}` | `['[Filtered]']` |

Fixes #24501

Added a changelog contribution entry because of this PR:
#24525
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consolidate the two cookie parsers parseCookie and parseCookieHeader

2 participants