Skip to content

Commit

Permalink
[ASM] Standalone Billing (part 2: Propagation) (#5743)
Browse files Browse the repository at this point in the history
## Summary of changes

Part 2 of the implementation of [Standalone ASM
billing](https://docs.google.com/document/d/12NBx-nD-IoQEMiCRnJXneq4Be7cbtSc6pJLOFUWTpNE/edit?pli=1#heading=h.hs091bhdmugz).
Part 1 was #5565 

This PR adds the propagated span tag `_dd.p.appsec: 1` allowing to
propagate to downstream services the information that the current
distributed trace is containing at least one ASM security event.

## Test coverage

This fully pass the system tests for ASM Standalone.
  • Loading branch information
e-n-0 authored Jul 17, 2024
1 parent 83dab2b commit 935deec
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 5 deletions.
1 change: 1 addition & 0 deletions tracer/src/Datadog.Trace/AppSec/Security.cs
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ internal void SetTraceSamplingPriority(Span span)
else if (_rateLimiter?.Allowed(span) ?? false)
{
span.Context.TraceContext?.SetSamplingPriority(SamplingPriorityValues.UserKeep, SamplingMechanism.Asm);
span.Context.TraceContext?.Tags.SetTag(Tags.Propagated.AppSec, "1");
}
}

Expand Down
4 changes: 4 additions & 0 deletions tracer/src/Datadog.Trace/Iast/IastModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ private static IastModuleResponse AddWebVulnerability(string? evidenceValue, Int
{
traceContext.IastRequestContext?.AddVulnerability(vulnerability);
traceContext.SetSamplingPriority(SamplingPriorityValues.UserKeep, SamplingMechanism.Asm);
traceContext.Tags.SetTag(Tags.Propagated.AppSec, "1");

return IastModuleResponse.Vulnerable;
}
Expand Down Expand Up @@ -636,6 +637,8 @@ private static IastModuleResponse GetScope(string evidenceValue, IntegrationId i
if (isRequest)
{
traceContext?.SetSamplingPriority(SamplingPriorityValues.UserKeep, SamplingMechanism.Asm);
traceContext?.Tags.SetTag(Tags.Propagated.AppSec, "1");

traceContext?.IastRequestContext?.AddVulnerability(vulnerability);
return IastModuleResponse.Vulnerable;
}
Expand Down Expand Up @@ -691,6 +694,7 @@ private static IastModuleResponse AddVulnerabilityAsSingleSpan(Tracer tracer, In
scope.Span.Type = SpanTypes.IastVulnerability;
tracer.TracerManager.Telemetry.IntegrationGeneratedSpan(integrationId);
scope.Span.Context.TraceContext?.SetSamplingPriority(SamplingPriorityValues.UserKeep, SamplingMechanism.Asm);
scope.Span.Context.TraceContext?.Tags.SetTag(Tags.Propagated.AppSec, "1");
return new IastModuleResponse(scope);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ public void Inject<TCarrier, TCarrierSetter>(SpanContext context, TCarrier carri
}

var propagatedTagsHeader = context.PrepareTagsHeaderForPropagation();

if (!string.IsNullOrEmpty(propagatedTagsHeader))
{
carrierSetter.Set(carrier, HttpHeaderNames.PropagatedTags, propagatedTagsHeader!);
Expand Down
6 changes: 6 additions & 0 deletions tracer/src/Datadog.Trace/Propagators/SpanContextPropagator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ internal void Inject<TCarrier, TCarrierSetter>(SpanContext context, TCarrier car
if (context == null!) { ThrowHelper.ThrowArgumentNullException(nameof(context)); }
if (carrier == null) { ThrowHelper.ThrowArgumentNullException(nameof(carrier)); }

// If appsec standalone is enabled and appsec propagation is disabled (no ASM events) -> stop propagation
if (context.TraceContext?.Tracer.Settings?.AppsecStandaloneEnabledInternal == true && context.TraceContext.Tags.GetTag(Tags.Propagated.AppSec) != "1")
{
return;
}

// trigger a sampling decision if it hasn't happened yet
_ = context.GetOrMakeSamplingDecision();

Expand Down
6 changes: 6 additions & 0 deletions tracer/src/Datadog.Trace/Tags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,12 @@ internal static class Propagated
/// lower-case hexadecimal string with no zero-padding or `0x` prefix.
/// </summary>
internal const string TraceIdUpper = "_dd.p.tid";

/// <summary>
/// A boolean allowing the propagation to downstream services the information that the current distributed trace
/// is containing at least one ASM security event, no matter its type (threats, business logic events, IAST, etc.).
/// </summary>
internal const string AppSec = "_dd.p.appsec";
}
}
}
20 changes: 16 additions & 4 deletions tracer/src/Datadog.Trace/Tracer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -419,13 +419,25 @@ internal SpanContext CreateSpanContext(ISpanContext parent = null, string servic

if (traceContext == null)
{
// If parent is SpanContext but its TraceContext is null, then it was extracted from
// propagation headers. Create a new TraceContext (this will start a new trace) and initialize
var propagatedTags = parentSpanContext.PropagatedTags;
var samplingPriority = parentSpanContext.SamplingPriority;

// When in appsec standalone mode, only distributed traces with the `_dd.p.appsec` tag
// are propagated downstream, however we need 1 trace per minute sent to the backend, so
// we unset sampling priority so the rate limiter decides.
if (Settings?.AppsecStandaloneEnabledInternal == true)
{
// If the trace has appsec propagation tag, the default priority is user keep
samplingPriority = propagatedTags?.GetTag(Tags.Propagated.AppSec) == "1" ? SamplingPriorityValues.UserKeep : null;
}

// If parent is SpanContext but its TraceContext is null, then it was extracted from propagation headers.
// Create a new TraceContext (this will start a new trace) and initialize
// it with the propagated values (sampling priority, origin, tags, W3C trace state, etc).
traceContext = new TraceContext(this, parentSpanContext.PropagatedTags);
traceContext = new TraceContext(this, propagatedTags);
TelemetryFactory.Metrics.RecordCountTraceSegmentCreated(MetricTags.TraceContinuation.Continued);

var samplingPriority = parentSpanContext.SamplingPriority ?? DistributedTracer.Instance.GetSamplingPriority();
samplingPriority ??= DistributedTracer.Instance.GetSamplingPriority();
traceContext.SetSamplingPriority(samplingPriority);
traceContext.Origin = parentSpanContext.Origin;
traceContext.AdditionalW3CTraceState = parentSpanContext.AdditionalW3CTraceState;
Expand Down

0 comments on commit 935deec

Please sign in to comment.