From de1d908a3274a9a681d840d64d12084dadbeb663 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 24 Feb 2025 11:28:42 +0100 Subject: [PATCH 1/7] refactor: use `java.net.URI` for parsing in `UrlUtils` --- .../main/java/io/sentry/util/UrlUtils.java | 135 ++++---------- .../test/java/io/sentry/util/UrlUtilsTest.kt | 167 +++++++++++++++++- 2 files changed, 194 insertions(+), 108 deletions(-) diff --git a/sentry/src/main/java/io/sentry/util/UrlUtils.java b/sentry/src/main/java/io/sentry/util/UrlUtils.java index dc36ba678c4..93397577e8d 100644 --- a/sentry/src/main/java/io/sentry/util/UrlUtils.java +++ b/sentry/src/main/java/io/sentry/util/UrlUtils.java @@ -3,10 +3,7 @@ import io.sentry.ISpan; import io.sentry.SpanDataConvention; import io.sentry.protocol.Request; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.net.URI; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -15,123 +12,55 @@ public final class UrlUtils { public static final @NotNull String SENSITIVE_DATA_SUBSTITUTE = "[Filtered]"; - private static final @NotNull Pattern AUTH_REGEX = Pattern.compile("(.+://)(.*@)(.*)"); public static @Nullable UrlDetails parseNullable(final @Nullable String url) { - if (url == null) { - return null; - } - - return parse(url); + return url == null ? null : parse(url); } public static @NotNull UrlDetails parse(final @NotNull String url) { - if (isAbsoluteUrl(url)) { - return splitAbsoluteUrl(url); - } else { - return splitRelativeUrl(url); - } - } - - private static boolean isAbsoluteUrl(@NotNull String url) { - return url.contains("://"); - } - - private static @NotNull UrlDetails splitRelativeUrl(final @NotNull String url) { - final int queryParamSeparatorIndex = url.indexOf("?"); - final int fragmentSeparatorIndex = url.indexOf("#"); - - final @Nullable String baseUrl = - extractBaseUrl(url, queryParamSeparatorIndex, fragmentSeparatorIndex); - final @Nullable String query = - extractQuery(url, queryParamSeparatorIndex, fragmentSeparatorIndex); - final @Nullable String fragment = extractFragment(url, fragmentSeparatorIndex); + try { + URI uri = new URI(url); + if (uri.isAbsolute() && !isValidAbsoluteUrl(uri)) { + return new UrlDetails(null, null, null); + } - return new UrlDetails(baseUrl, query, fragment); - } + final @NotNull String schemeAndSeparator = + uri.getScheme() == null ? "" : (uri.getScheme() + "://"); + final @NotNull String authority = uri.getRawAuthority() == null ? "" : uri.getRawAuthority(); + final @NotNull String path = uri.getRawPath() == null ? "" : uri.getRawPath(); + final @Nullable String query = uri.getRawQuery(); + final @Nullable String fragment = uri.getRawFragment(); - private static @Nullable String extractBaseUrl( - final @NotNull String url, - final int queryParamSeparatorIndex, - final int fragmentSeparatorIndex) { - if (queryParamSeparatorIndex >= 0) { - return url.substring(0, queryParamSeparatorIndex).trim(); - } else if (fragmentSeparatorIndex >= 0) { - return url.substring(0, fragmentSeparatorIndex).trim(); - } else { - return url; - } - } + final @NotNull String filteredUrl = schemeAndSeparator + filterUserInfo(authority) + path; - private static @Nullable String extractQuery( - final @NotNull String url, - final int queryParamSeparatorIndex, - final int fragmentSeparatorIndex) { - if (queryParamSeparatorIndex > 0) { - if (fragmentSeparatorIndex > 0 && fragmentSeparatorIndex > queryParamSeparatorIndex) { - return url.substring(queryParamSeparatorIndex + 1, fragmentSeparatorIndex).trim(); - } else { - return url.substring(queryParamSeparatorIndex + 1).trim(); - } - } else { - return null; - } - } - - private static @Nullable String extractFragment( - final @NotNull String url, final int fragmentSeparatorIndex) { - if (fragmentSeparatorIndex > 0) { - return url.substring(fragmentSeparatorIndex + 1).trim(); - } else { - return null; + return new UrlDetails(filteredUrl, query, fragment); + } catch (Exception e) { + return new UrlDetails(null, null, null); } } - private static @NotNull UrlDetails splitAbsoluteUrl(final @NotNull String url) { + private static boolean isValidAbsoluteUrl(final @NotNull URI uri) { try { - final @NotNull String filteredUrl = urlWithAuthRemoved(url); - final @NotNull URL urlObj = new URL(url); - final @NotNull String baseUrl = baseUrlOnly(filteredUrl); - if (baseUrl.contains("#")) { - // url considered malformed because it has fragment - return new UrlDetails(null, null, null); - } else { - final @Nullable String query = urlObj.getQuery(); - final @Nullable String fragment = urlObj.getRef(); - return new UrlDetails(baseUrl, query, fragment); - } - } catch (MalformedURLException e) { - return new UrlDetails(null, null, null); + uri.toURL(); + } catch (Exception e) { + return false; } + return true; } - private static @NotNull String urlWithAuthRemoved(final @NotNull String url) { - final @NotNull Matcher userInfoMatcher = AUTH_REGEX.matcher(url); - if (userInfoMatcher.matches() && userInfoMatcher.groupCount() == 3) { - final @NotNull String userInfoString = userInfoMatcher.group(2); - final @NotNull String replacementString = - userInfoString.contains(":") - ? (SENSITIVE_DATA_SUBSTITUTE + ":" + SENSITIVE_DATA_SUBSTITUTE + "@") - : (SENSITIVE_DATA_SUBSTITUTE + "@"); - return userInfoMatcher.group(1) + replacementString + userInfoMatcher.group(3); - } else { + private static @NotNull String filterUserInfo(final @NotNull String url) { + if (!url.contains("@")) { return url; } - } - - private static @NotNull String baseUrlOnly(final @NotNull String url) { - final int queryParamSeparatorIndex = url.indexOf("?"); - - if (queryParamSeparatorIndex >= 0) { - return url.substring(0, queryParamSeparatorIndex).trim(); - } else { - final int fragmentSeparatorIndex = url.indexOf("#"); - if (fragmentSeparatorIndex >= 0) { - return url.substring(0, fragmentSeparatorIndex).trim(); - } else { - return url; - } + if (url.startsWith("@")) { + return SENSITIVE_DATA_SUBSTITUTE + url; } + final @NotNull String userInfo = url.substring(0, url.indexOf('@') - 1); + final @NotNull String filteredUserInfo = + userInfo.contains(":") + ? (SENSITIVE_DATA_SUBSTITUTE + ":" + SENSITIVE_DATA_SUBSTITUTE) + : SENSITIVE_DATA_SUBSTITUTE; + return filteredUserInfo + url.substring(url.indexOf('@')); } public static final class UrlDetails { diff --git a/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt b/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt index af037b3344a..fc4de81a107 100644 --- a/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt @@ -1,5 +1,6 @@ package io.sentry.util +import java.net.URL import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull @@ -11,6 +12,32 @@ class UrlUtilsTest { assertNull(UrlUtils.parseNullable(null)) } + @Test + fun `i null for null`() { + val url = URL("https://www.example.com/search?q=hello%20world%21") + var uri = url.toURI() + } + + @Test + fun `filters auth`() { + val urlDetails = UrlUtils.parseNullable( + "https://user:password@sentry.io?q=1&s=2&token=secret" + )!! + assertEquals("https://[Filtered]:[Filtered]@sentry.io", urlDetails.url) + assertEquals("q=1&s=2&token=secret", urlDetails.query) + assertNull(urlDetails.fragment) + } + + @Test + fun `filters auth with fragment`() { + val urlDetails = UrlUtils.parseNullable( + "https://user:password@sentry.io?q=1&s=2&token=secret#top" + )!! + assertEquals("https://[Filtered]:[Filtered]@sentry.io", urlDetails.url) + assertEquals("q=1&s=2&token=secret", urlDetails.query) + assertEquals("top", urlDetails.fragment) + } + @Test fun `strips user info with user and password from http nullable`() { val urlDetails = UrlUtils.parseNullable( @@ -161,28 +188,70 @@ class UrlUtilsTest { assertEquals("top", urlDetails.fragment) } + // Fragment is allowed to contain '?' according to RFC 3986 + // See https://datatracker.ietf.org/doc/html/rfc3986#section-3.5 @Test - fun `no details extracted with query after fragment`() { + fun `extracts details with question mark after fragment`() { val urlDetails = UrlUtils.parse( "https://user:password@sentry.io#fragment?q=1&s=2&token=secret" ) - assertNull(urlDetails.url) + assertEquals("https://[Filtered]:[Filtered]@sentry.io", urlDetails.url) assertNull(urlDetails.query) - assertNull(urlDetails.fragment) + assertEquals("fragment?q=1&s=2&token=secret", urlDetails.fragment) } @Test - fun `no details extracted with query after fragment without authority`() { + fun `extracts details with question mark after fragment without user info`() { val urlDetails = UrlUtils.parse( "https://sentry.io#fragment?q=1&s=2&token=secret" ) + assertEquals("https://sentry.io", urlDetails.url) + assertNull(urlDetails.query) + assertEquals("fragment?q=1&s=2&token=secret", urlDetails.fragment) + } + + @Test + fun `filters empty user info`() { + val urlDetails = UrlUtils.parse( + "https://@sentry.io?query=a#fragment?q=1&s=2&token=secret" + ) + assertEquals("https://[Filtered]@sentry.io", urlDetails.url) + assertEquals("query=a", urlDetails.query) + assertEquals("fragment?q=1&s=2&token=secret", urlDetails.fragment) + } + + @Test + fun `extracts details from relative url with leading @ symbol`() { + val urlDetails = UrlUtils.parse( + "@@sentry.io/pages/10?query=a#fragment?q=1&s=2&token=secret" + ) + assertEquals("@@sentry.io/pages/10", urlDetails.url) + assertEquals("query=a", urlDetails.query) + assertEquals("fragment?q=1&s=2&token=secret", urlDetails.fragment) + } + + @Test + fun `extracts details from relative url with leading question mark`() { + val urlDetails = UrlUtils.parse( + "?query=a#fragment?q=1&s=2&token=secret" + ) + assertEquals("", urlDetails.url) + assertEquals("query=a", urlDetails.query) + assertEquals("fragment?q=1&s=2&token=secret", urlDetails.fragment) + } + + @Test + fun `no details extracted from malformed url due to # symbol in fragment`() { + val urlDetails = UrlUtils.parse( + "https://example.com#hello#fragment" + ) assertNull(urlDetails.url) assertNull(urlDetails.query) assertNull(urlDetails.fragment) } @Test - fun `no details extracted from malformed url`() { + fun `no details extracted from malformed url due to invalid protocol`() { val urlDetails = UrlUtils.parse( "htps://user@sentry.io#fragment?q=1&s=2&token=secret" ) @@ -190,4 +259,92 @@ class UrlUtilsTest { assertNull(urlDetails.query) assertNull(urlDetails.fragment) } + + @Test + fun `does not filter email address in path`() { + val urlDetails = UrlUtils.parseNullable( + "https://staging.server.com/api/v4/auth/password/reset/email@example.com" + )!! + assertEquals("https://staging.server.com/api/v4/auth/password/reset/email@example.com", urlDetails.url) + assertNull(urlDetails.query) + assertNull(urlDetails.fragment) + } + + @Test + fun `does not filter email address in path with fragment`() { + val urlDetails = UrlUtils.parseNullable( + "https://staging.server.com/api/v4/auth/password/reset/email@example.com#top" + )!! + assertEquals("https://staging.server.com/api/v4/auth/password/reset/email@example.com", urlDetails.url) + assertNull(urlDetails.query) + assertEquals("top", urlDetails.fragment) + } + + @Test + fun `does not filter email address in path with query and fragment`() { + val urlDetails = UrlUtils.parseNullable( + "https://staging.server.com/api/v4/auth/password/reset/email@example.com?a=b&c=d#top" + )!! + assertEquals("https://staging.server.com/api/v4/auth/password/reset/email@example.com", urlDetails.url) + assertEquals("a=b&c=d", urlDetails.query) + assertEquals("top", urlDetails.fragment) + } + + @Test + fun `does not filter email address in query`() { + val urlDetails = UrlUtils.parseNullable( + "https://staging.server.com/?email=someone@example.com" + )!! + assertEquals("https://staging.server.com/", urlDetails.url) + assertEquals("email=someone@example.com", urlDetails.query) + } + + @Test + fun `does not filter email address in fragment`() { + val urlDetails = UrlUtils.parseNullable( + "https://staging.server.com#email=someone@example.com" + )!! + assertEquals("https://staging.server.com", urlDetails.url) + assertEquals("email=someone@example.com", urlDetails.fragment) + } + + @Test + fun `does not filter email address in fragment with query`() { + val urlDetails = UrlUtils.parseNullable( + "https://staging.server.com?q=a&b=c#email=someone@example.com" + )!! + assertEquals("https://staging.server.com", urlDetails.url) + assertEquals("q=a&b=c", urlDetails.query) + assertEquals("email=someone@example.com", urlDetails.fragment) + } + + @Test + fun `extracts details from relative url with email in path`() { + val urlDetails = UrlUtils.parse( + "/emails/user@sentry.io?query=a&b=c#fragment?q=1&s=2&token=secret" + ) + assertEquals("/emails/user@sentry.io", urlDetails.url) + assertEquals("query=a&b=c", urlDetails.query) + assertEquals("fragment?q=1&s=2&token=secret", urlDetails.fragment) + } + + @Test + fun `extracts details from relative url with email in query`() { + val urlDetails = UrlUtils.parse( + "users/10?email=user@sentry.io&b=c#fragment?q=1&s=2&token=secret" + ) + assertEquals("users/10", urlDetails.url) + assertEquals("email=user@sentry.io&b=c", urlDetails.query) + assertEquals("fragment?q=1&s=2&token=secret", urlDetails.fragment) + } + + @Test + fun `extracts details from relative url with email in fragment`() { + val urlDetails = UrlUtils.parse( + "users/10?email=user@sentry.io&b=c#fragment?q=1&s=2&email=user@sentry.io" + ) + assertEquals("users/10", urlDetails.url) + assertEquals("email=user@sentry.io&b=c", urlDetails.query) + assertEquals("fragment?q=1&s=2&email=user@sentry.io", urlDetails.fragment) + } } From 8e27f4cb288199e8d7d170776bdfe615f135f672 Mon Sep 17 00:00:00 2001 From: lcian Date: Tue, 25 Feb 2025 15:49:01 +0100 Subject: [PATCH 2/7] reorganize tests --- .../test/java/io/sentry/util/UrlUtilsTest.kt | 72 ++++++------------- 1 file changed, 22 insertions(+), 50 deletions(-) diff --git a/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt b/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt index fc4de81a107..b62da8b0c9b 100644 --- a/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt @@ -1,6 +1,5 @@ package io.sentry.util -import java.net.URL import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull @@ -12,32 +11,6 @@ class UrlUtilsTest { assertNull(UrlUtils.parseNullable(null)) } - @Test - fun `i null for null`() { - val url = URL("https://www.example.com/search?q=hello%20world%21") - var uri = url.toURI() - } - - @Test - fun `filters auth`() { - val urlDetails = UrlUtils.parseNullable( - "https://user:password@sentry.io?q=1&s=2&token=secret" - )!! - assertEquals("https://[Filtered]:[Filtered]@sentry.io", urlDetails.url) - assertEquals("q=1&s=2&token=secret", urlDetails.query) - assertNull(urlDetails.fragment) - } - - @Test - fun `filters auth with fragment`() { - val urlDetails = UrlUtils.parseNullable( - "https://user:password@sentry.io?q=1&s=2&token=secret#top" - )!! - assertEquals("https://[Filtered]:[Filtered]@sentry.io", urlDetails.url) - assertEquals("q=1&s=2&token=secret", urlDetails.query) - assertEquals("top", urlDetails.fragment) - } - @Test fun `strips user info with user and password from http nullable`() { val urlDetails = UrlUtils.parseNullable( @@ -169,7 +142,7 @@ class UrlUtilsTest { } @Test - fun `splits url without query or fragment and no authority`() { + fun `splits url without query or fragment and no user info`() { val urlDetails = UrlUtils.parse( "https://sentry.io" ) @@ -189,7 +162,6 @@ class UrlUtilsTest { } // Fragment is allowed to contain '?' according to RFC 3986 - // See https://datatracker.ietf.org/doc/html/rfc3986#section-3.5 @Test fun `extracts details with question mark after fragment`() { val urlDetails = UrlUtils.parse( @@ -211,7 +183,27 @@ class UrlUtilsTest { } @Test - fun `filters empty user info`() { + fun `no details extracted from malformed url due to invalid protocol`() { + val urlDetails = UrlUtils.parse( + "htps://user@sentry.io#fragment?q=1&s=2&token=secret" + ) + assertNull(urlDetails.url) + assertNull(urlDetails.query) + assertNull(urlDetails.fragment) + } + + @Test + fun `no details extracted from malformed url due to # symbol in fragment`() { + val urlDetails = UrlUtils.parse( + "https://example.com#hello#fragment" + ) + assertNull(urlDetails.url) + assertNull(urlDetails.query) + assertNull(urlDetails.fragment) + } + + @Test + fun `strips empty user info`() { val urlDetails = UrlUtils.parse( "https://@sentry.io?query=a#fragment?q=1&s=2&token=secret" ) @@ -240,26 +232,6 @@ class UrlUtilsTest { assertEquals("fragment?q=1&s=2&token=secret", urlDetails.fragment) } - @Test - fun `no details extracted from malformed url due to # symbol in fragment`() { - val urlDetails = UrlUtils.parse( - "https://example.com#hello#fragment" - ) - assertNull(urlDetails.url) - assertNull(urlDetails.query) - assertNull(urlDetails.fragment) - } - - @Test - fun `no details extracted from malformed url due to invalid protocol`() { - val urlDetails = UrlUtils.parse( - "htps://user@sentry.io#fragment?q=1&s=2&token=secret" - ) - assertNull(urlDetails.url) - assertNull(urlDetails.query) - assertNull(urlDetails.fragment) - } - @Test fun `does not filter email address in path`() { val urlDetails = UrlUtils.parseNullable( From 726252b2a6796c625456305eeec7b9557cf0ad6e Mon Sep 17 00:00:00 2001 From: lcian Date: Tue, 25 Feb 2025 16:14:58 +0100 Subject: [PATCH 3/7] add tests --- .../test/java/io/sentry/util/UrlUtilsTest.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt b/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt index b62da8b0c9b..ca7475ef836 100644 --- a/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt @@ -319,4 +319,24 @@ class UrlUtilsTest { assertEquals("email=user@sentry.io&b=c", urlDetails.query) assertEquals("fragment?q=1&s=2&email=user@sentry.io", urlDetails.fragment) } + + @Test + fun `extracts path from file url`() { + val urlDetails = UrlUtils.parse( + "file:///users/sentry/text.txt" + ) + assertEquals("file:///users/sentry/text.txt", urlDetails.url) + assertNull(urlDetails.query) + assertNull(urlDetails.fragment) + } + + @Test + fun `does not extract details from websockets url`() { + val urlDetails = UrlUtils.parse( + "wss://example.com/socket" + ) + assertNull(urlDetails.url) + assertNull(urlDetails.query) + assertNull(urlDetails.fragment) + } } From a426e670e57c15e6f61d7878d0f8057f3c1b7775 Mon Sep 17 00:00:00 2001 From: lcian Date: Tue, 25 Feb 2025 16:31:07 +0100 Subject: [PATCH 4/7] tests --- sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt b/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt index ca7475ef836..ea407126544 100644 --- a/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/UrlUtilsTest.kt @@ -331,7 +331,7 @@ class UrlUtilsTest { } @Test - fun `does not extract details from websockets url`() { + fun `does not extract details from websockets uri`() { val urlDetails = UrlUtils.parse( "wss://example.com/socket" ) From c54965bfe8227921ab134fafe8aea6a896b943e8 Mon Sep 17 00:00:00 2001 From: lcian Date: Tue, 25 Feb 2025 16:43:17 +0100 Subject: [PATCH 5/7] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9babf5adab..89898991b9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ - The class `io.sentry.spring.jakarta.webflux.ReactorUtils` is now deprecated, please use `io.sentry.reactor.SentryReactorUtils` in the new `sentry-reactor` module instead ([#4155](https://github.com/getsentry/sentry-java/pull/4155)) - The new module will be exposed as an `api` dependency when using `sentry-spring-boot-jakarta` (Spring Boot 3) or `sentry-spring-jakarta` (Spring 6). Therefore, if you're using one of those modules, changing your imports will suffice. +- Use `java.net.URI` for parsing URLs in `UrlUtils` ([#4210](https://github.com/getsentry/sentry-java/pull/4210)) + - This could affect grouping for issues with messages containing URLs that fall in known corner cases that were handled incorrectly previously (e.g. email in URL path) ## 8.2.0 From 7c4b00f1b76b24336128665e7be7fc9b3eeb7aa9 Mon Sep 17 00:00:00 2001 From: Lorenzo Cian Date: Thu, 6 Mar 2025 13:33:35 +0100 Subject: [PATCH 6/7] Update sentry/src/main/java/io/sentry/util/UrlUtils.java Co-authored-by: Alexander Dinauer --- sentry/src/main/java/io/sentry/util/UrlUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry/src/main/java/io/sentry/util/UrlUtils.java b/sentry/src/main/java/io/sentry/util/UrlUtils.java index 93397577e8d..6c70cea0495 100644 --- a/sentry/src/main/java/io/sentry/util/UrlUtils.java +++ b/sentry/src/main/java/io/sentry/util/UrlUtils.java @@ -55,7 +55,7 @@ private static boolean isValidAbsoluteUrl(final @NotNull URI uri) { if (url.startsWith("@")) { return SENSITIVE_DATA_SUBSTITUTE + url; } - final @NotNull String userInfo = url.substring(0, url.indexOf('@') - 1); + final @NotNull String userInfo = url.substring(0, url.indexOf('@')); final @NotNull String filteredUserInfo = userInfo.contains(":") ? (SENSITIVE_DATA_SUBSTITUTE + ":" + SENSITIVE_DATA_SUBSTITUTE) From 9ec3b1b3a9522a976840c91d684030fead631ad0 Mon Sep 17 00:00:00 2001 From: Lorenzo Cian Date: Thu, 6 Mar 2025 13:35:03 +0100 Subject: [PATCH 7/7] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d002e000222..1b1508b4b7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ - The SDK now automatically propagates the trace-context to the native layer. This allows to connect errors on different layers of the application. ([#4137](https://github.com/getsentry/sentry-java/pull/4137)) +### Behavioural Changes + +- Use `java.net.URI` for parsing URLs in `UrlUtils` ([#4210](https://github.com/getsentry/sentry-java/pull/4210)) + - This could affect grouping for issues with messages containing URLs that fall in known corner cases that were handled incorrectly previously (e.g. email in URL path) + ### Dependencies - Bump Native SDK from v0.7.20 to v0.8.1 ([#4137](https://github.com/getsentry/sentry-java/pull/4137)) @@ -48,8 +53,6 @@ - The class `io.sentry.spring.jakarta.webflux.ReactorUtils` is now deprecated, please use `io.sentry.reactor.SentryReactorUtils` in the new `sentry-reactor` module instead ([#4155](https://github.com/getsentry/sentry-java/pull/4155)) - The new module will be exposed as an `api` dependency when using `sentry-spring-boot-jakarta` (Spring Boot 3) or `sentry-spring-jakarta` (Spring 6). Therefore, if you're using one of those modules, changing your imports will suffice. -- Use `java.net.URI` for parsing URLs in `UrlUtils` ([#4210](https://github.com/getsentry/sentry-java/pull/4210)) - - This could affect grouping for issues with messages containing URLs that fall in known corner cases that were handled incorrectly previously (e.g. email in URL path) ## 8.2.0