diff --git a/src/Tracing/PropagationContext.php b/src/Tracing/PropagationContext.php index cb150fe64..e9d36c4c4 100644 --- a/src/Tracing/PropagationContext.php +++ b/src/Tracing/PropagationContext.php @@ -188,6 +188,33 @@ private static function parseTraceparentAndBaggage(string $traceparent, string $ { $context = self::fromDefaults(); $hasSentryTrace = false; + $samplingContext = DynamicSamplingContext::fromHeader($baggage); + + $client = SentrySdk::getCurrentHub()->getClient(); + if ($client !== null) { + $options = $client->getOptions(); + $orgId = $options->getOrgId() ?? $options->getDsn()->getOrgId(); + + // Always check if the received org ID in the baggages matches + // the one configured in the SDK. + if ( + $orgId !== null + && $samplingContext->has('org_id') + && $samplingContext->get('org_id') !== (string) $orgId + ) { + // We do not continue the trace. + return $context; + } + + if ($client->getOptions()->isStrictTracePropagationEnabled()) { + if (!$samplingContext->has('org_id')) { + // We did not receive any org id in the baggage, + // do not continue the trace. The none matching org ID + // case was already handled above. + return $context; + } + } + } if (preg_match(self::SENTRY_TRACEPARENT_HEADER_REGEX, $traceparent, $matches)) { if (!empty($matches['trace_id'])) { @@ -206,8 +233,6 @@ private static function parseTraceparentAndBaggage(string $traceparent, string $ } } - $samplingContext = DynamicSamplingContext::fromHeader($baggage); - if ($hasSentryTrace && !$samplingContext->hasEntries()) { // The request comes from an old SDK which does not support Dynamic Sampling. // Propagate the Dynamic Sampling Context as is, but frozen, even without sentry-* entries. diff --git a/src/Tracing/TransactionContext.php b/src/Tracing/TransactionContext.php index e7774a3f0..c6fd251c5 100644 --- a/src/Tracing/TransactionContext.php +++ b/src/Tracing/TransactionContext.php @@ -4,6 +4,8 @@ namespace Sentry\Tracing; +use Sentry\SentrySdk; + final class TransactionContext extends SpanContext { private const SENTRY_TRACEPARENT_HEADER_REGEX = '/^[ \\t]*(?[0-9a-f]{32})?-?(?[0-9a-f]{16})?-?(?[01])?[ \\t]*$/i'; @@ -148,6 +150,33 @@ private static function parseTraceAndBaggage(string $sentryTrace, string $baggag { $context = new self(); $hasSentryTrace = false; + $samplingContext = DynamicSamplingContext::fromHeader($baggage); + + $client = SentrySdk::getCurrentHub()->getClient(); + if ($client !== null) { + $options = $client->getOptions(); + $orgId = $options->getOrgId() ?? $options->getDsn()->getOrgId(); + + // Always check if the received org ID in the baggages matches + // the one configured in the SDK. + if ( + $orgId !== null + && $samplingContext->has('org_id') + && $samplingContext->get('org_id') !== (string) $orgId + ) { + // We do not continue the trace. + return $context; + } + + if ($client->getOptions()->isStrictTracePropagationEnabled()) { + if (!$samplingContext->has('org_id')) { + // We did not receive any org id in the baggage, + // do not continue the trace. The none matching org ID + // case was already handled above. + return $context; + } + } + } if (preg_match(self::SENTRY_TRACEPARENT_HEADER_REGEX, $sentryTrace, $matches)) { if (!empty($matches['trace_id'])) { @@ -166,8 +195,6 @@ private static function parseTraceAndBaggage(string $sentryTrace, string $baggag } } - $samplingContext = DynamicSamplingContext::fromHeader($baggage); - if ($hasSentryTrace && !$samplingContext->hasEntries()) { // The request comes from an old SDK which does not support Dynamic Sampling. // Propagate the Dynamic Sampling Context as is, but frozen, even without sentry-* entries. diff --git a/tests/FunctionsTest.php b/tests/FunctionsTest.php index 949f9fadd..64d4be834 100644 --- a/tests/FunctionsTest.php +++ b/tests/FunctionsTest.php @@ -514,13 +514,20 @@ public function testBaggageWithTracingEnabled(): void public function testContinueTrace(): void { - $hub = new Hub(); + $client = $this->createMock(ClientInterface::class); + $client->expects($this->atLeastOnce()) + ->method('getOptions') + ->willReturn(new Options([ + 'org_id' => 1, + ])); + + $hub = new Hub($client); SentrySdk::setCurrentHub($hub); $transactionContext = continueTrace( '566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8-1', - 'sentry-trace_id=566e3688a61d4bc888951642d6f14a19' + 'sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-org_id=1' ); $this->assertSame('566e3688a61d4bc888951642d6f14a19', (string) $transactionContext->getTraceId()); @@ -539,4 +546,36 @@ public function testContinueTrace(): void $this->assertTrue($dynamicSamplingContext->isFrozen()); }); } + + public function testContinueTraceWithNoneMatchingOrgId(): void + { + $client = $this->createMock(ClientInterface::class); + $client->expects($this->atLeastOnce()) + ->method('getOptions') + ->willReturn(new Options([ + 'org_id' => 1, + ])); + + $hub = new Hub($client); + + SentrySdk::setCurrentHub($hub); + + $transactionContext = continueTrace( + '566e3688a61d4bc888951642d6f14a19-566e3688a61d4bc8-1', + 'sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-org_id=2' + ); + + $this->assertNull($transactionContext->getTraceId()); + $this->assertNull($transactionContext->getParentSpanId()); + $this->assertNull($transactionContext->getParentSampled()); + + configureScope(function (Scope $scope): void { + $propagationContext = $scope->getPropagationContext(); + + $this->assertNotSame('566e3688a61d4bc888951642d6f14a19', (string) $propagationContext->getTraceId()); + $this->assertNotSame('566e3688a61d4bc8', (string) $propagationContext->getParentSpanId()); + + $this->assertNull($propagationContext->getDynamicSamplingContext()); + }); + } }