From 419599d70de360d442b1a76068491bbc85ee4ed9 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Tue, 16 Sep 2025 11:38:20 -0700 Subject: [PATCH] Fix trace state key parsing in W3C propagator to support digits --- .../src/System/Diagnostics/W3CPropagator.cs | 11 +++++++---- .../tests/W3CPropagatorTests.cs | 13 ++++++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/W3CPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/W3CPropagator.cs index 239621061f19f2..1930552eb4fb75 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/W3CPropagator.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/W3CPropagator.cs @@ -130,6 +130,7 @@ internal static bool TryExtractBaggage(string? baggageString, out IEnumerable span) } #endif + // https://www.w3.org/TR/trace-context-2/#key // key = ( lcalpha / DIGIT ) 0*255 ( keychar ) // keychar = lcalpha / DIGIT / "_" / "-"/ "*" / "/" / "@" // lcalpha = %x61-7A ; a-z + // DIGIT = %x30-39 ; 0-9 #if NET - private const string TraceStateKeyValidChars = "*-/@_abcdefghijklmnopqrstuvwxyz"; + private const string TraceStateKeyValidChars = "*-/0123456789@_abcdefghijklmnopqrstuvwxyz"; private static readonly SearchValues s_validTraceStateChars = SearchValues.Create(TraceStateKeyValidChars); - private static bool IsInvalidTraceStateKey(ReadOnlySpan key) => key.IsEmpty || (key[0] < 'a' || key[0] > 'z') || key.ContainsAnyExcept(s_validTraceStateChars); + private static bool IsInvalidTraceStateKey(ReadOnlySpan key) => key.IsEmpty || ((uint)('z' - key[0]) > (uint)('z' - 'a') && (uint)('9' - key[0]) > (uint)('9' - '0')) || key.ContainsAnyExcept(s_validTraceStateChars); private const string TraceStateValueValidChars = "!\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; private static readonly SearchValues s_validTraceStateValueChars = SearchValues.Create(TraceStateValueValidChars); private static bool IsInvalidTraceStateValue(ReadOnlySpan value) => value.IsEmpty || value.ContainsAnyExcept(s_validTraceStateValueChars); #else - private static ulong[] ValidTraceStateKeyCharsMask = [0x0000A40000000000, 0x07FFFFFE80000001]; + private static ulong[] ValidTraceStateKeyCharsMask = [0x03FFA40000000000, 0x07FFFFFE80000001]; private static bool IsInvalidTraceStateKey(ReadOnlySpan key) { - if (key.IsEmpty || (key[0] < 'a' || key[0] > 'z')) // Key has to start with a lowercase letter + if (key.IsEmpty || ((uint)('z' - key[0]) > (uint)('z' - 'a') && (uint)('9' - key[0]) > (uint)('9' - '0'))) // Key has to start with a lowercase letter or digit { return true; // invalid key character, skip current entry } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/W3CPropagatorTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/W3CPropagatorTests.cs index 971276a8dcd8a7..a784dd7755dea9 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/W3CPropagatorTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/W3CPropagatorTests.cs @@ -31,16 +31,19 @@ public static IEnumerable W3CTestData() yield return new object[] { "state=1", "state=1", new[] { new KeyValuePair("b1", "v1") }, "b1 = v1", new[] { new KeyValuePair("b1", "v1") } }; // Invalid trace state - yield return new object[] { "PassThroughW3CState=1", null, null, null, null }; // trace state key has to be lowercase + yield return new object[] { "PassThroughW3CState=1", null, null, null, null }; // trace state key has to be lowercase or digit - // Invalid trace state - yield return new object[] { "1start=1", null, null, null, null }; // trace state key has to start with lowercase + // valid trace state, the key can have digits https://www.w3.org/TR/trace-context-2/#key + yield return new object[] { "1start=1", "1start=1", null, null, null }; // trace state key has to start with lowercase or digit + + // valid trace state, the key can have digits https://www.w3.org/TR/trace-context-2/#key + yield return new object[] { "123@456=1", "123@456=1", null, null, null }; // trace state key has to start with lowercase or digit // Tabs is not allowed in trace state values. use only the valid entry - yield return new object[] { "start=1, end=\t1", "start=1", null, null, null }; // trace state key has to start with lowercase + yield return new object[] { "start=1, end=\t1", "start=1", null, null, null }; // trace state key has to start with lowercase or digit // multiple trace states - yield return new object[] { "start=1, end=1", "start=1, end=1", null, null, null }; // trace state key has to start with lowercase + yield return new object[] { "start=1, end=1", "start=1, end=1", null, null, null }; // trace state key has to start with lowercase or digit // trace state longer than the max limit yield return new object[] { $"{new string('a', 255)}=1", null, null, null, null }; // trace state length max is 256