From c6b6424dc25ad9548a96d7a01db4d4e588cf891e Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Wed, 17 Jun 2026 11:02:32 -0400 Subject: [PATCH] Fix AppSec/IAST context dropped when trace.propagation.behavior.extract=ignore/restart With IGNORE or RESTART, CoreSpanBuilder nulls the remote TagContext parent before buildSpanContext reaches the instanceof TagContext branch that copies requestContextDataAppSec/requestContextDataIast. The AppSec context is lost and GatewayBridge callbacks no-op, silently bypassing WAF/RASP for all inbound requests in AppSec-enabled services configured to ignore distributed tracing extraction. Fix: promote the AppSec/IAST data from the TagContext into the builder fields before nulling the parent. The builder-field path in buildSpanContext applies these after the parent is gone, preserving the request context while still correctly dropping trace identifiers and sampling priority. Fixes: APMSP-3198 Co-Authored-By: Claude Sonnet 4.6 --- .../java/datadog/trace/core/CoreTracer.java | 12 ++++++++ .../trace/core/CoreSpanBuilderTest.java | 30 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java b/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java index 375480a8792..01f54c10f77 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java @@ -1790,6 +1790,18 @@ protected static final AgentSpan startSpan( // Handle remote terminated context as span links if (parentContext != null && parentContext.isRemote()) { + // Preserve AppSec/IAST request context before dropping the remote parent: when + // extract=IGNORE or RESTART the TagContext parent is nulled before buildSpanContext + // can copy requestContextDataAppSec/Iast into DDSpanContext, causing a WAF/RASP bypass. + if (parentContext instanceof TagContext) { + TagContext tc = (TagContext) parentContext; + if (builderRequestContextDataAppSec == null) { + builderRequestContextDataAppSec = tc.getRequestContextDataAppSec(); + } + if (builderRequestContextDataIast == null) { + builderRequestContextDataIast = tc.getRequestContextDataIast(); + } + } switch (Config.get().getTracePropagationBehaviorExtract()) { case RESTART: links = addParentContextLink(links, parentContext); diff --git a/dd-trace-core/src/test/java/datadog/trace/core/CoreSpanBuilderTest.java b/dd-trace-core/src/test/java/datadog/trace/core/CoreSpanBuilderTest.java index 016b7ceda8f..6e8de0a2b8f 100644 --- a/dd-trace-core/src/test/java/datadog/trace/core/CoreSpanBuilderTest.java +++ b/dd-trace-core/src/test/java/datadog/trace/core/CoreSpanBuilderTest.java @@ -420,6 +420,36 @@ void buildContextFromExtractedContextWithIgnoreBehavior() { assertTrue(span.getLinks().isEmpty()); } + @Test + @WithConfig(key = "trace.propagation.behavior.extract", value = "ignore") + void appSecContextPreservedFromTagContextWithIgnoreBehavior() { + Object appSecData = new Object(); + Object iastData = new Object(); + TagContext tagContext = + new TagContext().withRequestContextDataAppSec(appSecData).withRequestContextDataIast(iastData); + + DDSpan span = (DDSpan) tracer.buildSpan("test", "op name").asChildOf(tagContext).start(); + + assertEquals(appSecData, span.getRequestContext().getData(RequestContextSlot.APPSEC)); + assertEquals(iastData, span.getRequestContext().getData(RequestContextSlot.IAST)); + span.finish(); + } + + @Test + @WithConfig(key = "trace.propagation.behavior.extract", value = "restart") + void appSecContextPreservedFromTagContextWithRestartBehavior() { + Object appSecData = new Object(); + Object iastData = new Object(); + TagContext tagContext = + new TagContext().withRequestContextDataAppSec(appSecData).withRequestContextDataIast(iastData); + + DDSpan span = (DDSpan) tracer.buildSpan("test", "op name").asChildOf(tagContext).start(); + + assertEquals(appSecData, span.getRequestContext().getData(RequestContextSlot.APPSEC)); + assertEquals(iastData, span.getRequestContext().getData(RequestContextSlot.IAST)); + span.finish(); + } + @TableTest({ "scenario | origin | tagMap ", "empty tag map | | [:] ",