diff --git a/tracer/src/Datadog.Trace/AppSec/Security.cs b/tracer/src/Datadog.Trace/AppSec/Security.cs index 55a67e610775..d0acdd2dacdd 100644 --- a/tracer/src/Datadog.Trace/AppSec/Security.cs +++ b/tracer/src/Datadog.Trace/AppSec/Security.cs @@ -479,6 +479,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.PropagatedAppSec, "1"); } } diff --git a/tracer/src/Datadog.Trace/Iast/IastModule.cs b/tracer/src/Datadog.Trace/Iast/IastModule.cs index 97b0a5aabd3c..fc31e2185104 100644 --- a/tracer/src/Datadog.Trace/Iast/IastModule.cs +++ b/tracer/src/Datadog.Trace/Iast/IastModule.cs @@ -642,6 +642,7 @@ private static IastModuleResponse GetScope(string evidenceValue, IntegrationId i if (traceContext?.RootSpan is { } rootSpan) { rootSpan.Context.TraceContext?.SetSamplingPriority(SamplingPriorityValues.UserKeep, SamplingMechanism.Asm); + rootSpan.Context.TraceContext?.Tags.SetTag(Tags.PropagatedAppSec, "1"); } traceContext?.IastRequestContext?.AddVulnerability(vulnerability); @@ -699,6 +700,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.PropagatedAppSec, "1"); return new IastModuleResponse(scope); } diff --git a/tracer/src/Datadog.Trace/Propagators/DatadogContextPropagator.cs b/tracer/src/Datadog.Trace/Propagators/DatadogContextPropagator.cs index 5a88cd32a803..c05f6b62ba22 100644 --- a/tracer/src/Datadog.Trace/Propagators/DatadogContextPropagator.cs +++ b/tracer/src/Datadog.Trace/Propagators/DatadogContextPropagator.cs @@ -24,6 +24,12 @@ private DatadogContextPropagator() public void Inject(SpanContext context, TCarrier carrier, TCarrierSetter carrierSetter) where TCarrierSetter : struct, ICarrierSetter { + // Case 1 (1): If appsec standalone is enabled and appsec propagation is disabled (no ASM events) -> stop propagation + if (Tracer.Instance.Settings.AppsecStandaloneEnabledInternal && context.TraceContext.Tags.GetTag(Tags.PropagatedAppSec) != "1") + { + return; + } + TelemetryFactory.Metrics.RecordCountContextHeaderStyleInjected(MetricTags.ContextHeaderStyle.Datadog); var invariantCulture = CultureInfo.InvariantCulture; @@ -43,7 +49,6 @@ private DatadogContextPropagator() } var propagatedTagsHeader = context.PrepareTagsHeaderForPropagation(); - if (!string.IsNullOrEmpty(propagatedTagsHeader)) { carrierSetter.Set(carrier, HttpHeaderNames.PropagatedTags, propagatedTagsHeader!); @@ -67,9 +72,18 @@ private DatadogContextPropagator() var samplingPriority = ParseUtility.ParseInt32(carrier, carrierGetter, HttpHeaderNames.SamplingPriority); var origin = ParseUtility.ParseString(carrier, carrierGetter, HttpHeaderNames.Origin); var propagatedTraceTags = ParseUtility.ParseString(carrier, carrierGetter, HttpHeaderNames.PropagatedTags); - var traceTags = TagPropagation.ParseHeader(propagatedTraceTags); + // Case 1 (2): + // 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 (Tracer.Instance.Settings.AppsecStandaloneEnabledInternal) + { + // If the trace has appsec propagation tag, the default priority is user keep + samplingPriority = traceTags.GetTag(Tags.PropagatedAppSec) == "1" ? SamplingPriorityValues.UserKeep : SamplingPriorityValues.Default; + } + // reconstruct 128-bit trace id from the lower 64 bits in "x-datadog-traceid" // and the upper 64 bits in "_dd.p.tid" var traceId = GetFullTraceId((ulong)traceIdLower, traceTags); diff --git a/tracer/src/Datadog.Trace/Tags.cs b/tracer/src/Datadog.Trace/Tags.cs index 5923a725eba6..4a34dd4d0068 100644 --- a/tracer/src/Datadog.Trace/Tags.cs +++ b/tracer/src/Datadog.Trace/Tags.cs @@ -601,6 +601,12 @@ public static partial class Tags /// internal const string AppSecWafInitRuleErrors = "_dd.appsec.event_rules.errors"; + /// + /// 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.). + /// + internal const string PropagatedAppSec = "_dd.p.appsec"; + /// /// Should contain the public IP of the host initiating the request. /// diff --git a/tracer/src/Datadog.Trace/Tracer.cs b/tracer/src/Datadog.Trace/Tracer.cs index a4c87591a071..043885539529 100644 --- a/tracer/src/Datadog.Trace/Tracer.cs +++ b/tracer/src/Datadog.Trace/Tracer.cs @@ -419,13 +419,32 @@ 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 + // If parent is SpanContext but its TraceContext is null, then it was extracted from propagation headers. + + var propagatedTags = parentSpanContext.PropagatedTags; + var samplingPriority = parentSpanContext.SamplingPriority; + /*if (Settings?.AppsecStandaloneEnabledInternal == true) + { + // 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 (propagatedTags?.GetTag(Tags.PropagatedAppSec) != null) + { + // If the trace has appsec propagation tag, the default priority is user keep + samplingPriority = SamplingPriorityValues.UserKeep; + } + else + { + samplingPriority = SamplingPriorityValues.Default; + } + }*/ + + // 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; diff --git a/tracer/src/Datadog.Trace/TracerManagerFactory.cs b/tracer/src/Datadog.Trace/TracerManagerFactory.cs index 3793357c5b5c..e5f337d5700a 100644 --- a/tracer/src/Datadog.Trace/TracerManagerFactory.cs +++ b/tracer/src/Datadog.Trace/TracerManagerFactory.cs @@ -272,18 +272,6 @@ protected virtual ITraceSampler GetSampler(ImmutableTracerSettings settings) var sampler = new TraceSampler(new TracerRateLimiter(maxTracesPerInterval: settings.MaxTracesSubmittedPerSecondInternal, intervalMilliseconds: null)); - // remote sampling rules - var remoteSamplingRulesJson = settings.RemoteSamplingRules; - - if (!string.IsNullOrWhiteSpace(remoteSamplingRulesJson)) - { - var samplerStandalone = new TraceSampler(new TracerRateLimiter(1, 60_000)); - samplerStandalone.RegisterRule(new GlobalSamplingRateRule(1.0f)); - return samplerStandalone; - } - - var sampler = new TraceSampler(new TracerRateLimiter(settings.MaxTracesSubmittedPerSecondInternal, null)); - // sampling rules (remote value overrides local value) var samplingRulesJson = settings.CustomSamplingRulesInternal;