Skip to content

Commit 09801ba

Browse files
authored
Fix parsing trace parent in W3CPropagator (#117530)
* Fix parsing trace parent in W3CPropagator * Fix typo * Support non-00 version
1 parent 7a501a1 commit 09801ba

File tree

2 files changed

+87
-2
lines changed

2 files changed

+87
-2
lines changed

src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/W3CPropagator.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -601,16 +601,37 @@ private static bool NeedToEscapeBaggageValueCharacter(char c)
601601
// Example 00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01
602602
private static bool IsInvalidTraceParent(string? traceParent)
603603
{
604-
if (string.IsNullOrEmpty(traceParent) || traceParent.Length != 55)
604+
if (string.IsNullOrEmpty(traceParent) || traceParent.Length < 55)
605605
{
606606
return true;
607607
}
608608

609-
if (traceParent[0] == 'f' || traceParent[1] == 'f' || IsInvalidTraceParentCharacter(traceParent[0]) || IsInvalidTraceParentCharacter(traceParent[1]))
609+
if ((traceParent[0] == 'f' && traceParent[1] == 'f') || IsInvalidTraceParentCharacter(traceParent[0]) || IsInvalidTraceParentCharacter(traceParent[1]))
610610
{
611611
return true;
612612
}
613613

614+
if (traceParent[0] == '0' && traceParent[1] == '0')
615+
{
616+
// version 00 should have exactly 55 characters
617+
if (traceParent.Length != 55)
618+
{
619+
return true; // invalid length for version 00
620+
}
621+
}
622+
else
623+
{
624+
// If a higher version is detected, the implementation SHOULD try to parse it by trying the following:
625+
// o If the size of the header is shorter than 55 characters, the vendor should not parse the header and should restart the trace.
626+
// o Parse trace-id (from the first dash through the next 32 characters). Vendors MUST check that the 32 characters are hex, and that they are followed by a dash (-).
627+
// o Parse parent-id (from the second dash at the 35th position through the next 16 characters). Vendors MUST check that the 16 characters are hex and followed by a dash.
628+
// o Parse the sampled bit of flags (2 characters from the third dash). Vendors MUST check that the 2 characters are either the end of the string or a dash.
629+
if (traceParent.Length > 55 && traceParent[55] != '-')
630+
{
631+
return true; // invalid format for version other than 00
632+
}
633+
}
634+
614635
if (traceParent[2] != '-' || traceParent[35] != '-' || traceParent[52] != '-')
615636
{
616637
return true;

src/libraries/System.Diagnostics.DiagnosticSource/tests/W3CPropagatorTests.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,70 @@ public void TestBaggagePropagationLimits()
316316
Assert.Equal(entryCount, decodedBaggage.Count());
317317
}
318318

319+
// Trace ID format is defined in W3C Trace Context specification:
320+
// HEXDIGLC = DIGIT / "a" / "b" / "c" / "d" / "e" / "f"; lowercase hex character
321+
// value = version "-" version-format
322+
// version = 2HEXDIGLC; this document assumes version 00. Version ff is forbidden
323+
// version - format = trace - id "-" parent - id "-" trace - flags
324+
// trace - id = 32HEXDIGLC; 16 bytes array identifier. All zeroes forbidden
325+
// parent-id = 16HEXDIGLC ; 8 bytes array identifier. All zeroes forbidden
326+
// trace-flags = 2HEXDIGLC ; 8 bit flags.
327+
// . . . . . .
328+
// Example 00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01
329+
// If a higher version is detected, the implementation SHOULD try to parse it by trying the following:
330+
// o If the size of the header is shorter than 55 characters, the vendor should not parse the header and should restart the trace.
331+
// o Parse trace-id (from the first dash through the next 32 characters). Vendors MUST check that the 32 characters are hex, and that they are followed by a dash (-).
332+
// o Parse parent-id (from the second dash at the 35th position through the next 16 characters). Vendors MUST check that the 16 characters are hex and followed by a dash.
333+
// o Parse the sampled bit of flags (2 characters from the third dash). Vendors MUST check that the 2 characters are either the end of the string or a dash.
334+
public static IEnumerable<object[]> W3CTraceParentData()
335+
{
336+
// TraceId, valid data?
337+
338+
yield return new object[] { "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01", true }; // Perfect trace parent
339+
yield return new object[] { "0f-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01", true }; // version is 0f, which is valid
340+
yield return new object[] { "f0-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01", true }; // version is f0, which is valid
341+
yield return new object[] { "ff-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01", false }; // version is ff, which is invalid
342+
yield return new object[] { "f-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01", false }; // version is one digit 'f', which is invalid
343+
yield return new object[] { "0g-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01", false }; // version is 0g, which is invalid
344+
yield return new object[] { "0F-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01", false }; // version is 0F, which is invalid, 'F' should be lower case
345+
yield return new object[] { "00-af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01", false }; // trace-id length is wrong
346+
yield return new object[] { "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e-01", false }; // parent id length is wrong
347+
yield return new object[] { "00-00000000000000000000000000000000-b9c7c989f97918e1-01", false }; // all zeros trace-id is invalid
348+
yield return new object[] { "00-0af7651916cd43dd8448eb211c80319c-0000000000000000-01", false }; // all zeros parent id is invalid
349+
yield return new object[] { "00-0af7651916cd43dd8448eb211c80319C-b9c7c989f97918e1-01", false }; // trace-id has upper case 'C', which is invalid
350+
yield return new object[] { "00-0af7651916cd43dd8448eb211c80319c-B9c7c989f97918e1-01", false }; // parent-id has upper case 'B', which is invalid
351+
yield return new object[] { "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-0", false }; // trace flags length is wrong
352+
yield return new object[] { "00-0af7651916cd43dd8448ek211c80319c-b9c7c989f97918e1-01", false }; // trace-id has wrong character 'k', which is invalid
353+
yield return new object[] { "00-0af7651916cd43dd8448ee211c80319c-b9c7c989f97918z1-01", false }; // parent-id has wrong character 'z', which is invalid
354+
yield return new object[] { "00_0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01", false }; // invalid separator, should be '-'
355+
yield return new object[] { "00-0af7651916cd43dd8448eb211c80319c_b9c7c989f97918e1-01", false }; // invalid separator, should be '-'
356+
yield return new object[] { "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1_01", false }; // invalid separator, should be '-'
357+
yield return new object[] { "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-011", false }; // invalid trace parent length
358+
yield return new object[] { "01-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-011", false }; // version higher than 00 but it supposes to have '-' after the sampling flags
359+
360+
// version higher than 00, can have '-' after the sampling flags and more data. Vendors MUST NOT parse or assume anything about unknown fields for this version
361+
yield return new object[] { "01-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01-00", true };
362+
}
363+
364+
[Theory]
365+
[MemberData(nameof(W3CTraceParentData))]
366+
public void ValidateTraceIdAndStateExtraction(string traceParent, bool isValid)
367+
{
368+
s_w3cPropagator.ExtractTraceIdAndState(null, (object carrier, string fieldName, out string? fieldValue, out IEnumerable<string>? fieldValues) =>
369+
{
370+
fieldValues = null;
371+
fieldValue = null;
372+
373+
if (fieldName == PropagatorTests.TraceParent)
374+
{
375+
fieldValue = traceParent;
376+
return;
377+
}
378+
}, out string? traceId, out _);
379+
380+
Assert.Equal(isValid, traceId is not null);
381+
}
382+
319383
private static string? EncodeBaggage(IEnumerable<KeyValuePair<string, string>> baggageEntries)
320384
{
321385
Activity? current = Activity.Current;

0 commit comments

Comments
 (0)