Skip to content

Commit b7fde00

Browse files
adinauerclaudegetsentry-bot
authored
fix: Trim DSN string before URI parsing (#5113)
* fix: Trim DSN string before URI parsing Trailing or leading whitespace in the DSN string (commonly introduced by copy-paste) causes a URISyntaxException that crashes the application on startup. Trim the DSN before passing it to the URI constructor. Fixes GH-5087 Co-Authored-By: Claude <noreply@anthropic.com> * docs: Add changelog entry for DSN trimming fix Co-Authored-By: Claude <noreply@anthropic.com> * also trim on SentryOptions.setDsn * fix: Throw clear error when DSN is empty Previously an empty or whitespace-only DSN string would fall through to the URI constructor, producing a confusing error message. Now the Dsn constructor checks for empty strings after trimming and throws an IllegalArgumentException with a clear message. Co-Authored-By: Claude <noreply@anthropic.com> * Format code * ci: retrigger checks --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Sentry Github Bot <bot+github-bot@sentry.io>
1 parent fc52bb8 commit b7fde00

File tree

5 files changed

+43
-3
lines changed

5 files changed

+43
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
### Fixes
3232

3333
- Fix crash when unregistering `SystemEventsBroadcastReceiver` with try-catch block. ([#5106](https://github.com/getsentry/sentry-java/pull/5106))
34+
- Trim DSN string before parsing to avoid `URISyntaxException` caused by trailing whitespace ([#5113](https://github.com/getsentry/sentry-java/pull/5113))
3435

3536
### Dependencies
3637

sentry/src/main/java/io/sentry/Dsn.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,11 @@ URI getSentryUri() {
5050

5151
Dsn(@Nullable String dsn) throws IllegalArgumentException {
5252
try {
53-
Objects.requireNonNull(dsn, "The DSN is required.");
54-
final URI uri = new URI(dsn).normalize();
53+
final String dsnString = Objects.requireNonNull(dsn, "The DSN is required.").trim();
54+
if (dsnString.isEmpty()) {
55+
throw new IllegalArgumentException("The DSN is empty.");
56+
}
57+
final URI uri = new URI(dsnString).normalize();
5558
final String scheme = uri.getScheme();
5659
if (!("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme))) {
5760
throw new IllegalArgumentException("Invalid DSN scheme: " + scheme);

sentry/src/main/java/io/sentry/SentryOptions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ Dsn retrieveParsedDsn() throws IllegalArgumentException {
749749
* @param dsn the DSN
750750
*/
751751
public void setDsn(final @Nullable String dsn) {
752-
this.dsn = dsn;
752+
this.dsn = dsn != null ? dsn.trim() : null;
753753
this.parsedDsn.resetValue();
754754

755755
dsnHash = StringUtils.calculateStringHash(this.dsn, logger);

sentry/src/test/java/io/sentry/DsnTest.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,24 @@ class DsnTest {
8989
assertEquals("http://host/api/id", dsn.sentryUri.toURL().toString())
9090
}
9191

92+
@Test
93+
fun `dsn parsed with leading and trailing whitespace`() {
94+
val dsn = Dsn(" https://key@host/id ")
95+
assertEquals("https://host/api/id", dsn.sentryUri.toURL().toString())
96+
}
97+
98+
@Test
99+
fun `when dsn is empty, throws exception`() {
100+
val ex = assertFailsWith<IllegalArgumentException> { Dsn("") }
101+
assertEquals("java.lang.IllegalArgumentException: The DSN is empty.", ex.message)
102+
}
103+
104+
@Test
105+
fun `when dsn is only whitespace, throws exception`() {
106+
val ex = assertFailsWith<IllegalArgumentException> { Dsn(" ") }
107+
assertEquals("java.lang.IllegalArgumentException: The DSN is empty.", ex.message)
108+
}
109+
92110
@Test
93111
fun `non http protocols are not accepted`() {
94112
assertFailsWith<IllegalArgumentException> { Dsn("ftp://publicKey:secretKey@host/path/id") }

sentry/src/test/java/io/sentry/SentryOptionsTest.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,24 @@ class SentryOptionsTest {
588588
assertFalse(cacheDirPathWithoutDsn.contains(hash.toString()))
589589
}
590590

591+
@Test
592+
fun `when setting dsn with whitespace, it is trimmed and produces the same cache dir path`() {
593+
val dsn = "http://key@localhost/proj"
594+
val options1 =
595+
SentryOptions().apply {
596+
setDsn(dsn)
597+
cacheDirPath = "${File.separator}test"
598+
}
599+
val options2 =
600+
SentryOptions().apply {
601+
setDsn(" $dsn ")
602+
cacheDirPath = "${File.separator}test"
603+
}
604+
605+
assertEquals(dsn, options2.dsn)
606+
assertEquals(options1.cacheDirPath, options2.cacheDirPath)
607+
}
608+
591609
@Test
592610
fun `when options are initialized, idleTimeout is 3000`() {
593611
assertEquals(3000L, SentryOptions().idleTimeout)

0 commit comments

Comments
 (0)