Skip to content

Commit 3861907

Browse files
committed
enhance sanitization of complex objects to return dictionary representation
1 parent 0949d05 commit 3861907

File tree

2 files changed

+81
-8
lines changed

2 files changed

+81
-8
lines changed

libraries/src/AWS.Lambda.Powertools.Tracing/Internal/XRayRecorder.cs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ private static object SanitizeValueRecursive(object value, int depth)
381381
return collectionResult;
382382

383383
// Handle complex objects
384-
return SanitizeComplexObject(value, type);
384+
return SanitizeComplexObject(value, type, depth);
385385
}
386386

387387
/// <summary>
@@ -529,22 +529,52 @@ private static object SanitizeEnumerable(System.Collections.IEnumerable enumerab
529529
}
530530

531531
/// <summary>
532-
/// Sanitizes complex objects by converting them to safe string representation.
532+
/// Sanitizes complex objects by converting them to dictionaries.
533+
/// Uses reflection with proper AOT attributes for compatibility.
533534
/// </summary>
534535
/// <param name="value">The object to sanitize</param>
535536
/// <param name="type">The object type</param>
536-
/// <returns>Sanitized string representation</returns>
537-
private static object SanitizeComplexObject(object value, Type type)
537+
/// <param name="depth">Current recursion depth</param>
538+
/// <returns>Sanitized dictionary representation or string fallback</returns>
539+
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Complex object properties are preserved for tracing scenarios")]
540+
[UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling", Justification = "Complex object properties are preserved for tracing scenarios")]
541+
private static object SanitizeComplexObject(object value, Type type, int depth)
538542
{
539543
try
540544
{
541-
// This ensures the object can be serialized without issues
542-
return $"[{type.Name}] {value}";
545+
var properties = type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
546+
var sanitizedObject = new System.Collections.Generic.Dictionary<string, object>();
547+
548+
foreach (var prop in properties)
549+
{
550+
try
551+
{
552+
if (prop.CanRead && prop.GetIndexParameters().Length == 0) // Skip indexers
553+
{
554+
var propValue = prop.GetValue(value);
555+
sanitizedObject[prop.Name] = SanitizeValueRecursive(propValue, depth + 1);
556+
}
557+
}
558+
catch (Exception ex)
559+
{
560+
// If we can't read a property, record the error
561+
sanitizedObject[prop.Name] = $"[Error reading property: {ex.Message}]";
562+
}
563+
}
564+
565+
return sanitizedObject;
543566
}
544567
catch (Exception ex)
545568
{
546-
// If all else fails, return a safe fallback
547-
return $"[Object conversion failed: {ex.Message}]";
569+
// If reflection fails, fall back to string representation
570+
try
571+
{
572+
return $"[{type.Name}] {value}";
573+
}
574+
catch (Exception toStringEx)
575+
{
576+
return $"[Object conversion failed: {ex.Message}, ToString failed: {toStringEx.Message}]";
577+
}
548578
}
549579
}
550580

libraries/tests/AWS.Lambda.Powertools.Tracing.Tests/XRayRecorderSanitizationAdvancedTests.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,49 @@ public void AddMetadata_WithMaxDepthReached_ReturnsMaxDepthMessage()
376376
_mockAwsXRayRecorder.Received(1).AddMetadata("test", "very_deep", Arg.Any<object>());
377377
}
378378

379+
[Fact]
380+
public void AddMetadata_WithComplexObject_SerializesToDictionary()
381+
{
382+
// Arrange - Create a complex object that should be serialized to a dictionary
383+
var complexObject = new
384+
{
385+
SystemInfo = new
386+
{
387+
DotNetVersion = "10.0.0",
388+
RuntimeVersion = ".NET 10.0.0-rc.1.25451.107",
389+
OSDescription = "Amazon Linux 2023.8.20250915",
390+
OSArchitecture = "Arm64",
391+
ProcessArchitecture = "Arm64",
392+
RuntimeIdentifier = "linux-arm64",
393+
MachineName = "169",
394+
ProcessorCount = 2,
395+
WorkingSet = 74358784L,
396+
Is64BitOperatingSystem = true,
397+
Is64BitProcess = true,
398+
CLRVersion = "10.0.0",
399+
CurrentDirectory = "/var/task"
400+
},
401+
LambdaInfo = new
402+
{
403+
FunctionName = "dotnet10-container",
404+
FunctionVersion = "$LATEST",
405+
InvokedFunctionArn = "arn:aws:lambda:eu-west-1:746792595426:function:dotnet10-container",
406+
MemoryLimitInMB = 512,
407+
RemainingTime = TimeSpan.FromSeconds(28.3635803),
408+
RequestId = "fa48e22e-6312-47b7-8744-e0ae3f0b78bd",
409+
LogGroupName = "/aws/lambda/dotnet10-container",
410+
LogStreamName = "2025/10/01/[$LATEST]03cfeb57967e457db33a7011743f0217"
411+
}
412+
};
413+
414+
// Act
415+
_xrayRecorder.AddMetadata("dotnet10-ns", "FunctionHandler response", complexObject);
416+
417+
// Assert - Verify the call was made and the object should be serialized as a dictionary structure
418+
_mockAwsXRayRecorder.Received(1).AddMetadata("dotnet10-ns", "FunctionHandler response",
419+
Arg.Is<object>(obj => obj is System.Collections.Generic.Dictionary<string, object>));
420+
}
421+
379422
[Fact]
380423
public void SanitizeValueForMetadata_WithObjectThatThrowsInToString_ReturnsSanitizationFailedMessage()
381424
{

0 commit comments

Comments
 (0)