Skip to content

Commit

Permalink
[ASM] Send header values as string to the WAF (#6144)
Browse files Browse the repository at this point in the history
## Summary of changes

Util this PR, we woudl store the request headers in a array of strings
and we would send that array to the WAF. When generating fingerprints,
the WAF does not accept arrays of strings but a single string, so, when
we have a single header value, we store it in a string instead of in an
array of strings with a single value inside the array.

This change has fixed some problems related to the fingerprint
generation, which has derived in a large amount of updated snapshots.

## Reason for change

## Implementation details

## Test coverage

## Other details
<!-- Fixes #{issue} -->

<!-- ⚠️ Note: where possible, please obtain 2 approvals prior to
merging. Unless CODEOWNERS specifies otherwise, for external teams it is
typically best to have one review from a team member, and one review
from apm-dotnet. Trivial changes do not require 2 reviews. -->
  • Loading branch information
NachoEchevarria authored Oct 11, 2024
1 parent 56b733e commit c8399df
Show file tree
Hide file tree
Showing 126 changed files with 795 additions and 781 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Datadog.Trace.Util.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Primitives;

namespace Datadog.Trace.AppSec.Coordinator;

Expand All @@ -28,22 +29,23 @@ internal SecurityCoordinator(Security security, Span span, HttpTransport? transp

private static bool CanAccessHeaders => true;

public static Dictionary<string, string[]> ExtractHeadersFromRequest(IHeaderDictionary headers)
public static Dictionary<string, object> ExtractHeadersFromRequest(IHeaderDictionary headers)
{
var headersDic = new Dictionary<string, string[]>(headers.Keys.Count);
var headersDic = new Dictionary<string, object>(headers.Keys.Count);
foreach (var k in headers.Keys)
{
var currentKey = k ?? string.Empty;
if (!currentKey.Equals("cookie", System.StringComparison.OrdinalIgnoreCase))
{
currentKey = currentKey.ToLowerInvariant();
var value = GetHeaderValueForWaf(headers[currentKey]);
#if NETCOREAPP
if (!headersDic.TryAdd(currentKey, headers[currentKey]))
if (!headersDic.TryAdd(currentKey, value))
{
#else
if (!headersDic.ContainsKey(currentKey))
{
headersDic.Add(currentKey, headers[currentKey]);
headersDic.Add(currentKey, value);
}
else
{
Expand All @@ -56,6 +58,11 @@ public static Dictionary<string, string[]> ExtractHeadersFromRequest(IHeaderDict
return headersDic;
}

private static object GetHeaderValueForWaf(StringValues value)
{
return (value.Count == 1 ? value[0] : value);
}

internal void BlockAndReport(IResult? result)
{
if (result is not null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,20 +377,20 @@ public Dictionary<string, object> GetBasicRequestArgsForWaf()
{
var request = _httpTransport.Context.Request;
var headers = RequestDataHelper.GetHeaders(request);
Dictionary<string, string[]>? headersDic = null;
Dictionary<string, object>? headersDic = null;

if (headers is not null)
{
var headerKeys = headers.Keys;
headersDic = new Dictionary<string, string[]>(headerKeys.Count);
headersDic = new Dictionary<string, object>(headerKeys.Count);
foreach (string originalKey in headerKeys)
{
var keyForDictionary = originalKey?.ToLowerInvariant() ?? string.Empty;
if (keyForDictionary != "cookie")
{
if (!headersDic.ContainsKey(keyForDictionary))
{
headersDic.Add(keyForDictionary, headers.GetValues(originalKey));
headersDic.Add(keyForDictionary, GetHeaderValueForWaf(headers.GetValues(originalKey)));
}
else
{
Expand Down Expand Up @@ -488,10 +488,15 @@ public Dictionary<string, object> GetBasicRequestArgsForWaf()
return dict;
}

public Dictionary<string, string[]> GetResponseHeadersForWaf()
private static object GetHeaderValueForWaf(string[] value)
{
return (value.Count() == 1 ? value[0] : value);
}

public Dictionary<string, object> GetResponseHeadersForWaf()
{
var response = _httpTransport.Context.Response;
var headersDic = new Dictionary<string, string[]>(response.Headers.Keys.Count);
var headersDic = new Dictionary<string, object>(response.Headers.Keys.Count);
var headerKeys = response.Headers.Keys;
foreach (string originalKey in headerKeys)
{
Expand All @@ -501,7 +506,7 @@ public Dictionary<string, string[]> GetResponseHeadersForWaf()
keyForDictionary = keyForDictionary.ToLowerInvariant();
if (!headersDic.ContainsKey(keyForDictionary))
{
headersDic.Add(keyForDictionary, response.Headers.GetValues(originalKey));
headersDic.Add(keyForDictionary, GetHeaderValueForWaf(response.Headers.GetValues(originalKey)));
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public async Task TestApiSecurityScan(string url, string body, HttpStatusCode ex
// Simple scrubber for the response content type in .NET 8
// .NET 8 doesn't add the content-length header, whereas previous versions do
settings.AddSimpleScrubber(
"""_dd.appsec.s.res.headers: [{"content-length":[[[8]],{"len":1}]}],""",
"""_dd.appsec.s.res.headers: [{"content-length":[8]}],""",
"""_dd.appsec.s.res.headers: [{}],""");
#endif
await VerifySpans(spans, settings);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class AspNetBase : TestHelper
private static readonly Regex AppSecRaspWafDuration = new(@"_dd.appsec.rasp.duration: \d+\.0", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex AppSecRaspWafDurationWithBindings = new(@"_dd.appsec.rasp.duration_ext: \d+\.0", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex AppSecFingerPrintHeaders = new(@"_dd.appsec.fp.http.header: hdr-\d+-\S*-\d+-\S*", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex AppSecFingerPrintNetwork = new(@"_dd.appsec.fp.http.network: net-\d+-\d+", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex AppSecSpanIdRegex = (new Regex("\"span_id\":\\d+"));
private static readonly Type MetaStructHelperType = Type.GetType("Datadog.Trace.AppSec.Rasp.MetaStructHelper, Datadog.Trace");
private static readonly MethodInfo MetaStructByteArrayToObject = MetaStructHelperType.GetMethod("ByteArrayToObject", BindingFlags.Public | BindingFlags.Static);
Expand Down Expand Up @@ -124,6 +125,7 @@ public async Task TestAppSecRequestWithVerifyAsync(MockTracerAgent agent, string
public void ScrubFingerprintHeaders(VerifySettings settings)
{
settings.AddRegexScrubber(AppSecFingerPrintHeaders, "_dd.appsec.fp.http.header: <HeaderPrint>");
settings.AddRegexScrubber(AppSecFingerPrintNetwork, "_dd.appsec.fp.http.network: <NetworkPrint>");
}

public async Task VerifySpans(IImmutableList<MockSpan> spans, VerifySettings settings, bool testInit = false, string methodNameOverride = null, string testName = null, bool forceMetaStruct = false, string fileNameOverride = null)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[
[
{
TraceId: Id_1,
SpanId: Id_2,
Expand All @@ -25,8 +25,8 @@
network.client.ip: 127.0.0.1,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0000000000--1-4740ae63,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0000000000-3626b5f8-1-4740ae63,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"tst-037-011","name":"No fun","tags":{"category":"attack_attempt","type":"lfi"}},"rule_matches":[{"operator":"match_regex","operator_value":"fun","parameters":[{"address":"server.request.uri.raw","highlight":["fun"],"key_path":[],"value":"/health?q=fun"}]}]}]},
_dd.appsec.json.metastruct.test: true,
_dd.origin: appsec,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[
[
{
TraceId: Id_1,
SpanId: Id_2,
Expand Down Expand Up @@ -46,8 +46,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0100000001--3-bf93958a,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0100000001-3626b5f8-3-bf93958a,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-932-100","name":"Shell injection exploit","tags":{"category":"vulnerability_trigger","type":"command_injection"}},"rule_matches":[{"operator":"shi_detector","operator_value":"","parameters":[{"address":null,"highlight":[";evilCommand"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0100000001--3-bf93958a,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0100000001-3626b5f8-3-bf93958a,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-001-001","name":"Path traversal attack","tags":{"category":"vulnerability_trigger","type":"lfi"}},"rule_matches":[{"operator":"lfi_detector","operator_value":"","parameters":[{"address":null,"highlight":["/etc/password"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0100000001--3-bf93958a,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0100000001-3626b5f8-3-bf93958a,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-002-001","name":"Server-side request forgery","tags":{"category":"vulnerability_trigger","type":"ssrf"}},"rule_matches":[{"operator":"ssrf_detector","operator_value":"","parameters":[{"address":null,"highlight":["127.0.0.1"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[
[
{
TraceId: Id_1,
SpanId: Id_2,
Expand Down Expand Up @@ -47,8 +47,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0100000100--2-da57b738,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0100000100-3626b5f8-2-da57b738,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-942-100","name":"SQL injection exploit","tags":{"category":"vulnerability_trigger","type":"sql_injection"}},"rule_matches":[{"operator":"sqli_detector","operator_value":"","parameters":[{"address":null,"highlight":["' or '1'='1"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0000000001--3-bf93958a,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0000000001-3626b5f8-3-bf93958a,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-932-100","name":"Shell injection exploit","tags":{"category":"vulnerability_trigger","type":"command_injection"}},"rule_matches":[{"operator":"shi_detector","operator_value":"","parameters":[{"address":null,"highlight":[";evilCommand"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0000000001--3-bf93958a,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0000000001-3626b5f8-3-bf93958a,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-001-001","name":"Path traversal attack","tags":{"category":"vulnerability_trigger","type":"lfi"}},"rule_matches":[{"operator":"lfi_detector","operator_value":"","parameters":[{"address":null,"highlight":["/etc/password"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0000000001--3-bf93958a,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0000000001-3626b5f8-3-bf93958a,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-002-001","name":"Server-side request forgery","tags":{"category":"vulnerability_trigger","type":"ssrf"}},"rule_matches":[{"operator":"ssrf_detector","operator_value":"","parameters":[{"address":null,"highlight":["127.0.0.1"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0000000100--2-da57b738,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0000000100-3626b5f8-2-da57b738,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-942-100","name":"SQL injection exploit","tags":{"category":"vulnerability_trigger","type":"sql_injection"}},"rule_matches":[{"operator":"sqli_detector","operator_value":"","parameters":[{"address":null,"highlight":["' or '1'='1"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[
[
{
TraceId: Id_1,
SpanId: Id_2,
Expand All @@ -23,8 +23,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0000000001--3-bf93958a,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0000000001-3626b5f8-3-bf93958a,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-932-100","name":"Shell injection exploit","tags":{"category":"vulnerability_trigger","type":"command_injection"}},"rule_matches":[{"operator":"shi_detector","operator_value":"","parameters":[{"address":null,"highlight":[";evilCommand"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0000000001--3-bf93958a,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0000000001-3626b5f8-3-bf93958a,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-001-001","name":"Path traversal attack","tags":{"category":"vulnerability_trigger","type":"lfi"}},"rule_matches":[{"operator":"lfi_detector","operator_value":"","parameters":[{"address":null,"highlight":["/etc/password"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0000000001--3-bf93958a,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0000000001-3626b5f8-3-bf93958a,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-002-001","name":"Server-side request forgery","tags":{"category":"vulnerability_trigger","type":"ssrf"}},"rule_matches":[{"operator":"ssrf_detector","operator_value":"","parameters":[{"address":null,"highlight":["127.0.0.1"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0000000001--3-bf93958a,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0000000001-3626b5f8-3-bf93958a,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-002-001","name":"Server-side request forgery","tags":{"category":"vulnerability_trigger","type":"ssrf"}},"rule_matches":[{"operator":"ssrf_detector","operator_value":"","parameters":[{"address":null,"highlight":["127.0.0.1"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[
[
{
TraceId: Id_1,
SpanId: Id_2,
Expand All @@ -25,8 +25,8 @@
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.endpoint: http-post-a13f66cb--6f45fc03,
_dd.appsec.fp.http.header: hdr-0000000100--3-4d739311,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0000000100-3626b5f8-3-4d739311,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-942-100","name":"SQL injection exploit","tags":{"category":"vulnerability_trigger","type":"sql_injection"}},"rule_matches":[{"operator":"sqli_detector","operator_value":"","parameters":[{"address":null,"highlight":["' or '1'='1"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[
[
{
TraceId: Id_1,
SpanId: Id_2,
Expand All @@ -24,8 +24,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0000000001--5-6cdcf2fe,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0000000001-3626b5f8-5-6cdcf2fe,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-932-100","name":"Shell injection exploit","tags":{"category":"vulnerability_trigger","type":"command_injection"}},"rule_matches":[{"operator":"shi_detector","operator_value":"","parameters":[{"address":null,"highlight":[";evilCommand"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0000000001--5-6cdcf2fe,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0000000001-3626b5f8-5-6cdcf2fe,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-001-001","name":"Path traversal attack","tags":{"category":"vulnerability_trigger","type":"lfi"}},"rule_matches":[{"operator":"lfi_detector","operator_value":"","parameters":[{"address":null,"highlight":["/etc/password"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0000000001--5-6cdcf2fe,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0000000001-3626b5f8-5-6cdcf2fe,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-002-001","name":"Server-side request forgery","tags":{"category":"vulnerability_trigger","type":"ssrf"}},"rule_matches":[{"operator":"ssrf_detector","operator_value":"","parameters":[{"address":null,"highlight":["127.0.0.1"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
language: dotnet,
runtime-id: Guid_1,
span.kind: server,
_dd.appsec.fp.http.header: hdr-0000000001--5-6cdcf2fe,
_dd.appsec.fp.http.network: net-0-1000000000,
_dd.appsec.fp.http.header: hdr-0000000001-3626b5f8-5-6cdcf2fe,
_dd.appsec.fp.http.network: net-1-1000000000,
_dd.appsec.json: {"triggers":[{"rule":{"id":"rasp-002-001","name":"Server-side request forgery","tags":{"category":"vulnerability_trigger","type":"ssrf"}},"rule_matches":[{"operator":"ssrf_detector","operator_value":"","parameters":[{"address":null,"highlight":["127.0.0.1"],"key_path":null,"value":null}]}],"span_id": XXX}]},
_dd.origin: appsec,
_dd.runtime_family: dotnet
Expand Down
Loading

0 comments on commit c8399df

Please sign in to comment.