diff --git a/CHANGELOG.md b/CHANGELOG.md index 4082a81567..cc43ec0e83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixes - Fixed ArgumentNullException in FormRequestPayloadExtractor when handling invalid form data on ASP.NET ([#3734](https://github.com/getsentry/sentry-dotnet/pull/3734)) +- Fixed NullReferenceException in SentryTraceHeader when parsing null or empty values ([#3745](https://github.com/getsentry/sentry-dotnet/pull/3745)) ### Dependencies diff --git a/src/Sentry/SentryTraceHeader.cs b/src/Sentry/SentryTraceHeader.cs index 212deb99bd..ed4d35c372 100644 --- a/src/Sentry/SentryTraceHeader.cs +++ b/src/Sentry/SentryTraceHeader.cs @@ -40,10 +40,25 @@ public override string ToString() => IsSampled is { } isSampled : $"{TraceId}-{SpanId}"; /// - /// Parses from string. + /// Parses a from a string representation of the Sentry trace header. /// - public static SentryTraceHeader Parse(string value) + /// + /// A string containing the Sentry trace header, expected to follow the format "traceId-spanId-sampled", + /// where "sampled" is optional. + /// + /// + /// A object if parsing succeeds, or null if the input string is null, empty, or whitespace. + /// + /// + /// Thrown if the input string does not contain a valid trace header format, specifically if it lacks required trace ID and span ID components. + /// + public static SentryTraceHeader? Parse(string value) { + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + var components = value.Split('-', StringSplitOptions.RemoveEmptyEntries); if (components.Length < 2) { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 952c5ab12e..1c60b3fa26 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -931,7 +931,7 @@ namespace Sentry public Sentry.SpanId SpanId { get; } public Sentry.SentryId TraceId { get; } public override string ToString() { } - public static Sentry.SentryTraceHeader Parse(string value) { } + public static Sentry.SentryTraceHeader? Parse(string value) { } } public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 952c5ab12e..1c60b3fa26 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -931,7 +931,7 @@ namespace Sentry public Sentry.SpanId SpanId { get; } public Sentry.SentryId TraceId { get; } public override string ToString() { } - public static Sentry.SentryTraceHeader Parse(string value) { } + public static Sentry.SentryTraceHeader? Parse(string value) { } } public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 003fc8ed9a..6cfd7b9964 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -933,7 +933,7 @@ namespace Sentry public Sentry.SpanId SpanId { get; } public Sentry.SentryId TraceId { get; } public override string ToString() { } - public static Sentry.SentryTraceHeader Parse(string value) { } + public static Sentry.SentryTraceHeader? Parse(string value) { } } public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 5caab10797..5b4ba7b231 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -928,7 +928,7 @@ namespace Sentry public Sentry.SpanId SpanId { get; } public Sentry.SentryId TraceId { get; } public override string ToString() { } - public static Sentry.SentryTraceHeader Parse(string value) { } + public static Sentry.SentryTraceHeader? Parse(string value) { } } public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext { diff --git a/test/Sentry.Tests/Protocol/SentryTraceHeaderTests.cs b/test/Sentry.Tests/Protocol/SentryTraceHeaderTests.cs index 0c8ff348e1..11b46dc58c 100644 --- a/test/Sentry.Tests/Protocol/SentryTraceHeaderTests.cs +++ b/test/Sentry.Tests/Protocol/SentryTraceHeaderTests.cs @@ -46,4 +46,17 @@ public void Parse_WithSampledFalse_Works() header.SpanId.Should().Be(SpanId.Parse("1000000000000000")); header.IsSampled.Should().BeFalse(); } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public void Parse_WithoutHeaderValue_ReturnsNull(string headerValue) + { + // Act + var header = SentryTraceHeader.Parse(headerValue); + + // Assert + header.Should().BeNull(); + } }