Skip to content

Commit 65e461b

Browse files
committed
Merge branch 'feat/data-collection-cookies' into feat/data-collection-user-info
2 parents a0d6c1c + 1987b1e commit 65e461b

2 files changed

Lines changed: 100 additions & 3 deletions

File tree

sentry/src/main/java/io/sentry/util/HttpUtils.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ public static boolean containsSensitiveHeader(final @NotNull String header) {
156156
}
157157
return filteredCookies.toString();
158158
} catch (Throwable ignored) {
159-
return null;
159+
return SENSITIVE_DATA_SUBSTITUTE;
160160
}
161161
}
162162

@@ -170,20 +170,27 @@ public static boolean containsSensitiveHeader(final @NotNull String header) {
170170
final int attributesSeparator = cookie.indexOf(';');
171171
final @NotNull String cookieValue =
172172
attributesSeparator < 0 ? cookie : cookie.substring(0, attributesSeparator);
173+
if (!isValidCookiePair(cookieValue)) {
174+
return SENSITIVE_DATA_SUBSTITUTE;
175+
}
173176
final @NotNull String attributes =
174177
attributesSeparator < 0 ? "" : cookie.substring(attributesSeparator);
175178
return filterCookie(cookieValue, behavior, null) + attributes;
176179
} catch (Throwable ignored) {
177-
return null;
180+
return SENSITIVE_DATA_SUBSTITUTE;
178181
}
179182
}
180183

181184
private static @NotNull String filterCookie(
182185
final @NotNull String cookie,
183186
final @NotNull KeyValueCollectionBehavior behavior,
184187
final @Nullable List<String> additionalSensitiveCookieNames) {
188+
if (!isValidCookiePair(cookie)) {
189+
return SENSITIVE_DATA_SUBSTITUTE;
190+
}
191+
185192
final int separator = cookie.indexOf('=');
186-
final @NotNull String name = separator < 0 ? cookie : cookie.substring(0, separator);
193+
final @NotNull String name = cookie.substring(0, separator);
187194
final @NotNull String normalizedName = name.trim();
188195
final boolean sensitive =
189196
containsTerm(normalizedName, SENSITIVE_DATA_KEYS)
@@ -200,6 +207,11 @@ public static boolean containsSensitiveHeader(final @NotNull String header) {
200207
return cookie;
201208
}
202209

210+
private static boolean isValidCookiePair(final @NotNull String cookie) {
211+
final int separator = cookie.indexOf('=');
212+
return separator >= 0 && !cookie.substring(0, separator).trim().isEmpty();
213+
}
214+
203215
public static @NotNull Map<String, String> filterHeaders(
204216
final @NotNull Map<String, String> headers,
205217
final @NotNull KeyValueCollectionBehavior behavior) {

sentry/src/test/java/io/sentry/util/HttpUtilsTest.kt

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,18 @@ class HttpUtilsTest {
9797
.isEqualTo("theme=dark; sessionId=[Filtered]; language=[Filtered]")
9898
}
9999

100+
@Test
101+
fun `cookie filter preserves empty and padded base64 values`() {
102+
assertThat(
103+
HttpUtils.filterCookies(
104+
"empty=; data=YWJjZA==",
105+
KeyValueCollectionBehavior.denyList(),
106+
emptyList(),
107+
)
108+
)
109+
.isEqualTo("empty=; data=YWJjZA==")
110+
}
111+
100112
@Test
101113
fun `cookie filter uses only the first equals separator`() {
102114
assertThat(
@@ -109,6 +121,30 @@ class HttpUtilsTest {
109121
.isEqualTo("theme=dark=contrast; token=[Filtered]")
110122
}
111123

124+
@Test
125+
fun `cookie filter replaces malformed pairs without discarding valid pairs`() {
126+
assertThat(
127+
HttpUtils.filterCookies(
128+
"theme=dark; opaque; =secret; empty=; sessionId=secret",
129+
KeyValueCollectionBehavior.denyList(),
130+
emptyList(),
131+
)
132+
)
133+
.isEqualTo("theme=dark;[Filtered];[Filtered]; empty=; sessionId=[Filtered]")
134+
}
135+
136+
@Test
137+
fun `cookie allow list never exposes malformed pairs`() {
138+
assertThat(
139+
HttpUtils.filterCookies(
140+
"theme=dark; opaque; =secret",
141+
KeyValueCollectionBehavior.allowList("theme", "opaque"),
142+
emptyList(),
143+
)
144+
)
145+
.isEqualTo("theme=dark;[Filtered];[Filtered]")
146+
}
147+
112148
@Test
113149
fun `set cookie filter preserves attributes`() {
114150
assertThat(
@@ -120,6 +156,26 @@ class HttpUtilsTest {
120156
.isEqualTo("sessionId=[Filtered]; Path=/; HttpOnly; SameSite=Lax")
121157
}
122158

159+
@Test
160+
fun `set cookie filter preserves empty and padded base64 values`() {
161+
assertThat(
162+
HttpUtils.filterSetCookie(
163+
"data=YWJjZA==; Expires=Wed, 09 Jun 2021 10:18:14 GMT; Max-Age=3600; Domain=example.com; Path=/; Secure; HttpOnly; SameSite=Lax",
164+
KeyValueCollectionBehavior.denyList(),
165+
)
166+
)
167+
.isEqualTo(
168+
"data=YWJjZA==; Expires=Wed, 09 Jun 2021 10:18:14 GMT; Max-Age=3600; Domain=example.com; Path=/; Secure; HttpOnly; SameSite=Lax"
169+
)
170+
assertThat(
171+
HttpUtils.filterSetCookie(
172+
"empty=; Path=/",
173+
KeyValueCollectionBehavior.denyList(),
174+
)
175+
)
176+
.isEqualTo("empty=; Path=/")
177+
}
178+
123179
@Test
124180
fun `set cookie allow list retains allowed non-sensitive value and attributes`() {
125181
assertThat(
@@ -131,6 +187,35 @@ class HttpUtilsTest {
131187
.isEqualTo("theme=dark; Path=/; Secure")
132188
}
133189

190+
@Test
191+
fun `set cookie filter replaces malformed cookie pair and discards attributes`() {
192+
assertThat(
193+
HttpUtils.filterSetCookie(
194+
"opaque; Path=/; HttpOnly",
195+
KeyValueCollectionBehavior.denyList(),
196+
)
197+
)
198+
.isEqualTo("[Filtered]")
199+
assertThat(
200+
HttpUtils.filterSetCookie(
201+
"=secret; Path=/; HttpOnly",
202+
KeyValueCollectionBehavior.denyList(),
203+
)
204+
)
205+
.isEqualTo("[Filtered]")
206+
}
207+
208+
@Test
209+
fun `set cookie allow list never exposes malformed cookie pair`() {
210+
assertThat(
211+
HttpUtils.filterSetCookie(
212+
"opaque; Path=/; HttpOnly",
213+
KeyValueCollectionBehavior.allowList("opaque"),
214+
)
215+
)
216+
.isEqualTo("[Filtered]")
217+
}
218+
134219
@Test
135220
fun `set cookie filter disables collection in off mode`() {
136221
assertThat(

0 commit comments

Comments
 (0)