diff --git a/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/Helpers.cs b/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/Helpers.cs new file mode 100644 index 000000000..f124852dd --- /dev/null +++ b/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/Helpers.cs @@ -0,0 +1,24 @@ +using System; +using System.Text.RegularExpressions; + +namespace AWS.Lambda.Powertools.Tracing.Internal; + +/// +/// Helper class +/// +public static class Helpers +{ + /// + /// Sanitize a string by removing any characters that are not alphanumeric, whitespace, or one of the following: _ . : / % & # = + - @ + /// + /// + /// + public static string SanitizeString(string input) + { + // Define a regular expression pattern to match allowed characters + var pattern = @"[^a-zA-Z0-9\s_\.\:/%&#=+\-@]"; + + // Replace any character that does not match the pattern with an empty string, with a timeout + return Regex.Replace(input, pattern, string.Empty, RegexOptions.None, TimeSpan.FromMilliseconds(100)); + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/XRayRecorder.cs b/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/XRayRecorder.cs index e192036ee..53bface31 100644 --- a/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/XRayRecorder.cs +++ b/libraries/src/AWS.Lambda.Powertools.Tracing/Internal/XRayRecorder.cs @@ -78,7 +78,9 @@ public XRayRecorder(IAWSXRayRecorder awsxRayRecorder, IPowertoolsConfigurations public void BeginSubsegment(string name) { if (_isLambda) - _awsxRayRecorder.BeginSubsegment(name); + { + _awsxRayRecorder.BeginSubsegment(Helpers.SanitizeString(name)); + } } /// diff --git a/libraries/src/AWS.Lambda.Powertools.Tracing/TracingAttribute.cs b/libraries/src/AWS.Lambda.Powertools.Tracing/TracingAttribute.cs index 1946fb9c2..c144d0387 100644 --- a/libraries/src/AWS.Lambda.Powertools.Tracing/TracingAttribute.cs +++ b/libraries/src/AWS.Lambda.Powertools.Tracing/TracingAttribute.cs @@ -15,7 +15,6 @@ using System; using AspectInjector.Broker; -using AWS.Lambda.Powertools.Common; using AWS.Lambda.Powertools.Tracing.Internal; namespace AWS.Lambda.Powertools.Tracing; @@ -114,8 +113,10 @@ public class TracingAttribute : Attribute /// /// Set custom segment name for the operation. /// The default is '## {MethodName}'. + /// + /// The logical name of the service that handled the request, up to 200 characters. + /// Names can contain Unicode letters, numbers, and whitespace, and the following symbols: \_, ., :, /, %, &, #, =, +, \\, -, @ /// - /// The name of the segment. public string SegmentName { get; set; } = ""; /// diff --git a/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/Handlers/Handlers.cs b/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/Handlers/Handlers.cs index 385ce96c1..38210ac92 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/Handlers/Handlers.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/Handlers/Handlers.cs @@ -31,6 +31,18 @@ public void HandleWithSegmentName() } + [Tracing(SegmentName = "## <
$>g__Handler|0_0")] + public void HandleWithInvalidSegmentName() + { + MethodWithInvalidSegmentName(); + } + + [Tracing(SegmentName = "Inval$#id | ")] + private void MethodWithInvalidSegmentName() + { + + } + [Tracing(Namespace = "Namespace Defined")] public void HandleWithNamespace() { diff --git a/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/TracingAttributeTest.cs b/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/TracingAttributeTest.cs index 58252d67c..f02619c1f 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/TracingAttributeTest.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/TracingAttributeTest.cs @@ -256,6 +256,28 @@ public void OnEntry_WhenSegmentNameHasValue_BeginSubsegmentWithValue() Assert.Single(segment.Subsegments); Assert.Equal("SegmentName", subSegment.Name); } + + [Fact] + public void OnEntry_WhenSegmentName_Is_Unsupported() + { + // Arrange + Environment.SetEnvironmentVariable("LAMBDA_TASK_ROOT", "AWS"); + Environment.SetEnvironmentVariable("POWERTOOLS_SERVICE_NAME", "POWERTOOLS"); + + // Act + var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity(); + _handler.HandleWithInvalidSegmentName(); + var subSegment = segment.Subsegments[0]; + var childSegment = subSegment.Subsegments[0]; + + // Assert + Assert.True(segment.IsSubsegmentsAdded); + Assert.True(subSegment.IsSubsegmentsAdded); + Assert.Single(segment.Subsegments); + Assert.Single(subSegment.Subsegments); + Assert.Equal("## Maing__Handler0_0", subSegment.Name); + Assert.Equal("Inval#id Segment", childSegment.Name); + } [Fact] public void OnEntry_WhenNamespaceIsNull_SetNamespaceWithService()