Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## Unreleased

### Features

- Prevent cross-organization trace continuation ([#5136](https://github.com/getsentry/sentry-java/pull/5136))
- By default, the SDK now extracts the organization ID from the DSN (e.g. `o123.ingest.sentry.io`) and compares it with the `sentry-org_id` value in incoming baggage headers. When the two differ, the SDK starts a fresh trace instead of continuing the foreign one. This guards against accidentally linking traces across organizations.
- New option `enableStrictTraceContinuation` (default `false`): when enabled, both the SDK's org ID **and** the incoming baggage org ID must be present and match for a trace to be continued. Traces with a missing org ID on either side are rejected. Configurable via code (`setStrictTraceContinuation(true)`), `sentry.properties` (`enable-strict-trace-continuation=true`), Android manifest (`io.sentry.strict-trace-continuation`), or Spring Boot (`sentry.strict-trace-continuation=true`).
- New option `orgId`: allows explicitly setting the organization ID for self-hosted and Relay setups where it cannot be extracted from the DSN. Configurable via code (`setOrgId("123")`), `sentry.properties` (`org-id=123`), Android manifest (`io.sentry.org-id`), or Spring Boot (`sentry.org-id=123`).

## 8.34.1

### Fixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ final class ManifestMetadataReader {

static final String FEEDBACK_SHOW_BRANDING = "io.sentry.feedback.show-branding";

static final String STRICT_TRACE_CONTINUATION = "io.sentry.strict-trace-continuation";
static final String ORG_ID = "io.sentry.org-id";

static final String SPOTLIGHT_ENABLE = "io.sentry.spotlight.enable";

static final String SPOTLIGHT_CONNECTION_URL = "io.sentry.spotlight.url";
Expand Down Expand Up @@ -662,6 +665,15 @@ static void applyMetadata(
feedbackOptions.setShowBranding(
readBool(metadata, logger, FEEDBACK_SHOW_BRANDING, feedbackOptions.isShowBranding()));

options.setStrictTraceContinuation(
readBool(
metadata, logger, STRICT_TRACE_CONTINUATION, options.isStrictTraceContinuation()));

final @Nullable String orgId = readString(metadata, logger, ORG_ID, null);
if (orgId != null) {
options.setOrgId(orgId);
}

options.setEnableSpotlight(
readBool(metadata, logger, SPOTLIGHT_ENABLE, options.isEnableSpotlight()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2436,4 +2436,54 @@ class ManifestMetadataReaderTest {
// maskAllImages should also add WebView
assertTrue(fixture.options.screenshot.maskViewClasses.contains("android.webkit.WebView"))
}

@Test
fun `applyMetadata reads strictTraceContinuation and keeps default value if not found`() {
// Arrange
val context = fixture.getContext()

// Act
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)

// Assert
assertFalse(fixture.options.isStrictTraceContinuation)
}

@Test
fun `applyMetadata reads strictTraceContinuation to options`() {
// Arrange
val bundle = bundleOf(ManifestMetadataReader.STRICT_TRACE_CONTINUATION to true)
val context = fixture.getContext(metaData = bundle)

// Act
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)

// Assert
assertTrue(fixture.options.isStrictTraceContinuation)
}

@Test
fun `applyMetadata reads orgId and keeps null if not found`() {
// Arrange
val context = fixture.getContext()

// Act
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)

// Assert
assertNull(fixture.options.orgId)
}

@Test
fun `applyMetadata reads orgId to options`() {
// Arrange
val bundle = bundleOf(ManifestMetadataReader.ORG_ID to "12345")
val context = fixture.getContext(metaData = bundle)

// Act
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)

// Assert
assertEquals("12345", fixture.options.orgId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.sentry.SentryEvent;
import io.sentry.SentryLevel;
import io.sentry.SentryLongDate;
import io.sentry.SentryOptions;
import io.sentry.SentryTraceHeader;
import io.sentry.SpanId;
import io.sentry.TracesSamplingDecision;
Expand Down Expand Up @@ -94,9 +95,16 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri
}
}

final @NotNull PropagationContext propagationContext =
new PropagationContext(
new SentryId(traceId), sentrySpanId, sentryParentSpanId, baggage, sampled);
final @NotNull SentryOptions sentryOptions = scopes.getOptions();
final @NotNull PropagationContext propagationContext;
if (sentryTraceHeader != null) {
propagationContext =
PropagationContext.fromHeaders(sentryTraceHeader, baggage, sentrySpanId, sentryOptions);
} else {
propagationContext =
new PropagationContext(
new SentryId(traceId), sentrySpanId, sentryParentSpanId, baggage, sampled);
}

baggage = propagationContext.getBaggage();
baggage.setValuesFromSamplingDecision(samplingDecision);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ public SamplingResult shouldSample(
final @NotNull PropagationContext propagationContext =
sentryTraceHeader == null
? new PropagationContext(new SentryId(traceId), randomSpanId, null, baggage, null)
: PropagationContext.fromHeaders(sentryTraceHeader, baggage, randomSpanId);
: PropagationContext.fromHeaders(
sentryTraceHeader, baggage, randomSpanId, scopes.getOptions());

final @NotNull TransactionContext transactionContext =
TransactionContext.fromPropagationContext(propagationContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,10 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri
new SentryId(traceData.getTraceId()), spanId, null, null, null)
: TransactionContext.fromPropagationContext(
PropagationContext.fromHeaders(
traceData.getSentryTraceHeader(), traceData.getBaggage(), spanId));
traceData.getSentryTraceHeader(),
traceData.getBaggage(),
spanId,
scopes.getOptions()));
;
transactionContext.setName(transactionName);
transactionContext.setTransactionNameSource(transactionNameSource);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class SentryTracingFilterTest {
logger,
it.arguments[0] as String?,
it.arguments[1] as List<String>?,
null,
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class SentryWebFluxTracingFilterTest {
logger,
it.arguments[0] as String?,
it.arguments[1] as List<String>?,
null,
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ class SentryAutoConfigurationTest {
"sentry.cron.default-failure-issue-threshold=40",
"sentry.cron.default-recovery-threshold=50",
"sentry.logs.enabled=true",
"sentry.strict-trace-continuation=true",
"sentry.org-id=12345",
)
.run {
val options = it.getBean(SentryProperties::class.java)
Expand Down Expand Up @@ -296,6 +298,8 @@ class SentryAutoConfigurationTest {
assertThat(options.cron!!.defaultFailureIssueThreshold).isEqualTo(40L)
assertThat(options.cron!!.defaultRecoveryThreshold).isEqualTo(50L)
assertThat(options.logs.isEnabled).isEqualTo(true)
assertThat(options.isStrictTraceContinuation).isEqualTo(true)
assertThat(options.orgId).isEqualTo("12345")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ class SentryAutoConfigurationTest {
"sentry.profile-session-sample-rate=1.0",
"sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces",
"sentry.profile-lifecycle=TRACE",
"sentry.strict-trace-continuation=true",
"sentry.org-id=12345",
)
.run {
val options = it.getBean(SentryProperties::class.java)
Expand Down Expand Up @@ -307,6 +309,8 @@ class SentryAutoConfigurationTest {
assertThat(options.profilingTracesDirPath)
.startsWith(File("tmp/sentry/profiling-traces").absolutePath)
assertThat(options.profileLifecycle).isEqualTo(ProfileLifecycle.TRACE)
assertThat(options.isStrictTraceContinuation).isEqualTo(true)
assertThat(options.orgId).isEqualTo("12345")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ class SentryAutoConfigurationTest {
"sentry.profile-session-sample-rate=1.0",
"sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces",
"sentry.profile-lifecycle=TRACE",
"sentry.strict-trace-continuation=true",
"sentry.org-id=12345",
)
.run {
val options = it.getBean(SentryProperties::class.java)
Expand Down Expand Up @@ -305,6 +307,8 @@ class SentryAutoConfigurationTest {
assertThat(options.profilingTracesDirPath)
.startsWith(File("tmp/sentry/profiling-traces").absolutePath)
assertThat(options.profileLifecycle).isEqualTo(ProfileLifecycle.TRACE)
assertThat(options.isStrictTraceContinuation).isEqualTo(true)
assertThat(options.orgId).isEqualTo("12345")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class SentryTracingFilterTest {
logger,
it.arguments[0] as String?,
it.arguments[1] as List<String>?,
null,
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class SentryWebFluxTracingFilterTest {
logger,
it.arguments[0] as String?,
it.arguments[1] as List<String>?,
null,
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class SentryTracingFilterTest {
logger,
it.arguments[0] as String?,
it.arguments[1] as List<String>?,
null,
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class SentryWebFluxTracingFilterTest {
logger,
it.arguments[0] as String?,
it.arguments[1] as List<String>?,
null,
)
)
}
Expand Down
18 changes: 15 additions & 3 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public final class io/sentry/Baggage {
public static fun fromHeader (Ljava/util/List;ZLio/sentry/ILogger;)Lio/sentry/Baggage;
public fun get (Ljava/lang/String;)Ljava/lang/String;
public fun getEnvironment ()Ljava/lang/String;
public fun getOrgId ()Ljava/lang/String;
public fun getPublicKey ()Ljava/lang/String;
public fun getRelease ()Ljava/lang/String;
public fun getReplayId ()Ljava/lang/String;
Expand All @@ -62,6 +63,7 @@ public final class io/sentry/Baggage {
public fun isShouldFreeze ()Z
public fun set (Ljava/lang/String;Ljava/lang/String;)V
public fun setEnvironment (Ljava/lang/String;)V
public fun setOrgId (Ljava/lang/String;)V
public fun setPublicKey (Ljava/lang/String;)V
public fun setRelease (Ljava/lang/String;)V
public fun setReplayId (Ljava/lang/String;)V
Expand All @@ -81,6 +83,7 @@ public final class io/sentry/Baggage {
public final class io/sentry/Baggage$DSCKeys {
public static final field ALL Ljava/util/List;
public static final field ENVIRONMENT Ljava/lang/String;
public static final field ORG_ID Ljava/lang/String;
public static final field PUBLIC_KEY Ljava/lang/String;
public static final field RELEASE Ljava/lang/String;
public static final field REPLAY_ID Ljava/lang/String;
Expand Down Expand Up @@ -501,6 +504,7 @@ public final class io/sentry/ExternalOptions {
public fun getInAppExcludes ()Ljava/util/List;
public fun getInAppIncludes ()Ljava/util/List;
public fun getMaxRequestBodySize ()Lio/sentry/SentryOptions$RequestSize;
public fun getOrgId ()Ljava/lang/String;
public fun getPrintUncaughtStackTrace ()Ljava/lang/Boolean;
public fun getProfileLifecycle ()Lio/sentry/ProfileLifecycle;
public fun getProfileSessionSampleRate ()Ljava/lang/Double;
Expand Down Expand Up @@ -530,6 +534,7 @@ public final class io/sentry/ExternalOptions {
public fun isGlobalHubMode ()Ljava/lang/Boolean;
public fun isSendDefaultPii ()Ljava/lang/Boolean;
public fun isSendModules ()Ljava/lang/Boolean;
public fun isStrictTraceContinuation ()Ljava/lang/Boolean;
public fun setCaptureOpenTelemetryEvents (Ljava/lang/Boolean;)V
public fun setCron (Lio/sentry/SentryOptions$Cron;)V
public fun setDebug (Ljava/lang/Boolean;)V
Expand All @@ -552,6 +557,7 @@ public final class io/sentry/ExternalOptions {
public fun setIgnoredErrors (Ljava/util/List;)V
public fun setIgnoredTransactions (Ljava/util/List;)V
public fun setMaxRequestBodySize (Lio/sentry/SentryOptions$RequestSize;)V
public fun setOrgId (Ljava/lang/String;)V
public fun setPrintUncaughtStackTrace (Ljava/lang/Boolean;)V
public fun setProfileLifecycle (Lio/sentry/ProfileLifecycle;)V
public fun setProfileSessionSampleRate (Ljava/lang/Double;)V
Expand All @@ -568,6 +574,7 @@ public final class io/sentry/ExternalOptions {
public fun setSessionFlushTimeoutMillis (Ljava/lang/Long;)V
public fun setShutdownTimeoutMillis (Ljava/lang/Long;)V
public fun setSpotlightConnectionUrl (Ljava/lang/String;)V
public fun setStrictTraceContinuation (Ljava/lang/Boolean;)V
public fun setTag (Ljava/lang/String;Ljava/lang/String;)V
public fun setTracesSampleRate (Ljava/lang/Double;)V
}
Expand Down Expand Up @@ -2269,9 +2276,9 @@ public final class io/sentry/PropagationContext {
public fun <init> (Lio/sentry/PropagationContext;)V
public fun <init> (Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Lio/sentry/SpanId;Lio/sentry/Baggage;Ljava/lang/Boolean;)V
public static fun fromExistingTrace (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Double;)Lio/sentry/PropagationContext;
public static fun fromHeaders (Lio/sentry/ILogger;Ljava/lang/String;Ljava/lang/String;)Lio/sentry/PropagationContext;
public static fun fromHeaders (Lio/sentry/ILogger;Ljava/lang/String;Ljava/util/List;)Lio/sentry/PropagationContext;
public static fun fromHeaders (Lio/sentry/SentryTraceHeader;Lio/sentry/Baggage;Lio/sentry/SpanId;)Lio/sentry/PropagationContext;
public static fun fromHeaders (Lio/sentry/ILogger;Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryOptions;)Lio/sentry/PropagationContext;
public static fun fromHeaders (Lio/sentry/ILogger;Ljava/lang/String;Ljava/util/List;Lio/sentry/SentryOptions;)Lio/sentry/PropagationContext;
public static fun fromHeaders (Lio/sentry/SentryTraceHeader;Lio/sentry/Baggage;Lio/sentry/SpanId;Lio/sentry/SentryOptions;)Lio/sentry/PropagationContext;
public fun getBaggage ()Lio/sentry/Baggage;
public fun getParentSpanId ()Lio/sentry/SpanId;
public fun getSampleRand ()Ljava/lang/Double;
Expand Down Expand Up @@ -3573,6 +3580,7 @@ public class io/sentry/SentryOptions {
public fun getDistribution ()Lio/sentry/SentryOptions$DistributionOptions;
public fun getDistributionController ()Lio/sentry/IDistributionApi;
public fun getDsn ()Ljava/lang/String;
public fun getEffectiveOrgId ()Ljava/lang/String;
public fun getEnvelopeDiskCache ()Lio/sentry/cache/IEnvelopeCache;
public fun getEnvelopeReader ()Lio/sentry/IEnvelopeReader;
public fun getEnvironment ()Ljava/lang/String;
Expand Down Expand Up @@ -3613,6 +3621,7 @@ public class io/sentry/SentryOptions {
public fun getOnOversizedEvent ()Lio/sentry/SentryOptions$OnOversizedEventCallback;
public fun getOpenTelemetryMode ()Lio/sentry/SentryOpenTelemetryMode;
public fun getOptionsObservers ()Ljava/util/List;
public fun getOrgId ()Ljava/lang/String;
public fun getOutboxPath ()Ljava/lang/String;
public fun getPerformanceCollectors ()Ljava/util/List;
public fun getProfileLifecycle ()Lio/sentry/ProfileLifecycle;
Expand Down Expand Up @@ -3683,6 +3692,7 @@ public class io/sentry/SentryOptions {
public fun isSendDefaultPii ()Z
public fun isSendModules ()Z
public fun isStartProfilerOnAppStart ()Z
public fun isStrictTraceContinuation ()Z
public fun isTraceOptionsRequests ()Z
public fun isTraceSampling ()Z
public fun isTracingEnabled ()Z
Expand Down Expand Up @@ -3766,6 +3776,7 @@ public class io/sentry/SentryOptions {
public fun setOnDiscard (Lio/sentry/SentryOptions$OnDiscardCallback;)V
public fun setOnOversizedEvent (Lio/sentry/SentryOptions$OnOversizedEventCallback;)V
public fun setOpenTelemetryMode (Lio/sentry/SentryOpenTelemetryMode;)V
public fun setOrgId (Ljava/lang/String;)V
public fun setPrintUncaughtStackTrace (Z)V
public fun setProfileLifecycle (Lio/sentry/ProfileLifecycle;)V
public fun setProfileSessionSampleRate (Ljava/lang/Double;)V
Expand Down Expand Up @@ -3797,6 +3808,7 @@ public class io/sentry/SentryOptions {
public fun setSpotlightConnectionUrl (Ljava/lang/String;)V
public fun setSslSocketFactory (Ljavax/net/ssl/SSLSocketFactory;)V
public fun setStartProfilerOnAppStart (Z)V
public fun setStrictTraceContinuation (Z)V
public fun setTag (Ljava/lang/String;Ljava/lang/String;)V
public fun setThreadChecker (Lio/sentry/util/thread/IThreadChecker;)V
public fun setTraceOptionsRequests (Z)V
Expand Down
Loading