Skip to content

Commit 327d897

Browse files
adinauerclaude
andcommitted
fix(opentelemetry): Enforce strict continuation in propagators
Apply strict trace continuation checks in all OpenTelemetry propagator extract paths before creating remote parent span context. When org-id validation fails, return the original context and ignore incoming sentry-trace and baggage to keep propagation behavior aligned with strict continuation requirements. Add rejection tests for OtelSentryPropagator, deprecated SentryPropagator, and OpenTelemetryOtlpPropagator. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 71562fa commit 327d897

File tree

8 files changed

+116
-10
lines changed

8 files changed

+116
-10
lines changed

sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentryPropagator.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ public <C> Context extract(
113113

114114
final @Nullable String baggageString = getter.get(carrier, BaggageHeader.BAGGAGE_HEADER);
115115
final Baggage baggage = Baggage.fromHeader(baggageString);
116+
if (!TracingUtils.shouldContinueTrace(scopes.getOptions(), baggage)) {
117+
scopes
118+
.getOptions()
119+
.getLogger()
120+
.log(
121+
SentryLevel.DEBUG, "Not continuing trace due to strict org ID validation failure.");
122+
return context;
123+
}
116124
final @NotNull TraceState traceState = TraceState.getDefault();
117125

118126
SpanContext otelSpanContext =

sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import io.sentry.SentryLevel;
1717
import io.sentry.SentryTraceHeader;
1818
import io.sentry.exception.InvalidSentryTraceHeaderException;
19+
import io.sentry.util.TracingUtils;
1920
import java.util.Arrays;
2021
import java.util.Collection;
2122
import java.util.Collections;
@@ -98,6 +99,17 @@ public <C> Context extract(
9899
try {
99100
SentryTraceHeader sentryTraceHeader = new SentryTraceHeader(sentryTraceString);
100101

102+
final @Nullable String baggageString = getter.get(carrier, BaggageHeader.BAGGAGE_HEADER);
103+
Baggage baggage = Baggage.fromHeader(baggageString);
104+
if (!TracingUtils.shouldContinueTrace(scopes.getOptions(), baggage)) {
105+
scopes
106+
.getOptions()
107+
.getLogger()
108+
.log(
109+
SentryLevel.DEBUG, "Not continuing trace due to strict org ID validation failure.");
110+
return context;
111+
}
112+
101113
SpanContext otelSpanContext =
102114
SpanContext.createFromRemoteParent(
103115
sentryTraceHeader.getTraceId().toString(),
@@ -107,9 +119,6 @@ public <C> Context extract(
107119

108120
@NotNull
109121
Context modifiedContext = context.with(SentryOtelKeys.SENTRY_TRACE_KEY, sentryTraceHeader);
110-
111-
final @Nullable String baggageString = getter.get(carrier, BaggageHeader.BAGGAGE_HEADER);
112-
Baggage baggage = Baggage.fromHeader(baggageString);
113122
modifiedContext = modifiedContext.with(SentryOtelKeys.SENTRY_BAGGAGE_KEY, baggage);
114123

115124
Span wrappedSpan = Span.wrap(otelSpanContext);

sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/OtelSentryPropagatorTest.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import kotlin.test.AfterTest
1919
import kotlin.test.BeforeTest
2020
import kotlin.test.Test
2121
import kotlin.test.assertEquals
22+
import kotlin.test.assertFalse
2223
import kotlin.test.assertNotNull
2324
import kotlin.test.assertNull
2425
import kotlin.test.assertSame
@@ -69,6 +70,26 @@ class OtelSentryPropagatorTest {
6970
assertSame(scopeInContext, scopes)
7071
}
7172

73+
@Test
74+
fun `ignores incoming headers when strict continuation rejects org id`() {
75+
Sentry.init { options ->
76+
options.dsn = "https://key@o2.ingest.sentry.io/123"
77+
options.isStrictTraceContinuation = true
78+
}
79+
val propagator = OtelSentryPropagator()
80+
val carrier: Map<String, String> =
81+
mapOf(
82+
"sentry-trace" to "f9118105af4a2d42b4124532cd1065ff-424cffc8f94feeee-1",
83+
"baggage" to "sentry-trace_id=f9118105af4a2d42b4124532cd1065ff,sentry-org_id=1",
84+
)
85+
86+
val newContext = propagator.extract(Context.root(), carrier, MapGetter())
87+
88+
assertFalse(Span.fromContext(newContext).spanContext.isValid)
89+
assertNull(newContext.get(SENTRY_TRACE_KEY))
90+
assertNull(newContext.get(SENTRY_BAGGAGE_KEY))
91+
}
92+
7293
@Test
7394
fun `uses incoming headers`() {
7495
val propagator = OtelSentryPropagator()
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package io.sentry.opentelemetry
2+
3+
import io.opentelemetry.api.trace.Span
4+
import io.opentelemetry.context.Context
5+
import io.sentry.Sentry
6+
import io.sentry.opentelemetry.SentryOtelKeys.SENTRY_BAGGAGE_KEY
7+
import io.sentry.opentelemetry.SentryOtelKeys.SENTRY_TRACE_KEY
8+
import kotlin.test.BeforeTest
9+
import kotlin.test.Test
10+
import kotlin.test.assertFalse
11+
import kotlin.test.assertNull
12+
13+
class SentryPropagatorTest {
14+
15+
@BeforeTest
16+
fun setup() {
17+
Sentry.init("https://key@sentry.io/proj")
18+
}
19+
20+
@Suppress("DEPRECATION")
21+
@Test
22+
fun `ignores incoming headers when strict continuation rejects org id`() {
23+
Sentry.init { options ->
24+
options.dsn = "https://key@o2.ingest.sentry.io/123"
25+
options.isStrictTraceContinuation = true
26+
}
27+
28+
val propagator = SentryPropagator()
29+
val carrier: Map<String, String> =
30+
mapOf(
31+
"sentry-trace" to "f9118105af4a2d42b4124532cd1065ff-424cffc8f94feeee-1",
32+
"baggage" to "sentry-trace_id=f9118105af4a2d42b4124532cd1065ff,sentry-org_id=1",
33+
)
34+
35+
val newContext = propagator.extract(Context.root(), carrier, MapGetter())
36+
37+
assertFalse(Span.fromContext(newContext).spanContext.isValid)
38+
assertNull(newContext.get(SENTRY_TRACE_KEY))
39+
assertNull(newContext.get(SENTRY_BAGGAGE_KEY))
40+
}
41+
}

sentry-opentelemetry/sentry-opentelemetry-otlp/src/main/java/io/sentry/opentelemetry/otlp/OpenTelemetryOtlpPropagator.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import io.sentry.SentryLevel;
1919
import io.sentry.SentryTraceHeader;
2020
import io.sentry.exception.InvalidSentryTraceHeaderException;
21+
import io.sentry.util.TracingUtils;
2122
import java.util.Arrays;
2223
import java.util.Collection;
2324
import java.util.List;
@@ -87,6 +88,16 @@ public <C> Context extract(
8788
SentryTraceHeader sentryTraceHeader = new SentryTraceHeader(sentryTraceString);
8889

8990
final @Nullable String baggageString = getter.get(carrier, BaggageHeader.BAGGAGE_HEADER);
91+
final @Nullable Baggage baggage =
92+
baggageString == null ? null : Baggage.fromHeader(baggageString);
93+
if (!TracingUtils.shouldContinueTrace(scopes.getOptions(), baggage)) {
94+
scopes
95+
.getOptions()
96+
.getLogger()
97+
.log(
98+
SentryLevel.DEBUG, "Not continuing trace due to strict org ID validation failure.");
99+
return context;
100+
}
90101
final @NotNull TraceState traceState = TraceState.getDefault();
91102

92103
final @NotNull TraceFlags traceFlags =
@@ -104,9 +115,8 @@ public <C> Context extract(
104115
Span wrappedSpan = Span.wrap(otelSpanContext);
105116

106117
@NotNull Context modifiedContext = context.with(wrappedSpan);
107-
if (baggageString != null) {
108-
modifiedContext =
109-
modifiedContext.with(SENTRY_BAGGAGE_KEY, Baggage.fromHeader(baggageString));
118+
if (baggage != null) {
119+
modifiedContext = modifiedContext.with(SENTRY_BAGGAGE_KEY, baggage);
110120
}
111121

112122
scopes

sentry-opentelemetry/sentry-opentelemetry-otlp/src/test/kotlin/OtelSentryPropagatorTest.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,25 @@ class OpenTelemetryOtlpPropagatorTest {
4646
assertNull(baggage)
4747
}
4848

49+
@Test
50+
fun `ignores incoming headers when strict continuation rejects org id`() {
51+
Sentry.init { options ->
52+
options.dsn = "https://key@o2.ingest.sentry.io/123"
53+
options.isStrictTraceContinuation = true
54+
}
55+
val propagator = OpenTelemetryOtlpPropagator()
56+
val carrier: Map<String, String> =
57+
mapOf(
58+
"sentry-trace" to "f9118105af4a2d42b4124532cd1065ff-424cffc8f94feeee-1",
59+
"baggage" to "sentry-trace_id=f9118105af4a2d42b4124532cd1065ff,sentry-org_id=1",
60+
)
61+
62+
val newContext = propagator.extract(Context.root(), carrier, MapGetter())
63+
64+
assertFalse(Span.fromContext(newContext).spanContext.isValid)
65+
assertNull(newContext.get(OpenTelemetryOtlpPropagator.SENTRY_BAGGAGE_KEY))
66+
}
67+
4968
@Test
5069
fun `uses incoming headers`() {
5170
val propagator = OpenTelemetryOtlpPropagator()

sentry/api/sentry.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7786,6 +7786,7 @@ public final class io/sentry/util/TracingUtils {
77867786
public static fun isIgnored (Ljava/util/List;Ljava/lang/String;)Z
77877787
public static fun maybeUpdateBaggage (Lio/sentry/IScope;Lio/sentry/SentryOptions;)Lio/sentry/PropagationContext;
77887788
public static fun setTrace (Lio/sentry/IScopes;Lio/sentry/PropagationContext;)V
7789+
public static fun shouldContinueTrace (Lio/sentry/SentryOptions;Lio/sentry/Baggage;)Z
77897790
public static fun startNewTrace (Lio/sentry/IScopes;)V
77907791
public static fun trace (Lio/sentry/IScopes;Ljava/util/List;Lio/sentry/ISpan;)Lio/sentry/util/TracingUtils$TracingHeaders;
77917792
public static fun traceIfAllowed (Lio/sentry/IScopes;Ljava/lang/String;Ljava/util/List;Lio/sentry/ISpan;)Lio/sentry/util/TracingUtils$TracingHeaders;

sentry/src/main/java/io/sentry/PropagationContext.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,7 @@ public static PropagationContext fromHeaders(
4848
if (options != null && !TracingUtils.shouldContinueTrace(options, baggage)) {
4949
options
5050
.getLogger()
51-
.log(
52-
SentryLevel.DEBUG,
53-
"Not continuing trace due to strict org ID validation failure.");
51+
.log(SentryLevel.DEBUG, "Not continuing trace due to strict org ID validation failure.");
5452
return new PropagationContext();
5553
}
5654

@@ -162,5 +160,4 @@ public void setSampled(final @Nullable Boolean sampled) {
162160
// should never be null since we ensure it in ctor
163161
return sampleRand == null ? 0.0 : sampleRand;
164162
}
165-
166163
}

0 commit comments

Comments
 (0)