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 | | [:] ",