Skip to content

Commit bb73d62

Browse files
authored
Merge pull request #5811 from getsentry/feat/data-collection-cookies
feat(http): [Data Collection 13] Apply cookie collection policy
2 parents 1f2aba4 + 2434ba0 commit bb73d62

22 files changed

Lines changed: 1089 additions & 267 deletions

File tree

‎sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt‎

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import io.sentry.exception.ExceptionMechanismException
2727
import io.sentry.protocol.Mechanism
2828
import io.sentry.protocol.Request
2929
import io.sentry.protocol.Response
30+
import io.sentry.util.CookieUtils
3031
import io.sentry.util.GraphqlUtils
3132
import io.sentry.util.HttpUtils
3233
import io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion
@@ -391,9 +392,7 @@ constructor(
391392
val sentryRequest =
392393
Request().apply {
393394
urlDetails.applyToRequest(this)
394-
// Cookie is only sent if isSendDefaultPii is enabled
395-
cookies =
396-
if (scopes.options.isSendDefaultPii) getHeader("Cookie", request.headers) else null
395+
cookies = CookieUtils.filterCookies(getHeader("Cookie", request.headers), scopes.options)
397396
method = request.method.name
398397
headers = getRequestHeaders(request.headers)
399398
apiTarget = "graphql"
@@ -419,13 +418,8 @@ constructor(
419418

420419
val sentryResponse =
421420
Response().apply {
422-
// Set-Cookie is only sent if isSendDefaultPii is enabled due to PII
423421
cookies =
424-
if (scopes.options.isSendDefaultPii) {
425-
getHeader("Set-Cookie", response.headers)
426-
} else {
427-
null
428-
}
422+
CookieUtils.filterSetCookie(getHeader("Set-Cookie", response.headers), scopes.options)
429423
headers = getResponseHeaders(response.headers)
430424
statusCode = response.statusCode
431425

‎sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorClientErrors.kt‎

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class SentryApollo3InterceptorClientErrors {
7373
httpStatusCode: Int = 200,
7474
responseBody: String = responseBodyOk,
7575
sendDefaultPii: Boolean = false,
76+
includeCookies: Boolean = sendDefaultPii,
7677
socketPolicy: SocketPolicy = SocketPolicy.KEEP_OPEN,
7778
configureOptions: SentryOptions.() -> Unit = {},
7879
): ApolloClient {
@@ -98,8 +99,8 @@ class SentryApollo3InterceptorClientErrors {
9899
.setSocketPolicy(socketPolicy)
99100
.setResponseCode(httpStatusCode)
100101

101-
if (sendDefaultPii) {
102-
response.addHeader("Set-Cookie", "Test")
102+
if (includeCookies) {
103+
response.addHeader("Set-Cookie", "theme=dark; Path=/")
103104
}
104105

105106
server.enqueue(response)
@@ -112,8 +113,8 @@ class SentryApollo3InterceptorClientErrors {
112113
captureFailedRequests = captureFailedRequests,
113114
failedRequestTargets = failedRequestTargets,
114115
)
115-
if (sendDefaultPii) {
116-
builder.addHttpHeader("Cookie", "Test")
116+
if (includeCookies) {
117+
builder.addHttpHeader("Cookie", "theme=dark; sessionId=secret")
117118
}
118119

119120
return builder.build()
@@ -362,6 +363,46 @@ class SentryApollo3InterceptorClientErrors {
362363
)
363364
}
364365

366+
@Test
367+
fun `data collection filters cookies`() {
368+
val sut =
369+
fixture.getSut(responseBody = fixture.responseBodyNotOk, includeCookies = true) {
370+
dataCollection.cookies = KeyValueCollectionBehavior.denyList("theme")
371+
}
372+
executeQuery(sut)
373+
374+
verify(fixture.scopes)
375+
.captureEvent(
376+
check {
377+
assertEquals("theme=[Filtered]; sessionId=[Filtered]", it.request!!.cookies)
378+
assertEquals("theme=[Filtered]; Path=/", it.contexts.response!!.cookies)
379+
},
380+
any<Hint>(),
381+
)
382+
}
383+
384+
@Test
385+
fun `data collection can disable cookies`() {
386+
val sut =
387+
fixture.getSut(
388+
responseBody = fixture.responseBodyNotOk,
389+
sendDefaultPii = true,
390+
includeCookies = true,
391+
) {
392+
dataCollection.cookies = KeyValueCollectionBehavior.off()
393+
}
394+
executeQuery(sut)
395+
396+
verify(fixture.scopes)
397+
.captureEvent(
398+
check {
399+
assertNull(it.request!!.cookies)
400+
assertNull(it.contexts.response!!.cookies)
401+
},
402+
any<Hint>(),
403+
)
404+
}
405+
365406
@Test
366407
fun `data collection can disable request headers`() {
367408
val sut =
@@ -387,7 +428,7 @@ class SentryApollo3InterceptorClientErrors {
387428
check {
388429
val request = it.request!!
389430

390-
assertEquals("Test", request.cookies)
431+
assertEquals("theme=dark; sessionId=secret", request.cookies)
391432
assertNotNull(request.headers)
392433
assertEquals("LaunchDetails", request.headers?.get("X-APOLLO-OPERATION-NAME"))
393434
},
@@ -477,7 +518,7 @@ class SentryApollo3InterceptorClientErrors {
477518
check {
478519
val response = it.contexts.response!!
479520

480-
assertEquals("Test", response.cookies)
521+
assertEquals("theme=dark; Path=/", response.cookies)
481522
assertNotNull(response.headers)
482523
assertEquals(200, response.headers?.get("Content-Length")?.toInt())
483524
},

‎sentry-apollo-4/src/main/java/io/sentry/apollo4/SentryApollo4HttpInterceptor.kt‎

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import io.sentry.exception.ExceptionMechanismException
2525
import io.sentry.protocol.Mechanism
2626
import io.sentry.protocol.Request
2727
import io.sentry.protocol.Response
28+
import io.sentry.util.CookieUtils
2829
import io.sentry.util.GraphqlUtils
2930
import io.sentry.util.HttpUtils
3031
import io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion
@@ -390,9 +391,7 @@ constructor(
390391
val sentryRequest =
391392
Request().apply {
392393
urlDetails.applyToRequest(this)
393-
// Cookie is only sent if isSendDefaultPii is enabled
394-
cookies =
395-
if (scopes.options.isSendDefaultPii) getHeader("Cookie", request.headers) else null
394+
cookies = CookieUtils.filterCookies(getHeader("Cookie", request.headers), scopes.options)
396395
method = request.method.name
397396
headers = getRequestHeaders(request.headers)
398397
apiTarget = "graphql"
@@ -418,13 +417,8 @@ constructor(
418417

419418
val sentryResponse =
420419
Response().apply {
421-
// Set-Cookie is only sent if isSendDefaultPii is enabled due to PII
422420
cookies =
423-
if (scopes.options.isSendDefaultPii) {
424-
getHeader("Set-Cookie", response.headers)
425-
} else {
426-
null
427-
}
421+
CookieUtils.filterSetCookie(getHeader("Set-Cookie", response.headers), scopes.options)
428422
headers = getResponseHeaders(response.headers)
429423
statusCode = response.statusCode
430424

‎sentry-apollo-4/src/test/java/io/sentry/apollo4/SentryApollo4BuilderExtensionsClientErrorsTest.kt‎

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ abstract class SentryApollo4BuilderExtensionsClientErrorsTest(
8787
httpStatusCode: Int = 200,
8888
responseBody: String = responseBodyOk,
8989
sendDefaultPii: Boolean = false,
90+
includeCookies: Boolean = sendDefaultPii,
9091
socketPolicy: SocketPolicy = SocketPolicy.KEEP_OPEN,
9192
configureOptions: SentryOptions.() -> Unit = {},
9293
): ApolloClient {
@@ -112,8 +113,8 @@ abstract class SentryApollo4BuilderExtensionsClientErrorsTest(
112113
.setSocketPolicy(socketPolicy)
113114
.setResponseCode(httpStatusCode)
114115

115-
if (sendDefaultPii) {
116-
response.addHeader("Set-Cookie", "Test")
116+
if (includeCookies) {
117+
response.addHeader("Set-Cookie", "theme=dark; Path=/")
117118
}
118119

119120
server.enqueue(response)
@@ -126,8 +127,8 @@ abstract class SentryApollo4BuilderExtensionsClientErrorsTest(
126127
captureFailedRequests = captureFailedRequests,
127128
failedRequestTargets = failedRequestTargets,
128129
)
129-
if (sendDefaultPii) {
130-
builder.addHttpHeader("Cookie", "Test")
130+
if (includeCookies) {
131+
builder.addHttpHeader("Cookie", "theme=dark; sessionId=secret")
131132
}
132133

133134
return builder.build()
@@ -356,6 +357,46 @@ abstract class SentryApollo4BuilderExtensionsClientErrorsTest(
356357
)
357358
}
358359

360+
@Test
361+
fun `data collection filters cookies`() {
362+
val sut =
363+
fixture.getSut(responseBody = fixture.responseBodyNotOk, includeCookies = true) {
364+
dataCollection.cookies = KeyValueCollectionBehavior.denyList("theme")
365+
}
366+
executeQuery(sut)
367+
368+
verify(fixture.scopes)
369+
.captureEvent(
370+
check {
371+
assertEquals("theme=[Filtered]; sessionId=[Filtered]", it.request!!.cookies)
372+
assertEquals("theme=[Filtered]; Path=/", it.contexts.response!!.cookies)
373+
},
374+
any<Hint>(),
375+
)
376+
}
377+
378+
@Test
379+
fun `data collection can disable cookies`() {
380+
val sut =
381+
fixture.getSut(
382+
responseBody = fixture.responseBodyNotOk,
383+
sendDefaultPii = true,
384+
includeCookies = true,
385+
) {
386+
dataCollection.cookies = KeyValueCollectionBehavior.off()
387+
}
388+
executeQuery(sut)
389+
390+
verify(fixture.scopes)
391+
.captureEvent(
392+
check {
393+
assertNull(it.request!!.cookies)
394+
assertNull(it.contexts.response!!.cookies)
395+
},
396+
any<Hint>(),
397+
)
398+
}
399+
359400
@Test
360401
fun `data collection filters request headers`() {
361402
val sut =
@@ -398,7 +439,7 @@ abstract class SentryApollo4BuilderExtensionsClientErrorsTest(
398439
check {
399440
val request = it.request!!
400441

401-
assertEquals("Test", request.cookies)
442+
assertEquals("theme=dark; sessionId=secret", request.cookies)
402443
assertNotNull(request.headers)
403444
},
404445
any<Hint>(),
@@ -487,7 +528,7 @@ abstract class SentryApollo4BuilderExtensionsClientErrorsTest(
487528
check {
488529
val response = it.contexts.response!!
489530

490-
assertEquals("Test", response.cookies)
531+
assertEquals("theme=dark; Path=/", response.cookies)
491532
assertNotNull(response.headers)
492533
assertEquals(200, response.headers?.get("Content-Length")?.toInt())
493534
},

‎sentry-ktor-client/src/main/java/io/sentry/ktorClient/SentryKtorClientUtils.kt‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import io.sentry.TypeCheckHint
1616
import io.sentry.exception.ExceptionMechanismException
1717
import io.sentry.exception.SentryHttpClientException
1818
import io.sentry.protocol.Mechanism
19+
import io.sentry.util.CookieUtils
1920
import io.sentry.util.HttpUtils
2021
import io.sentry.util.UrlUtils
2122

@@ -36,18 +37,16 @@ internal object SentryKtorClientUtils {
3637

3738
val sentryRequest =
3839
io.sentry.protocol.Request().apply {
39-
// Cookie is only sent if isSendDefaultPii is enabled
4040
urlDetails.applyToRequest(this)
41-
cookies = if (scopes.options.isSendDefaultPii) request.headers["Cookie"] else null
41+
cookies = CookieUtils.filterCookies(request.headers["Cookie"], scopes.options)
4242
method = request.method.value
4343
headers = getRequestHeaders(scopes, request.headers)
4444
bodySize = request.content.contentLength
4545
}
4646

4747
val sentryResponse =
4848
io.sentry.protocol.Response().apply {
49-
// Set-Cookie is only sent if isSendDefaultPii is enabled due to PII
50-
cookies = if (scopes.options.isSendDefaultPii) response.headers["Set-Cookie"] else null
49+
cookies = CookieUtils.filterSetCookie(response.headers["Set-Cookie"], scopes.options)
5150
headers = getResponseHeaders(scopes, response.headers)
5251
statusCode = response.status.value
5352
try {

‎sentry-ktor-client/src/test/java/io/sentry/ktorClient/SentryKtorClientPluginTest.kt‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class SentryKtorClientPluginTest {
104104
MockResponse()
105105
.setBody(responseBody)
106106
.addHeader("myResponseHeader", "myValue")
107+
.addHeader("Set-Cookie", "theme=dark; Path=/")
107108
.setSocketPolicy(socketPolicy)
108109
.setResponseCode(httpStatusCode)
109110
)
@@ -256,6 +257,60 @@ class SentryKtorClientPluginTest {
256257
verify(fixture.scopes, never()).captureEvent(any(), any<Hint>())
257258
}
258259

260+
@Test
261+
fun `data collection filters cookies`(): Unit = runBlocking {
262+
val sut =
263+
fixture.getSut(
264+
captureFailedRequests = true,
265+
httpStatusCode = 500,
266+
optionsConfiguration =
267+
Sentry.OptionsConfiguration {
268+
it.dataCollection.cookies = KeyValueCollectionBehavior.denyList("theme")
269+
},
270+
)
271+
272+
sut.get(fixture.server.url("/hello").toString()) {
273+
headers["Cookie"] = "language=en; theme=dark; sessionId=secret"
274+
}
275+
276+
verify(fixture.scopes)
277+
.captureEvent(
278+
check<SentryEvent> {
279+
assertEquals(
280+
"language=en; theme=[Filtered]; sessionId=[Filtered]",
281+
it.request!!.cookies,
282+
)
283+
assertEquals("theme=[Filtered]; Path=/", it.contexts.response!!.cookies)
284+
},
285+
any<Hint>(),
286+
)
287+
}
288+
289+
@Test
290+
fun `data collection can disable cookies`(): Unit = runBlocking {
291+
val sut =
292+
fixture.getSut(
293+
captureFailedRequests = true,
294+
httpStatusCode = 500,
295+
sendDefaultPii = true,
296+
optionsConfiguration =
297+
Sentry.OptionsConfiguration {
298+
it.dataCollection.cookies = KeyValueCollectionBehavior.off()
299+
},
300+
)
301+
302+
sut.get(fixture.server.url("/hello").toString()) { headers["Cookie"] = "theme=dark" }
303+
304+
verify(fixture.scopes)
305+
.captureEvent(
306+
check<SentryEvent> {
307+
assertNull(it.request!!.cookies)
308+
assertNull(it.contexts.response!!.cookies)
309+
},
310+
any<Hint>(),
311+
)
312+
}
313+
259314
@Test
260315
fun `data collection filters request headers`(): Unit = runBlocking {
261316
val sut =

‎sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpUtils.kt‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import io.sentry.TypeCheckHint
77
import io.sentry.exception.ExceptionMechanismException
88
import io.sentry.exception.SentryHttpClientException
99
import io.sentry.protocol.Mechanism
10+
import io.sentry.util.CookieUtils
1011
import io.sentry.util.HttpUtils
1112
import io.sentry.util.UrlUtils
1213
import okhttp3.Headers
@@ -37,8 +38,7 @@ internal object SentryOkHttpUtils {
3738
val sentryRequest =
3839
io.sentry.protocol.Request().apply {
3940
urlDetails.applyToRequest(this)
40-
// Cookie is only sent if isSendDefaultPii is enabled
41-
cookies = if (scopes.options.isSendDefaultPii) request.headers["Cookie"] else null
41+
cookies = CookieUtils.filterCookies(request.headers["Cookie"], scopes.options)
4242
method = request.method
4343
headers = getRequestHeaders(scopes, request.headers)
4444

@@ -47,8 +47,7 @@ internal object SentryOkHttpUtils {
4747

4848
val sentryResponse =
4949
io.sentry.protocol.Response().apply {
50-
// Set-Cookie is only sent if isSendDefaultPii is enabled due to PII
51-
cookies = if (scopes.options.isSendDefaultPii) response.headers["Set-Cookie"] else null
50+
cookies = CookieUtils.filterSetCookie(response.headers["Set-Cookie"], scopes.options)
5251
headers = getResponseHeaders(scopes, response.headers)
5352
statusCode = response.code
5453

0 commit comments

Comments
 (0)