Skip to content

Commit d70d287

Browse files
[ASM] WAF error span tags (#6729)
## Summary of changes This PR adds the new error span tags for ASM. It adds the error tags containing the error returned by the WAF. These tags are defined in [this RFC](https://docs.google.com/document/d/1D4hkC0jwwUyeo0hEQgyKP54kM1LZU98GL8MaP60tQrA/edit?tab=t.0). ## 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. -->
1 parent fe97103 commit d70d287

File tree

4 files changed

+83
-0
lines changed

4 files changed

+83
-0
lines changed

tracer/src/Datadog.Trace/AppSec/AppSecRequestContext.cs

+28
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ internal partial class AppSecRequestContext
2424
private readonly object _sync = new();
2525
private readonly RaspTelemetryHelper? _raspTelemetryHelper = Security.Instance.RaspEnabled ? new RaspTelemetryHelper() : null;
2626
private readonly List<object> _wafSecurityEvents = new();
27+
private int? _wafError = null;
28+
private int? _wafRaspError = null;
2729
private Dictionary<string, List<Dictionary<string, object>>>? _raspStackTraces;
2830

2931
internal void CloseWebSpan(TraceTagCollection tags, Span span)
@@ -50,10 +52,36 @@ internal void CloseWebSpan(TraceTagCollection tags, Span span)
5052
span.SetMetaStruct(StackKey, MetaStructHelper.ObjectToByteArray(_raspStackTraces));
5153
}
5254

55+
if (_wafError != null)
56+
{
57+
tags.SetTag(Tags.WafError, _wafError.ToString());
58+
}
59+
60+
if (_wafRaspError != null)
61+
{
62+
tags.SetTag(Tags.RaspWafError, _wafRaspError.ToString());
63+
}
64+
5365
_raspTelemetryHelper?.GenerateRaspSpanMetricTags(span.Tags);
5466
}
5567
}
5668

69+
internal void CheckWAFError(int code, bool isRasp)
70+
{
71+
int? existingValue = isRasp ? _wafRaspError : _wafError;
72+
if (code < 0 && (existingValue == null || existingValue < code))
73+
{
74+
if (isRasp)
75+
{
76+
_wafRaspError = code;
77+
}
78+
else
79+
{
80+
_wafError = code;
81+
}
82+
}
83+
}
84+
5785
internal void AddRaspSpanMetrics(ulong duration, ulong durationWithBindings, bool timeout)
5886
{
5987
lock (_sync)

tracer/src/Datadog.Trace/AppSec/Coordinator/SecurityCoordinator.cs

+10
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ internal readonly partial struct SecurityCoordinator
7373
? additiveContext.RunWithEphemeral(args, _security.Settings.WafTimeoutMicroSeconds, isRasp)
7474
: additiveContext.Run(args, _security.Settings.WafTimeoutMicroSeconds);
7575

76+
SetErrorInformation(isRasp, result);
7677
SecurityReporter.RecordTelemetry(result);
7778
}
7879
catch (Exception ex) when (ex is not BlockException)
@@ -94,6 +95,14 @@ internal readonly partial struct SecurityCoordinator
9495
return result;
9596
}
9697

98+
private void SetErrorInformation(bool isRasp, IResult? result)
99+
{
100+
if (result is not null)
101+
{
102+
_localRootSpan.Context.TraceContext.AppSecRequestContext.CheckWAFError((int)result.ReturnCode, isRasp);
103+
}
104+
}
105+
97106
internal static Span TryGetRoot(Span span) => span.Context.TraceContext?.RootSpan ?? span;
98107

99108
public IResult? RunWafForUser(string? userId = null, string? userLogin = null, string? userSessionId = null, bool fromSdk = false, Dictionary<string, string>? otherTags = null)
@@ -129,6 +138,7 @@ internal readonly partial struct SecurityCoordinator
129138

130139
// run the WAF and execute the results
131140
result = additiveContext.Run(userAddresses, _security.Settings.WafTimeoutMicroSeconds);
141+
SetErrorInformation(false, result);
132142
additiveContext.CommitUserRuns(userAddresses, fromSdk);
133143
RecordTelemetry(result);
134144

tracer/src/Datadog.Trace/Tags.cs

+10
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,16 @@ public static partial class Tags
614614
/// </summary>
615615
internal const string AppSecWafVersion = "_dd.appsec.waf.version";
616616

617+
/// <summary>
618+
/// The most relevant WAF error code during a request
619+
/// </summary>
620+
public const string WafError = "_dd.appsec.waf.error";
621+
622+
/// <summary>
623+
/// The most relevant WAF error code during a request when using RASP
624+
/// </summary>
625+
public const string RaspWafError = "_dd.appsec.rasp.error";
626+
617627
/// <summary>
618628
/// String-serialized JSON array, each item being a map containing:
619629
/// Error(e) - the error string.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// <copyright file="AppSecContextTests.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
4+
// </copyright>
5+
6+
using System.Collections.Generic;
7+
using Datadog.Trace.Configuration;
8+
using Datadog.Trace.Tagging;
9+
using FluentAssertions;
10+
using Xunit;
11+
12+
namespace Datadog.Trace.Security.Unit.Tests;
13+
14+
public class AppSecContextTests
15+
{
16+
[InlineData(-2, -2, -1, -2, -1)]
17+
[InlineData(2, -2, -1, null, -1)]
18+
[InlineData(2, 2, 1, null, null)]
19+
[Theory]
20+
public void GivenAQuery_WhenWAFError_ThenSpanHasErrorTags(int raspErrorCode, int wafErrorCode, int wafErrorCode2, int? expectedRaspErrorCode, int? expectedWafErrorCode)
21+
{
22+
var settings = TracerSettings.Create(new Dictionary<string, object>());
23+
var tracer = new Tracer(settings, null, null, null, null);
24+
var rootTestScope = (Scope)tracer.StartActive("test.trace");
25+
26+
var appSecContext = rootTestScope.Span.Context.TraceContext.AppSecRequestContext;
27+
appSecContext.CheckWAFError(raspErrorCode, true);
28+
appSecContext.CheckWAFError(wafErrorCode, false);
29+
appSecContext.CheckWAFError(wafErrorCode2, false);
30+
TraceTagCollection tags = new();
31+
appSecContext.CloseWebSpan(tags, rootTestScope.Span);
32+
tags.GetTag(Tags.WafError).Should().Be(expectedWafErrorCode?.ToString());
33+
tags.GetTag(Tags.RaspWafError).Should().Be(expectedRaspErrorCode?.ToString());
34+
}
35+
}

0 commit comments

Comments
 (0)