Skip to content

Commit ed36c19

Browse files
authored
Prevent NullReferenceException in SentryTraceHeader Parsing (#3757)
1 parent ec3e21f commit ed36c19

8 files changed

Lines changed: 39 additions & 7 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
- Added support for `.NET 9` (preview) ([#3699](https://github.com/getsentry/sentry-dotnet/pull/3699))
1818
- libsentrysupplemental.so now supports 16 KB page sizes on Android ([#3723](https://github.com/getsentry/sentry-dotnet/pull/3723))
1919

20+
### Fixes
21+
22+
- Fixed NullReferenceException in SentryTraceHeader when parsing null or empty values ([#3757](https://github.com/getsentry/sentry-dotnet/pull/3757))
23+
2024
## Unreleased
2125

2226
### Fixes

src/Sentry/SentryTraceHeader.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,25 @@ public override string ToString() => IsSampled is { } isSampled
4040
: $"{TraceId}-{SpanId}";
4141

4242
/// <summary>
43-
/// Parses <see cref="SentryTraceHeader"/> from string.
43+
/// Parses a <see cref="SentryTraceHeader"/> from a string representation of the Sentry trace header.
4444
/// </summary>
45-
public static SentryTraceHeader Parse(string value)
45+
/// <param name="value">
46+
/// A string containing the Sentry trace header, expected to follow the format "traceId-spanId-sampled",
47+
/// where "sampled" is optional.
48+
/// </param>
49+
/// <returns>
50+
/// A <see cref="SentryTraceHeader"/> object if parsing succeeds, or <c>null</c> if the input string is null, empty, or whitespace.
51+
/// </returns>
52+
/// <exception cref="FormatException">
53+
/// Thrown if the input string does not contain a valid trace header format, specifically if it lacks required trace ID and span ID components.
54+
/// </exception>
55+
public static SentryTraceHeader? Parse(string value)
4656
{
57+
if (string.IsNullOrWhiteSpace(value))
58+
{
59+
return null;
60+
}
61+
4762
var components = value.Split('-', StringSplitOptions.RemoveEmptyEntries);
4863
if (components.Length < 2)
4964
{

test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,7 @@ namespace Sentry
911911
public Sentry.SpanId SpanId { get; }
912912
public Sentry.SentryId TraceId { get; }
913913
public override string ToString() { }
914-
public static Sentry.SentryTraceHeader Parse(string value) { }
914+
public static Sentry.SentryTraceHeader? Parse(string value) { }
915915
}
916916
public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext
917917
{

test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,7 @@ namespace Sentry
911911
public Sentry.SpanId SpanId { get; }
912912
public Sentry.SentryId TraceId { get; }
913913
public override string ToString() { }
914-
public static Sentry.SentryTraceHeader Parse(string value) { }
914+
public static Sentry.SentryTraceHeader? Parse(string value) { }
915915
}
916916
public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext
917917
{

test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,7 @@ namespace Sentry
913913
public Sentry.SpanId SpanId { get; }
914914
public Sentry.SentryId TraceId { get; }
915915
public override string ToString() { }
916-
public static Sentry.SentryTraceHeader Parse(string value) { }
916+
public static Sentry.SentryTraceHeader? Parse(string value) { }
917917
}
918918
public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext
919919
{

test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,7 @@ namespace Sentry
913913
public Sentry.SpanId SpanId { get; }
914914
public Sentry.SentryId TraceId { get; }
915915
public override string ToString() { }
916-
public static Sentry.SentryTraceHeader Parse(string value) { }
916+
public static Sentry.SentryTraceHeader? Parse(string value) { }
917917
}
918918
public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext
919919
{

test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,7 @@ namespace Sentry
908908
public Sentry.SpanId SpanId { get; }
909909
public Sentry.SentryId TraceId { get; }
910910
public override string ToString() { }
911-
public static Sentry.SentryTraceHeader Parse(string value) { }
911+
public static Sentry.SentryTraceHeader? Parse(string value) { }
912912
}
913913
public class SentryTransaction : Sentry.IEventLike, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISentryJsonSerializable, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext
914914
{

test/Sentry.Tests/Protocol/SentryTraceHeaderTests.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,17 @@ public void Parse_WithSampledFalse_Works()
4646
header.SpanId.Should().Be(SpanId.Parse("1000000000000000"));
4747
header.IsSampled.Should().BeFalse();
4848
}
49+
50+
[Theory]
51+
[InlineData(null)]
52+
[InlineData("")]
53+
[InlineData(" ")]
54+
public void Parse_WithoutHeaderValue_ReturnsNull(string headerValue)
55+
{
56+
// Act
57+
var header = SentryTraceHeader.Parse(headerValue);
58+
59+
// Assert
60+
header.Should().BeNull();
61+
}
4962
}

0 commit comments

Comments
 (0)