Skip to content

Commit efe1444

Browse files
e-n-0dromanolclaude
authored
[AAP] Collect DataContract JSON response body schemas (#8706)
## Summary of changes Adds API Security response body schema collection for ASP.NET MVC/Web API responses serialized with `DataContractJsonSerializer.WriteObject(response.OutputStream, data)` ([doc](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.json.datacontractjsonserializer.writeobject?view=netframework-4.8.1)). This adds a .NET Framework AppSec instrumentation for `DataContractJsonSerializer.WriteObject(Stream, object)`. The hook only reports when the serializer writes to the current ASP.NET response stream, AppSec is enabled, response body parsing is enabled, and an ASP.NET MVC/Web API scope is active. It also adds a `DataContractObjectExtractor` for this path. For `[DataContract]` types, it reports only `[DataMember]` members, uses `DataMember(Name = ...)`, and skips ignored or unmarked members. ## Reason for change A case seen writes the response with `DataContractJsonSerializer` directly to `HttpResponse.OutputStream`, which bypasses the existing `JsonResult.Data` response body extraction. Without this instrumentation, those endpoints skipped AAP API Security traces. ## Implementation details - Instruments `System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject(Stream, object)` - Captures after successful serialization, using the original graph object instead of buffering response bytes. - Requires the stream to be the current `HttpContext.Response.OutputStream`, so serializing to `MemoryStream`, files, or other streams are ignored. ## Test coverage - Added unit coverage for DataContract member naming and over-capture prevention. - Added an MVC5 sample endpoint that returns a custom `ActionResult` and calls `DataContractJsonSerializer.WriteObject(response.OutputStream, data)`. - Added API Security snapshots for the new MVC5 scenario with API Security enabled and disabled. ## Other details https://datadoghq.atlassian.net/browse/APPSEC-68123 --------- Co-authored-by: Daniel Romano <daniel.romanolorente@datadoghq.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4937b53 commit efe1444

18 files changed

Lines changed: 2151 additions & 178 deletions

tracer/build/_build/Honeypot/IntegrationGroups.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ static IntegrationMap()
2626
NugetPackages.Add("System", new string[] { });
2727
NugetPackages.Add("System.Diagnostics.Process", new string[] { });
2828
NugetPackages.Add("System.Runtime.Remoting", new string[] { });
29+
NugetPackages.Add("System.Runtime.Serialization", new string[] { });
2930
NugetPackages.Add("System.Security.Cryptography", new string[] { });
3031
NugetPackages.Add("System.Security.Cryptography.Primitives", new string[] { });
3132

tracer/build/supported_calltargets.g.json

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,81 @@
857857
"IsAdoNetIntegration": false,
858858
"InstrumentationCategory": 1
859859
},
860+
{
861+
"IntegrationName": "AspNet",
862+
"AssemblyName": "System.Runtime.Serialization",
863+
"TargetTypeName": "System.Runtime.Serialization.Json.DataContractJsonSerializer",
864+
"TargetMethodName": "WriteObject",
865+
"TargetReturnType": "System.Void",
866+
"TargetParameterTypes": [
867+
"System.Xml.XmlDictionaryWriter",
868+
"System.Object"
869+
],
870+
"MinimumVersion": {
871+
"Item1": 4,
872+
"Item2": 0,
873+
"Item3": 0
874+
},
875+
"MaximumVersion": {
876+
"Item1": 4,
877+
"Item2": 65535,
878+
"Item3": 65535
879+
},
880+
"InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.DataContractJsonSerializer_WriteObject_XmlDictionaryWriter_Integration",
881+
"IntegrationKind": 0,
882+
"IsAdoNetIntegration": false,
883+
"InstrumentationCategory": 2
884+
},
885+
{
886+
"IntegrationName": "AspNet",
887+
"AssemblyName": "System.Runtime.Serialization",
888+
"TargetTypeName": "System.Runtime.Serialization.XmlObjectSerializer",
889+
"TargetMethodName": "WriteObject",
890+
"TargetReturnType": "System.Void",
891+
"TargetParameterTypes": [
892+
"System.IO.Stream",
893+
"System.Object"
894+
],
895+
"MinimumVersion": {
896+
"Item1": 4,
897+
"Item2": 0,
898+
"Item3": 0
899+
},
900+
"MaximumVersion": {
901+
"Item1": 4,
902+
"Item2": 65535,
903+
"Item3": 65535
904+
},
905+
"InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.DataContractJsonSerializer_WriteObject_Integration",
906+
"IntegrationKind": 1,
907+
"IsAdoNetIntegration": false,
908+
"InstrumentationCategory": 2
909+
},
910+
{
911+
"IntegrationName": "AspNet",
912+
"AssemblyName": "System.Runtime.Serialization",
913+
"TargetTypeName": "System.Runtime.Serialization.XmlObjectSerializer",
914+
"TargetMethodName": "WriteObject",
915+
"TargetReturnType": "System.Void",
916+
"TargetParameterTypes": [
917+
"System.Xml.XmlWriter",
918+
"System.Object"
919+
],
920+
"MinimumVersion": {
921+
"Item1": 4,
922+
"Item2": 0,
923+
"Item3": 0
924+
},
925+
"MaximumVersion": {
926+
"Item1": 4,
927+
"Item2": 65535,
928+
"Item3": 65535
929+
},
930+
"InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.DataContractJsonSerializer_WriteObject_XmlWriter_Integration",
931+
"IntegrationKind": 1,
932+
"IsAdoNetIntegration": false,
933+
"InstrumentationCategory": 2
934+
},
860935
{
861936
"IntegrationName": "AspNet",
862937
"AssemblyName": "System.Web",

tracer/build/supported_versions.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@
5757
}
5858
]
5959
},
60+
{
61+
"integrationName": "AspNet",
62+
"assemblyName": "System.Runtime.Serialization",
63+
"minAssemblyVersionInclusive": "4.0.0",
64+
"maxAssemblyVersionInclusive": "4.65535.65535",
65+
"packages": []
66+
},
6067
{
6168
"integrationName": "AspNet",
6269
"assemblyName": "System.Web",
@@ -1550,4 +1557,4 @@
15501557
}
15511558
]
15521559
}
1553-
]
1560+
]

0 commit comments

Comments
 (0)