From e1538ab85ebe95840f3a47107f58267a83e1a79a Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 16 Jun 2026 16:55:02 +0200 Subject: [PATCH 1/9] use the same method to get url in all spring filters --- .../spring7/tracing/SentrySpanClientWebRequestFilter.java | 5 ++++- .../io/sentry/spring7/mvc/SentrySpringIntegrationTest.kt | 7 +++++-- .../jakarta/tracing/SentrySpanClientWebRequestFilter.java | 5 ++++- .../spring/jakarta/mvc/SentrySpringIntegrationTest.kt | 7 +++++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientWebRequestFilter.java b/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientWebRequestFilter.java index 6726302a83e..2061eb3c74b 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientWebRequestFilter.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientWebRequestFilter.java @@ -15,6 +15,7 @@ import io.sentry.util.Objects; import io.sentry.util.SpanUtils; import io.sentry.util.TracingUtils; +import io.sentry.util.UrlUtils; import java.util.Locale; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -45,9 +46,11 @@ public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) { final @NotNull SpanOptions spanOptions = new SpanOptions(); spanOptions.setOrigin(TRACE_ORIGIN); final ISpan span = activeSpan.startChild("http.client", null, spanOptions); + final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(request.url().toString()); final @NotNull String method = request.method().name(); - span.setDescription(method + " " + request.url()); + span.setDescription(method + " " + urlDetails.getUrlOrFallback()); span.setData(SpanDataConvention.HTTP_METHOD_KEY, method.toUpperCase(Locale.ROOT)); + urlDetails.applyToSpan(span); final @NotNull ClientRequest modifiedRequest = maybeAddTracingHeaders(request, span); diff --git a/sentry-spring-7/src/test/kotlin/io/sentry/spring7/mvc/SentrySpringIntegrationTest.kt b/sentry-spring-7/src/test/kotlin/io/sentry/spring7/mvc/SentrySpringIntegrationTest.kt index 4b7e806730f..7486b348670 100644 --- a/sentry-spring-7/src/test/kotlin/io/sentry/spring7/mvc/SentrySpringIntegrationTest.kt +++ b/sentry-spring-7/src/test/kotlin/io/sentry/spring7/mvc/SentrySpringIntegrationTest.kt @@ -376,7 +376,8 @@ class SentrySpringIntegrationTest { assertThat(transaction.spans).hasSize(1) val span = transaction.spans.first() assertThat(span.op).isEqualTo("http.client") - assertThat(span.description).isEqualTo("GET http://localhost:$port/hello") + assertThat(span.description) + .isEqualTo("GET http://[Filtered]:[Filtered]@localhost:$port/hello") assertThat(span.data?.get(SpanDataConvention.HTTP_STATUS_CODE_KEY)).isEqualTo(200) assertThat(span.status).isEqualTo(SpanStatus.OK) }, @@ -519,7 +520,9 @@ class HelloController(private val webClient: WebClient, private val env: Environ fun webClient(): String? { return webClient .get() - .uri("http://localhost:${env.getProperty("local.server.port")}/hello") + .uri( + "http://user:password@localhost:${env.getProperty("local.server.port")}/hello" + ) .retrieve() .bodyToMono(String::class.java) .block() diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java index 1189532c0c4..66b92af4cbc 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java @@ -15,6 +15,7 @@ import io.sentry.util.Objects; import io.sentry.util.SpanUtils; import io.sentry.util.TracingUtils; +import io.sentry.util.UrlUtils; import java.util.Locale; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -45,9 +46,11 @@ public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) { final @NotNull SpanOptions spanOptions = new SpanOptions(); spanOptions.setOrigin(TRACE_ORIGIN); final ISpan span = activeSpan.startChild("http.client", null, spanOptions); + final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(request.url().toString()); final @NotNull String method = request.method().name(); - span.setDescription(method + " " + request.url()); + span.setDescription(method + " " + urlDetails.getUrlOrFallback()); span.setData(SpanDataConvention.HTTP_METHOD_KEY, method.toUpperCase(Locale.ROOT)); + urlDetails.applyToSpan(span); final @NotNull ClientRequest modifiedRequest = maybeAddTracingHeaders(request, span); diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt index 6d399323f5f..4d9c436712c 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt @@ -374,7 +374,8 @@ class SentrySpringIntegrationTest { assertThat(transaction.spans).hasSize(1) val span = transaction.spans.first() assertThat(span.op).isEqualTo("http.client") - assertThat(span.description).isEqualTo("GET http://localhost:$port/hello") + assertThat(span.description) + .isEqualTo("GET http://[Filtered]:[Filtered]@localhost:$port/hello") assertThat(span.data?.get(SpanDataConvention.HTTP_STATUS_CODE_KEY)).isEqualTo(200) assertThat(span.status).isEqualTo(SpanStatus.OK) }, @@ -517,7 +518,9 @@ class HelloController(private val webClient: WebClient, private val env: Environ fun webClient(): String? { return webClient .get() - .uri("http://localhost:${env.getProperty("local.server.port")}/hello") + .uri( + "http://user:password@localhost:${env.getProperty("local.server.port")}/hello" + ) .retrieve() .bodyToMono(String::class.java) .block() From 8e8c73da303873779f0d77f33367c0c697f6d7fd Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 16 Jun 2026 16:55:54 +0200 Subject: [PATCH 2/9] remove unnecessary system out message in DatabaseUtils --- sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java b/sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java index 6879723ebf9..4583dc9e6c1 100644 --- a/sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java +++ b/sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java @@ -131,7 +131,6 @@ public static DatabaseDetails parse(final @Nullable String databaseConnectionUrl String pathWithoutProperties = StringUtils.substringBefore(path, ";"); return new DatabaseDetails(dbSystem, pathWithoutProperties); } catch (Throwable t) { - System.out.println(t.getMessage()); // ignore } return new DatabaseDetails(dbSystem, null); From f9a6c50e593c0fca309a28f048e62aea9f823164 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 16 Jun 2026 16:58:26 +0200 Subject: [PATCH 3/9] spotless --- .../io/sentry/spring7/mvc/SentrySpringIntegrationTest.kt | 4 +--- .../sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/sentry-spring-7/src/test/kotlin/io/sentry/spring7/mvc/SentrySpringIntegrationTest.kt b/sentry-spring-7/src/test/kotlin/io/sentry/spring7/mvc/SentrySpringIntegrationTest.kt index 7486b348670..36e6af289fd 100644 --- a/sentry-spring-7/src/test/kotlin/io/sentry/spring7/mvc/SentrySpringIntegrationTest.kt +++ b/sentry-spring-7/src/test/kotlin/io/sentry/spring7/mvc/SentrySpringIntegrationTest.kt @@ -520,9 +520,7 @@ class HelloController(private val webClient: WebClient, private val env: Environ fun webClient(): String? { return webClient .get() - .uri( - "http://user:password@localhost:${env.getProperty("local.server.port")}/hello" - ) + .uri("http://user:password@localhost:${env.getProperty("local.server.port")}/hello") .retrieve() .bodyToMono(String::class.java) .block() diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt index 4d9c436712c..f1837a08c1f 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt @@ -518,9 +518,7 @@ class HelloController(private val webClient: WebClient, private val env: Environ fun webClient(): String? { return webClient .get() - .uri( - "http://user:password@localhost:${env.getProperty("local.server.port")}/hello" - ) + .uri("http://user:password@localhost:${env.getProperty("local.server.port")}/hello") .retrieve() .bodyToMono(String::class.java) .block() From 0c476a37a0fae4e7b01e4eb04ce46ed58bd6fe11 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 16 Jun 2026 16:59:42 +0200 Subject: [PATCH 4/9] extract target url to match against tracePropagationTargets --- .../build.gradle.kts | 2 +- .../otlp/OpenTelemetryOtlpPropagator.java | 58 ++++++++++++ .../test/kotlin/OtelSentryPropagatorTest.kt | 92 +++++++++++++++++++ 3 files changed, 151 insertions(+), 1 deletion(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-otlp/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-otlp/build.gradle.kts index f039b3c95ef..adeb594b57f 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-otlp/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-otlp/build.gradle.kts @@ -21,7 +21,7 @@ dependencies { api(libs.otel.extension.autoconfigure) api(libs.otel.exporter.otlp) compileOnly(libs.otel.extension.autoconfigure.spi) - // compileOnly(libs.otel.semconv) + implementation(libs.otel.semconv) // compileOnly(libs.otel.semconv.incubating) compileOnly(libs.jetbrains.annotations) diff --git a/sentry-opentelemetry/sentry-opentelemetry-otlp/src/main/java/io/sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java b/sentry-opentelemetry/sentry-opentelemetry-otlp/src/main/java/io/sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java index a4249b27ec0..3904f20f47d 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-otlp/src/main/java/io/sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java +++ b/sentry-opentelemetry/sentry-opentelemetry-otlp/src/main/java/io/sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java @@ -2,6 +2,7 @@ import static io.sentry.SentryTraceHeader.SENTRY_TRACE_HEADER; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.TraceFlags; @@ -11,14 +12,20 @@ import io.opentelemetry.context.propagation.TextMapGetter; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.context.propagation.TextMapSetter; +import io.opentelemetry.sdk.trace.ReadableSpan; +import io.opentelemetry.semconv.ServerAttributes; +import io.opentelemetry.semconv.UrlAttributes; import io.sentry.Baggage; import io.sentry.BaggageHeader; import io.sentry.IScopes; import io.sentry.ScopesAdapter; import io.sentry.SentryLevel; +import io.sentry.SentryOptions; import io.sentry.SentryTraceHeader; import io.sentry.exception.InvalidSentryTraceHeaderException; +import io.sentry.util.PropagationTargetsUtils; import io.sentry.util.TracingUtils; +import java.net.URL; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -61,6 +68,10 @@ public void inject(final Context context, final C carrier, final TextMapSett return; } + if (!shouldInjectTracingHeaders(otelSpan)) { + return; + } + setter.set( carrier, SENTRY_TRACE_HEADER, @@ -76,6 +87,53 @@ public void inject(final Context context, final C carrier, final TextMapSett } } + private boolean shouldInjectTracingHeaders(final @NotNull Span otelSpan) { + final @NotNull SentryOptions options = scopes.getOptions(); + final @Nullable String url = extractUrl(otelSpan, options); + + if (url != null) { + return PropagationTargetsUtils.contain(options.getTracePropagationTargets(), url); + } + + return options.getTracePropagationTargets().contains(SentryOptions.DEFAULT_PROPAGATION_TARGETS); + } + + private @Nullable String extractUrl( + final @NotNull Span otelSpan, final @NotNull SentryOptions options) { + if (!(otelSpan instanceof ReadableSpan)) { + return null; + } + + final @NotNull Attributes attributes = ((ReadableSpan) otelSpan).getAttributes(); + final @Nullable String urlFull = attributes.get(UrlAttributes.URL_FULL); + if (urlFull != null) { + return urlFull; + } + + final @Nullable String scheme = attributes.get(UrlAttributes.URL_SCHEME); + final @Nullable String serverAddress = attributes.get(ServerAttributes.SERVER_ADDRESS); + final @Nullable Long serverPort = attributes.get(ServerAttributes.SERVER_PORT); + final @Nullable String path = attributes.get(UrlAttributes.URL_PATH); + + if (scheme == null || serverAddress == null) { + return null; + } + + try { + final @NotNull String pathToUse = path == null ? "" : path; + if (serverPort == null) { + return new URL(scheme, serverAddress, pathToUse).toString(); + } else { + return new URL(scheme, serverAddress, serverPort.intValue(), pathToUse).toString(); + } + } catch (Throwable t) { + options + .getLogger() + .log(SentryLevel.WARNING, "Unable to combine URL span attributes into one.", t); + return null; + } + } + @Override public Context extract( final Context context, final C carrier, final TextMapGetter getter) { diff --git a/sentry-opentelemetry/sentry-opentelemetry-otlp/src/test/kotlin/OtelSentryPropagatorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-otlp/src/test/kotlin/OtelSentryPropagatorTest.kt index 1d5d56c5bff..5e82ae748b4 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-otlp/src/test/kotlin/OtelSentryPropagatorTest.kt +++ b/sentry-opentelemetry/sentry-opentelemetry-otlp/src/test/kotlin/OtelSentryPropagatorTest.kt @@ -7,6 +7,7 @@ import io.opentelemetry.api.trace.TraceState import io.opentelemetry.context.Context import io.opentelemetry.context.propagation.TextMapGetter import io.opentelemetry.context.propagation.TextMapSetter +import io.opentelemetry.sdk.trace.SdkTracerProvider import io.sentry.Baggage import io.sentry.Sentry import kotlin.test.AfterTest @@ -171,6 +172,97 @@ class OpenTelemetryOtlpPropagatorTest { ) } + @Test + fun `injects headers if URL in span attributes matches tracePropagationTargets`() { + Sentry.init { options -> + options.dsn = "https://key@sentry.io/proj" + options.setTracePropagationTargets(listOf("sentry.io")) + } + val propagator = OpenTelemetryOtlpPropagator() + val carrier = mutableMapOf() + val tracerProvider = SdkTracerProvider.builder().build() + val otelSpan = + tracerProvider + .get("test") + .spanBuilder("test") + .setAttribute("url.full", "https://sentry.io/api/0/") + .startSpan() + val baggage = + Baggage.fromHeader( + "sentry-environment=production,sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rand=0.456789,sentry-sample_rate=0.5,sentry-sampled=true,sentry-trace_id=df71f5972f754b4c85af13ff5c07017d" + ) + + try { + val context = + Context.root().with(otelSpan).with(OpenTelemetryOtlpPropagator.SENTRY_BAGGAGE_KEY, baggage) + + propagator.inject(context, carrier, MapSetter()) + } finally { + otelSpan.end() + tracerProvider.shutdown() + } + + assertEquals( + "${otelSpan.spanContext.traceId}-${otelSpan.spanContext.spanId}-1", + carrier["sentry-trace"], + ) + assertEquals( + "sentry-environment=production,sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rand=0.456789,sentry-sample_rate=0.5,sentry-sampled=true,sentry-trace_id=df71f5972f754b4c85af13ff5c07017d", + carrier["baggage"], + ) + } + + @Test + fun `does not inject headers if URL in span attributes does not match tracePropagationTargets`() { + Sentry.init { options -> + options.dsn = "https://key@sentry.io/proj" + options.setTracePropagationTargets(listOf("github.com")) + } + val propagator = OpenTelemetryOtlpPropagator() + val carrier = mutableMapOf() + val tracerProvider = SdkTracerProvider.builder().build() + val otelSpan = + tracerProvider + .get("test") + .spanBuilder("test") + .setAttribute("url.full", "https://sentry.io/api/0/") + .startSpan() + + try { + propagator.inject(Context.root().with(otelSpan), carrier, MapSetter()) + } finally { + otelSpan.end() + tracerProvider.shutdown() + } + + assertNull(carrier["sentry-trace"]) + assertNull(carrier["baggage"]) + } + + @Test + fun `does not inject headers if tracePropagationTargets is restricted and URL is unavailable`() { + Sentry.init { options -> + options.dsn = "https://key@sentry.io/proj" + options.setTracePropagationTargets(listOf("sentry.io")) + } + val propagator = OpenTelemetryOtlpPropagator() + val carrier = mutableMapOf() + + val otelSpanContext = + SpanContext.create( + "f9118105af4a2d42b4124532cd1065ff", + "424cffc8f94feeee", + TraceFlags.getSampled(), + TraceState.getDefault(), + ) + val otelSpan = Span.wrap(otelSpanContext) + + propagator.inject(Context.root().with(otelSpan), carrier, MapSetter()) + + assertNull(carrier["sentry-trace"]) + assertNull(carrier["baggage"]) + } + @Test fun `does not inject headers when no span in context`() { val propagator = OpenTelemetryOtlpPropagator() From 34ba3d305f26b6b0b1a7cee180727c5e27cce1c2 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Mon, 6 Jul 2026 15:13:09 +0200 Subject: [PATCH 5/9] inject headers if url cannot be extracted to be in line with other integrations --- .../opentelemetry/otlp/OpenTelemetryOtlpPropagator.java | 6 +----- .../src/test/kotlin/OtelSentryPropagatorTest.kt | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-otlp/src/main/java/io/sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java b/sentry-opentelemetry/sentry-opentelemetry-otlp/src/main/java/io/sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java index 3904f20f47d..506b93eae00 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-otlp/src/main/java/io/sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java +++ b/sentry-opentelemetry/sentry-opentelemetry-otlp/src/main/java/io/sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java @@ -91,11 +91,7 @@ private boolean shouldInjectTracingHeaders(final @NotNull Span otelSpan) { final @NotNull SentryOptions options = scopes.getOptions(); final @Nullable String url = extractUrl(otelSpan, options); - if (url != null) { - return PropagationTargetsUtils.contain(options.getTracePropagationTargets(), url); - } - - return options.getTracePropagationTargets().contains(SentryOptions.DEFAULT_PROPAGATION_TARGETS); + return url == null || PropagationTargetsUtils.contain(options.getTracePropagationTargets(), url); } private @Nullable String extractUrl( diff --git a/sentry-opentelemetry/sentry-opentelemetry-otlp/src/test/kotlin/OtelSentryPropagatorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-otlp/src/test/kotlin/OtelSentryPropagatorTest.kt index 5e82ae748b4..afa728c7ff0 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-otlp/src/test/kotlin/OtelSentryPropagatorTest.kt +++ b/sentry-opentelemetry/sentry-opentelemetry-otlp/src/test/kotlin/OtelSentryPropagatorTest.kt @@ -240,7 +240,7 @@ class OpenTelemetryOtlpPropagatorTest { } @Test - fun `does not inject headers if tracePropagationTargets is restricted and URL is unavailable`() { + fun `injects headers if tracePropagationTargets is restricted and URL is unavailable`() { Sentry.init { options -> options.dsn = "https://key@sentry.io/proj" options.setTracePropagationTargets(listOf("sentry.io")) @@ -259,7 +259,7 @@ class OpenTelemetryOtlpPropagatorTest { propagator.inject(Context.root().with(otelSpan), carrier, MapSetter()) - assertNull(carrier["sentry-trace"]) + assertEquals("f9118105af4a2d42b4124532cd1065ff-424cffc8f94feeee-1", carrier["sentry-trace"]) assertNull(carrier["baggage"]) } From 90ca1d8dabd4ef9498790509c537321f11394021 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Mon, 6 Jul 2026 13:15:08 +0000 Subject: [PATCH 6/9] Format code --- .../sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-otlp/src/main/java/io/sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java b/sentry-opentelemetry/sentry-opentelemetry-otlp/src/main/java/io/sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java index 506b93eae00..4cdf1b0ed09 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-otlp/src/main/java/io/sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java +++ b/sentry-opentelemetry/sentry-opentelemetry-otlp/src/main/java/io/sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java @@ -91,7 +91,8 @@ private boolean shouldInjectTracingHeaders(final @NotNull Span otelSpan) { final @NotNull SentryOptions options = scopes.getOptions(); final @Nullable String url = extractUrl(otelSpan, options); - return url == null || PropagationTargetsUtils.contain(options.getTracePropagationTargets(), url); + return url == null + || PropagationTargetsUtils.contain(options.getTracePropagationTargets(), url); } private @Nullable String extractUrl( From e307aeb837441730e40f6f83c6dd3f2950d8a205 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Mon, 6 Jul 2026 15:36:06 +0200 Subject: [PATCH 7/9] verify host matches requestingHost before returning authentication --- .../io/sentry/transport/HttpConnection.java | 6 +- .../sentry/transport/ProxyAuthenticator.java | 15 +++-- .../io/sentry/transport/HttpConnectionTest.kt | 11 ++++ .../transport/ProxyAuthenticatorTest.kt | 62 +++++++++++++++++++ 4 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 sentry/src/test/java/io/sentry/transport/ProxyAuthenticatorTest.kt diff --git a/sentry/src/main/java/io/sentry/transport/HttpConnection.java b/sentry/src/main/java/io/sentry/transport/HttpConnection.java index 71c3ebb15b2..bebb7d2372a 100644 --- a/sentry/src/main/java/io/sentry/transport/HttpConnection.java +++ b/sentry/src/main/java/io/sentry/transport/HttpConnection.java @@ -64,9 +64,9 @@ public HttpConnection( if (proxy != null && options.getProxy() != null) { final String proxyUser = options.getProxy().getUser(); final String proxyPassword = options.getProxy().getPass(); - - if (proxyUser != null && proxyPassword != null) { - authenticatorWrapper.setDefault(new ProxyAuthenticator(proxyUser, proxyPassword)); + final String proxyHost = options.getProxy().getHost(); + if (proxyUser != null && proxyPassword != null && proxyHost != null) { + authenticatorWrapper.setDefault(new ProxyAuthenticator(proxyUser, proxyPassword, proxyHost)); } } } diff --git a/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java b/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java index 7aad9967f18..78871f7641f 100644 --- a/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java +++ b/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java @@ -1,5 +1,7 @@ package io.sentry.transport; +import io.sentry.ScopesAdapter; +import io.sentry.SentryOptions; import io.sentry.util.Objects; import java.net.Authenticator; import java.net.PasswordAuthentication; @@ -9,6 +11,7 @@ final class ProxyAuthenticator extends Authenticator { private final @NotNull String user; private final @NotNull String password; + private final @NotNull String proxyHost; /** * Proxy authenticator. @@ -16,18 +19,20 @@ final class ProxyAuthenticator extends Authenticator { * @param user proxy username * @param password proxy password */ - ProxyAuthenticator(final @NotNull String user, final @NotNull String password) { + ProxyAuthenticator(final @NotNull String user, final @NotNull String password, final @NotNull String proxyHost) { this.user = Objects.requireNonNull(user, "user is required"); this.password = Objects.requireNonNull(password, "password is required"); + this.proxyHost = Objects.requireNonNull(proxyHost, "proxyHost is required"); } @Override protected @Nullable PasswordAuthentication getPasswordAuthentication() { - if (getRequestorType() == RequestorType.PROXY) { - return new PasswordAuthentication(user, password.toCharArray()); + if (getRequestorType() == RequestorType.PROXY + && proxyHost.equals(getRequestingHost())) { + return new PasswordAuthentication(user, password.toCharArray()); + } + return null; } - return null; - } @NotNull String getUser() { diff --git a/sentry/src/test/java/io/sentry/transport/HttpConnectionTest.kt b/sentry/src/test/java/io/sentry/transport/HttpConnectionTest.kt index 2a856ab5800..4c17c71fecc 100644 --- a/sentry/src/test/java/io/sentry/transport/HttpConnectionTest.kt +++ b/sentry/src/test/java/io/sentry/transport/HttpConnectionTest.kt @@ -259,6 +259,17 @@ class HttpConnectionTest { assertEquals(Type.SOCKS, transport.proxy!!.type()) } + @Test + fun `When Proxy username and password are given but host is missing, authenticator is not set`() { + fixture.proxy = Proxy(null, "8090", "some-user", "some-password") + val transport = fixture.getSUT() + + transport.send(createEnvelope()) + + assertNull(transport.proxy) + verify(fixture.authenticatorWrapper, never()).setDefault(any()) + } + @Test fun `sets common headers and on http connection`() { val transport = fixture.getSUT() diff --git a/sentry/src/test/java/io/sentry/transport/ProxyAuthenticatorTest.kt b/sentry/src/test/java/io/sentry/transport/ProxyAuthenticatorTest.kt new file mode 100644 index 00000000000..c3af7da226d --- /dev/null +++ b/sentry/src/test/java/io/sentry/transport/ProxyAuthenticatorTest.kt @@ -0,0 +1,62 @@ +package io.sentry.transport + +import java.net.Authenticator +import java.net.Authenticator.RequestorType +import java.net.PasswordAuthentication +import java.net.URL +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +class ProxyAuthenticatorTest { + @BeforeTest + @AfterTest + fun reset() { + Authenticator.setDefault(null) + } + + @Test + fun `returns authentication when proxy request host matches proxy host`() { + Authenticator.setDefault(ProxyAuthenticator("some-user", "some-password", "proxy.example.com")) + + val authentication = requestPasswordAuthentication("proxy.example.com", RequestorType.PROXY) + + assertEquals("some-user", authentication!!.userName) + assertEquals("some-password", String(authentication.password)) + } + + @Test + fun `returns null when requestor type is not proxy`() { + Authenticator.setDefault(ProxyAuthenticator("some-user", "some-password", "proxy.example.com")) + + val authentication = requestPasswordAuthentication("proxy.example.com", RequestorType.SERVER) + + assertNull(authentication) + } + + @Test + fun `returns null when proxy request host does not match proxy host`() { + Authenticator.setDefault(ProxyAuthenticator("some-user", "some-password", "proxy.example.com")) + + val authentication = requestPasswordAuthentication("other.example.com", RequestorType.PROXY) + + assertNull(authentication) + } + + private fun requestPasswordAuthentication( + host: String, + requestorType: RequestorType, + ): PasswordAuthentication? = + Authenticator.requestPasswordAuthentication( + host, + null, + 8080, + "https", + "prompt", + "basic", + URL("https://sentry.io"), + requestorType, + ) +} From 4440a64ae4e592bc061d5942921b1aa7cb065c3b Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Mon, 6 Jul 2026 15:38:38 +0200 Subject: [PATCH 8/9] format --- .../java/io/sentry/transport/HttpConnection.java | 3 ++- .../io/sentry/transport/ProxyAuthenticator.java | 14 ++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/sentry/src/main/java/io/sentry/transport/HttpConnection.java b/sentry/src/main/java/io/sentry/transport/HttpConnection.java index bebb7d2372a..e7935e76cf3 100644 --- a/sentry/src/main/java/io/sentry/transport/HttpConnection.java +++ b/sentry/src/main/java/io/sentry/transport/HttpConnection.java @@ -66,7 +66,8 @@ public HttpConnection( final String proxyPassword = options.getProxy().getPass(); final String proxyHost = options.getProxy().getHost(); if (proxyUser != null && proxyPassword != null && proxyHost != null) { - authenticatorWrapper.setDefault(new ProxyAuthenticator(proxyUser, proxyPassword, proxyHost)); + authenticatorWrapper.setDefault( + new ProxyAuthenticator(proxyUser, proxyPassword, proxyHost)); } } } diff --git a/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java b/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java index 78871f7641f..74b63566b37 100644 --- a/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java +++ b/sentry/src/main/java/io/sentry/transport/ProxyAuthenticator.java @@ -1,7 +1,5 @@ package io.sentry.transport; -import io.sentry.ScopesAdapter; -import io.sentry.SentryOptions; import io.sentry.util.Objects; import java.net.Authenticator; import java.net.PasswordAuthentication; @@ -19,7 +17,8 @@ final class ProxyAuthenticator extends Authenticator { * @param user proxy username * @param password proxy password */ - ProxyAuthenticator(final @NotNull String user, final @NotNull String password, final @NotNull String proxyHost) { + ProxyAuthenticator( + final @NotNull String user, final @NotNull String password, final @NotNull String proxyHost) { this.user = Objects.requireNonNull(user, "user is required"); this.password = Objects.requireNonNull(password, "password is required"); this.proxyHost = Objects.requireNonNull(proxyHost, "proxyHost is required"); @@ -27,12 +26,11 @@ final class ProxyAuthenticator extends Authenticator { @Override protected @Nullable PasswordAuthentication getPasswordAuthentication() { - if (getRequestorType() == RequestorType.PROXY - && proxyHost.equals(getRequestingHost())) { - return new PasswordAuthentication(user, password.toCharArray()); - } - return null; + if (getRequestorType() == RequestorType.PROXY && proxyHost.equals(getRequestingHost())) { + return new PasswordAuthentication(user, password.toCharArray()); } + return null; + } @NotNull String getUser() { From 512dd0772a87de98982998f4df621f5ca04c5c43 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 7 Jul 2026 09:54:22 +0200 Subject: [PATCH 9/9] remove applyToSpan to not introduce unexpected behaviour in a minor release --- .../sentry/spring7/tracing/SentrySpanClientWebRequestFilter.java | 1 - .../spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java | 1 - 2 files changed, 2 deletions(-) diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientWebRequestFilter.java b/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientWebRequestFilter.java index 2061eb3c74b..942cda241b7 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientWebRequestFilter.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientWebRequestFilter.java @@ -50,7 +50,6 @@ public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) { final @NotNull String method = request.method().name(); span.setDescription(method + " " + urlDetails.getUrlOrFallback()); span.setData(SpanDataConvention.HTTP_METHOD_KEY, method.toUpperCase(Locale.ROOT)); - urlDetails.applyToSpan(span); final @NotNull ClientRequest modifiedRequest = maybeAddTracingHeaders(request, span); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java index 66b92af4cbc..ec29b9c68a3 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java @@ -50,7 +50,6 @@ public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) { final @NotNull String method = request.method().name(); span.setDescription(method + " " + urlDetails.getUrlOrFallback()); span.setData(SpanDataConvention.HTTP_METHOD_KEY, method.toUpperCase(Locale.ROOT)); - urlDetails.applyToSpan(span); final @NotNull ClientRequest modifiedRequest = maybeAddTracingHeaders(request, span);