feat(http): [Data Collection 13] Apply cookie collection policy - #5811
feat(http): [Data Collection 13] Apply cookie collection policy#5811adinauer wants to merge 12 commits into
Conversation
Filter automatically captured request and response cookies according to the Data Collection policy across Spring and HTTP client integrations. Preserve existing sendDefaultPii behavior when Data Collection is absent. Co-Authored-By: Claude <noreply@anthropic.com>
|
📲 Install BuildsAndroid
|
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| adae5de | 372.16 ms | 476.20 ms | 104.04 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| adae5de | 0 B | 0 B | 0 B |
Previous results on branch: feat/data-collection-cookies
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 670b3d8 | 329.76 ms | 371.15 ms | 41.39 ms |
| df76abc | 308.28 ms | 356.24 ms | 47.96 ms |
| b0be236 | 316.30 ms | 372.60 ms | 56.30 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 670b3d8 | 0 B | 0 B | 0 B |
| df76abc | 0 B | 0 B | 0 B |
| b0be236 | 0 B | 0 B | 0 B |
Replace malformed request cookie pairs and invalid Set-Cookie values with the filtered placeholder. Preserve valid empty values, padded values, and response cookie attributes. Co-Authored-By: Claude <noreply@anthropic.com>
Keep mocked OkHttp responses consistent with the non-null headers contract so failed-request capture can inspect response cookies. Co-Authored-By: Claude <noreply@anthropic.com>
Semver Impact of This PR🟡 Minor (new features) 📋 Changelog PreviewThis is how your changes will appear in the changelog. This PR will not appear in the changelog. 🤖 This preview updates automatically when you update the PR. |
| // Cookie is only sent if isSendDefaultPii is enabled | ||
| urlDetails.applyToRequest(this) | ||
| cookies = if (scopes.options.isSendDefaultPii) request.headers["Cookie"] else null | ||
| cookies = getRequestCookies(scopes, request.headers["Cookie"]) |
There was a problem hiding this comment.
There could be multiple Cookie and Set-Cookie headers. we are currently only ever processing one of them and send them to sentry. Is that what we want or should we go through all of these?
There was a problem hiding this comment.
I've created #5982 so we can change this (likely) in the next major.
| io.sentry.protocol.Response().apply { | ||
| // Set-Cookie is only sent if isSendDefaultPii is enabled due to PII | ||
| cookies = if (scopes.options.isSendDefaultPii) response.headers["Set-Cookie"] else null | ||
| cookies = getResponseCookies(scopes, response.headers["Set-Cookie"]) |
There was a problem hiding this comment.
There could be multiple Cookie and Set-Cookie headers. we are currently only ever processing one of them and send them to sentry. Is that what we want or should we go through all of these?
| urlDetails.applyToRequest(this) | ||
| // Cookie is only sent if isSendDefaultPii is enabled | ||
| cookies = if (scopes.options.isSendDefaultPii) request.headers["Cookie"] else null | ||
| cookies = getRequestCookies(scopes, request.headers["Cookie"]) |
There was a problem hiding this comment.
There could be multiple Cookie and Set-Cookie headers. here we are currently only ever processing one of them and send them to sentry. Is that what we want or should we go through all of these?
| io.sentry.protocol.Response().apply { | ||
| // Set-Cookie is only sent if isSendDefaultPii is enabled due to PII | ||
| cookies = if (scopes.options.isSendDefaultPii) response.headers["Set-Cookie"] else null | ||
| cookies = getResponseCookies(scopes, response.headers["Set-Cookie"]) |
There was a problem hiding this comment.
There could be multiple Cookie and Set-Cookie headers. here we are currently only ever processing one of them and send them to sentry. Is that what we want or should we go through all of these?
| } | ||
|
|
||
| private fun getResponseCookies(headers: List<HttpHeader>): String? { | ||
| val cookies = getHeader("Set-Cookie", headers) |
There was a problem hiding this comment.
There could be multiple Cookie and Set-Cookie headers. here we are currently only ever processing one of them and send them to sentry. Is that what we want or should we go through all of these?
| } else { | ||
| null | ||
| } | ||
| cookies = getResponseCookies(response.headers) |
There was a problem hiding this comment.
There could be multiple Cookie and Set-Cookie headers. here we are currently only ever processing one of them and send them to sentry. Is that what we want or should we go through all of these?
| } | ||
|
|
||
| try { | ||
| final @NotNull String[] cookieValues = cookies.split(";", -1); |
There was a problem hiding this comment.
Malformed cookies could become a problem. i.e.
Cookie: theme=dark, sessionId=secret
Cookie: theme=dark sessionId=secret
would be parsed as valid in our current implementation and leak the sessionId.
This is a fabricated example but we may want to check for cookie validity
There was a problem hiding this comment.
I've improved the parser so this now falls back to [Filtered].
Validate cookie names and values before applying Data Collection filters. Fail closed for malformed values that could embed additional sensitive cookie pairs while preserving valid quoted and padded values. Refs #5666 Co-Authored-By: Claude <noreply@anthropic.com>
Bring the latest data collection behavior into the cookie filtering branch and retain coverage for both cookie and request header filtering. Co-Authored-By: Claude <noreply@anthropic.com>
runningcode
left a comment
There was a problem hiding this comment.
added some comments!
| filterCookie(cookieValues[i], behavior, additionalSensitiveCookieNames)); | ||
| } | ||
| return filteredCookies.toString(); | ||
| } catch (Throwable ignored) { |
There was a problem hiding this comment.
This should probably catch something narrower otherwise we have no way of knowing that this code is working or not.
We also shouldn't be catching exceptions like OutOfMemoryError or StackOverflowError as the jvm is already in a bad state and ignoring it will leave the JVM in a worse state.
| return filteredQuery.toString(); | ||
| } | ||
|
|
||
| public static @Nullable List<String> filterCookiesFromHeader( |
There was a problem hiding this comment.
Two nits:
- Can we move all the cookie related methods to a
Cookies.java? I feelHttpUtils.javais getting too long. This also makes the methods more idiomatic.Cookies.filterFromHeader()reads more naturally to me thanHttpUtils.filterCookiesFromHeader() - Can we declare all of these as
@ApiUsage.Internalor are they intended to be customer facing?
| return filteredHeaders; | ||
| } | ||
|
|
||
| public static @Nullable String filterCookies( |
There was a problem hiding this comment.
can you add a test for trailing spaces in cookies ?
my clanker tells me this would add a [Filtered] because of the way split is called.
|
|
||
| final @NotNull List<String> filteredHeaders = new ArrayList<>(); | ||
| for (final String header : headers) { | ||
| filteredHeaders.add(filterCookies(header, behavior, additionalSensitiveCookieNames)); |
There was a problem hiding this comment.
this can add null items in to the list since filterCookies is @Nullable
PR Stack (Data Collection)
📜 Description
Apply the cookie Data Collection policy to automatically captured request and response cookies.
The policy is used by Spring MVC/WebFlux, OkHttp, Ktor, and Apollo 3/4 failed-request events. Explicit Data Collection supports off, deny-list, and allow-list behavior. Built-in sensitive cookie names and Spring integration-provided session cookie names are always filtered, including when allow-listed.
Set-Cookievalues are parsed separately so attributes such asPath,HttpOnly, andSameSiteare preserved.When Data Collection is absent, integrations preserve their existing
sendDefaultPiibehavior, including Spring's legacy security-cookie filtering and raw HTTP-client cookie values.💡 Motivation and Context
Cookie collection previously relied on
sendDefaultPiias an on/off gate, with filtering only in Spring integrations. This wires the existingdataCollection.cookiesoption across cookie capture paths and provides consistent explicit-mode filtering without changing the legacy bridge for applications that have not configured Data Collection.Refs #5666
💚 How did you test it?
./gradlew spotlessApply apiDump./gradlew :sentry:apiCheckgit diff --check📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps
Complete the remaining Data Collection configuration, documentation, and migration work.
#skip-changelog