diff --git a/CHANGELOG.md b/CHANGELOG.md index dba0f4dcdf..56a7be44b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ ### Fixes - Fix crash when unregistering `SystemEventsBroadcastReceiver` with try-catch block. ([#5106](https://github.com/getsentry/sentry-java/pull/5106)) +- Trim DSN string before parsing to avoid `URISyntaxException` caused by trailing whitespace ([#5113](https://github.com/getsentry/sentry-java/pull/5113)) ### Dependencies diff --git a/sentry/src/main/java/io/sentry/Dsn.java b/sentry/src/main/java/io/sentry/Dsn.java index 836e2c5546..705d383266 100644 --- a/sentry/src/main/java/io/sentry/Dsn.java +++ b/sentry/src/main/java/io/sentry/Dsn.java @@ -50,8 +50,11 @@ URI getSentryUri() { Dsn(@Nullable String dsn) throws IllegalArgumentException { try { - Objects.requireNonNull(dsn, "The DSN is required."); - final URI uri = new URI(dsn).normalize(); + final String dsnString = Objects.requireNonNull(dsn, "The DSN is required.").trim(); + if (dsnString.isEmpty()) { + throw new IllegalArgumentException("The DSN is empty."); + } + final URI uri = new URI(dsnString).normalize(); final String scheme = uri.getScheme(); if (!("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme))) { throw new IllegalArgumentException("Invalid DSN scheme: " + scheme); diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 14f4acf51f..298e37795b 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -749,7 +749,7 @@ Dsn retrieveParsedDsn() throws IllegalArgumentException { * @param dsn the DSN */ public void setDsn(final @Nullable String dsn) { - this.dsn = dsn; + this.dsn = dsn != null ? dsn.trim() : null; this.parsedDsn.resetValue(); dsnHash = StringUtils.calculateStringHash(this.dsn, logger); diff --git a/sentry/src/test/java/io/sentry/DsnTest.kt b/sentry/src/test/java/io/sentry/DsnTest.kt index eaa129c207..6c454ad5c7 100644 --- a/sentry/src/test/java/io/sentry/DsnTest.kt +++ b/sentry/src/test/java/io/sentry/DsnTest.kt @@ -89,6 +89,24 @@ class DsnTest { assertEquals("http://host/api/id", dsn.sentryUri.toURL().toString()) } + @Test + fun `dsn parsed with leading and trailing whitespace`() { + val dsn = Dsn(" https://key@host/id ") + assertEquals("https://host/api/id", dsn.sentryUri.toURL().toString()) + } + + @Test + fun `when dsn is empty, throws exception`() { + val ex = assertFailsWith { Dsn("") } + assertEquals("java.lang.IllegalArgumentException: The DSN is empty.", ex.message) + } + + @Test + fun `when dsn is only whitespace, throws exception`() { + val ex = assertFailsWith { Dsn(" ") } + assertEquals("java.lang.IllegalArgumentException: The DSN is empty.", ex.message) + } + @Test fun `non http protocols are not accepted`() { assertFailsWith { Dsn("ftp://publicKey:secretKey@host/path/id") } diff --git a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt index 6868b55c2b..80510db931 100644 --- a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt +++ b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt @@ -588,6 +588,24 @@ class SentryOptionsTest { assertFalse(cacheDirPathWithoutDsn.contains(hash.toString())) } + @Test + fun `when setting dsn with whitespace, it is trimmed and produces the same cache dir path`() { + val dsn = "http://key@localhost/proj" + val options1 = + SentryOptions().apply { + setDsn(dsn) + cacheDirPath = "${File.separator}test" + } + val options2 = + SentryOptions().apply { + setDsn(" $dsn ") + cacheDirPath = "${File.separator}test" + } + + assertEquals(dsn, options2.dsn) + assertEquals(options1.cacheDirPath, options2.cacheDirPath) + } + @Test fun `when options are initialized, idleTimeout is 3000`() { assertEquals(3000L, SentryOptions().idleTimeout)