From 072e68a52162cfb38d67a1751e23bcb2f623cd06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20=C3=81lvarez=20=C3=81lvarez?= Date: Wed, 11 Oct 2023 21:54:11 +0200 Subject: [PATCH] Refactor of the PropagationModule and removal of WebModule --- .../StringBuilderAppendBenchmark.java | 2 +- .../StringBuilderBatchBenchmark.java | 4 +- .../StringBuilderInitBenchmark.java | 4 +- .../StringBuilderToStringBenchmark.java | 4 +- .../propagation/StringConcatBenchmark.java | 4 +- .../StringConcatFactoryBatchBenchmark.java | 3 +- .../StringConcatFactoryBenchmark.java | 5 +- .../iast/propagation/StringJoinBenchmark.java | 2 +- .../StringSubsequenceBenchmark.java | 2 +- .../iast/GrpcRequestMessageHandler.java | 6 +- .../com/datadog/iast/IastRequestContext.java | 23 +- .../java/com/datadog/iast/IastSystem.java | 2 - .../java/com/datadog/iast/model/Range.java | 4 +- .../java/com/datadog/iast/model/Source.java | 4 + .../datadog/iast/model/VulnerabilityType.java | 2 +- .../propagation/PropagationModuleImpl.java | 579 ++++++++----- .../datadog/iast/source/WebModuleImpl.java | 156 ---- .../java/com/datadog/iast/taint/Ranges.java | 12 +- .../datadog/iast/taint/TaintedObjects.java | 201 +---- .../taint/TaintedObjectsWithTelemetry.java | 34 +- .../iast/GrpcRequestMessageHandlerTest.groovy | 2 +- .../com/datadog/iast/model/RangeTest.groovy | 2 +- .../model/json/EvidenceEncodingTest.groovy | 4 +- .../json/TaintedObjectEncodingTest.groovy | 4 +- .../json/VulnerabilityEncodingTest.groovy | 2 +- .../propagation/PropagationModuleTest.groovy | 800 +++++++++--------- .../sink/CommandInjectionModuleTest.groovy | 2 +- .../iast/sink/LdapInjectionModuleTest.groovy | 2 +- .../iast/sink/PathTraversalModuleTest.groovy | 2 +- .../iast/sink/SqlInjectionModuleTest.groovy | 2 +- .../datadog/iast/sink/SsrfModuleTest.groovy | 3 +- .../TrustBoundaryViolationModuleTest.groovy | 13 +- .../sink/UnvalidatedRedirectModuleTest.groovy | 5 +- .../iast/sink/XPathInjectionModuleTest.groovy | 2 +- .../datadog/iast/sink/XssModuleTest.groovy | 5 +- .../datadog/iast/source/WebModuleTest.groovy | 130 --- .../com/datadog/iast/taint/RangesTest.groovy | 2 +- .../com/datadog/iast/taint/TaintUtils.groovy | 4 +- .../iast/taint/TaintedObjectTest.groovy | 2 +- .../iast/taint/TaintedObjectsLazyTest.groovy | 70 -- .../iast/taint/TaintedObjectsLogTest.groovy | 25 +- .../iast/taint/TaintedObjectsNoOpTest.groovy | 9 +- .../TelemetryRequestEndedHandlerTest.groovy | 3 +- .../TaintedObjectsWithTelemetryTest.groovy | 6 +- .../IastRequestContextPreparationTrait.groovy | 25 +- .../iast/CookieHeaderInstrumentation.java | 15 +- .../akkahttp/iast/HeaderNameCallSite.java | 2 +- .../HttpHeaderSubclassesInstrumentation.java | 2 +- .../iast/HttpRequestInstrumentation.java | 6 +- .../iast/PathMatcherInstrumentation.java | 8 +- .../iast/RequestContextInstrumentation.java | 2 +- .../akkahttp/iast/UriInstrumentation.java | 14 +- .../iast/helpers/TaintCookieFunction.java | 7 +- .../iast/helpers/TaintFutureHelper.java | 2 +- .../iast/helpers/TaintMapFunction.java | 13 +- .../iast/helpers/TaintMultiMapFunction.java | 18 +- .../helpers/TaintOptionalCookieFunction.java | 11 +- .../helpers/TaintRequestContextFunction.java | 4 +- .../iast/helpers/TaintRequestFunction.java | 4 +- .../iast/helpers/TaintSeqFunction.java | 12 +- .../helpers/TaintSingleParameterFunction.java | 8 +- .../iast/helpers/TaintUnmarshaller.java | 2 +- .../iast/helpers/TaintUriFunction.java | 2 +- .../iast/helpers/TaintParametersFunction.java | 8 +- .../IastHttpMethodBaseInstrumentation.java | 2 +- ...ommonsHttpClientInstrumentationTest.groovy | 2 +- .../StringEscapeUtilsCallSite.java | 10 +- .../StringEscapeUtilsCallSiteTest.groovy | 2 +- .../StringEscapeUtilsCallSite.java | 10 +- .../StringEscapeUtilsCallSiteTest.groovy | 4 +- .../StringEscapeUtilsCallSite.java | 10 +- .../StringEscapeUtilsCallSiteTest.groovy | 4 +- .../freemarker/StringUtilCallSite.java | 2 +- .../freemarker/StringUtilCallSiteTest.groovy | 27 +- .../core/Json1FactoryInstrumentation.java | 2 +- .../core/Json1ParserInstrumentation.java | 2 +- .../Json1FactoryInstrumentationTest.groovy | 8 +- .../Json1ParserInstrumentationTest.groovy | 6 +- .../core/Json2FactoryInstrumentation.java | 2 +- .../core/Json2ParserInstrumentation.java | 2 +- .../core/TokenBufferInstrumentation.java | 2 +- .../Json2FactoryInstrumentationTest.groovy | 12 +- .../Json2ParserInstrumentationTest.groovy | 12 +- .../java/lang/InputStreamInstrumentation.java | 2 +- .../io/InputStreamInstrumentationTest.groovy | 2 +- .../java/lang/StringCallSite.java | 4 +- .../instrumentation/java/net/URICallSite.java | 8 +- .../instrumentation/java/net/URLCallSite.java | 6 +- .../java/net/URLEncoderCallSite.java | 2 +- .../java/net/URICallSIteTest.groovy | 34 +- .../java/net/URLCallSiteTest.groovy | 4 +- .../java/net/URLEncoderCallSiteTest.groovy | 19 +- .../jdbc/src/test/groovy/IastJDBCTest.groovy | 5 +- .../AbstractFormProviderInstrumentation.java | 18 +- ...actParamValueExtractorInstrumentation.java | 2 +- .../jersey/AbstractStringReaderAdvice.java | 4 +- .../jersey/CookieInstrumentation.java | 4 +- .../InboundMessageContextInstrumentation.java | 20 +- ...derInterceptorExecutorInstrumentation.java | 2 +- .../JSONObjectUtilsInstrumentation.java | 8 +- .../josejwt/JWTParserInstrumentation.java | 2 +- .../JWTParserInstrumentationTest.groovy | 12 +- .../ByteBufInputStreamInstrumentation.java | 2 +- .../okhttp2/IastHttpUrlInstrumentation.java | 6 +- .../IastOkHttp2InstrumentationTest.groovy | 4 +- .../okhttp3/IastHttpUrlInstrumentation.java | 2 +- .../IastOkHttp3InstrumentationTest.groovy | 2 +- .../owasp/esapi/EncoderCallSite.java | 16 +- .../owasp/esapi/EncoderCallSiteTest.groovy | 2 +- .../iast/CookieHeaderInstrumentation.java | 17 +- .../pekkohttp/iast/HeaderNameCallSite.java | 2 +- .../HttpHeaderSubclassesInstrumentation.java | 2 +- .../iast/HttpRequestInstrumentation.java | 6 +- .../iast/PathMatcherInstrumentation.java | 5 +- .../iast/RequestContextInstrumentation.java | 2 +- .../pekkohttp/iast/UriInstrumentation.java | 16 +- .../iast/helpers/TaintCookieFunction.java | 7 +- .../iast/helpers/TaintFutureHelper.java | 2 +- .../iast/helpers/TaintMapFunction.java | 13 +- .../iast/helpers/TaintMultiMapFunction.java | 18 +- .../helpers/TaintOptionalCookieFunction.java | 9 +- .../iast/helpers/TaintParametersFunction.java | 8 +- .../helpers/TaintRequestContextFunction.java | 2 +- .../iast/helpers/TaintRequestFunction.java | 2 +- .../iast/helpers/TaintSeqFunction.java | 12 +- .../helpers/TaintSingleParameterFunction.java | 8 +- .../iast/helpers/TaintUnmarshaller.java | 2 +- .../iast/helpers/TaintUriFunction.java | 2 +- .../resteasy/CookieParamInjectorAdvice.java | 8 +- .../resteasy/FormParamInjectorAdvice.java | 6 +- .../resteasy/HeaderParamInjectorAdvice.java | 8 +- .../resteasy/PathParamInjectorAdvice.java | 6 +- .../resteasy/QueryParamInjectorAdvice.java | 6 +- .../HttpServletResponseInstrumentation.java | 4 +- ...pServletResponseInstrumentationTest.groovy | 4 +- .../servlet3/MultipartInstrumentation.java | 30 +- .../callsite/HttpServlet3RequestCallSite.java | 16 +- .../callsite/Servlet3RequestCallSite.java | 16 +- .../MultipartInstrumentationForkedTest.groovy | 37 +- ...let3TestGetParameterInstrumentation.groovy | 12 +- .../test/java/foo/bar/smoketest/MockPart.java | 26 +- .../JakartaHttpServletRequestCallSite.java | 37 +- ...HttpServletRequestInputStreamCallSite.java | 4 +- ...rtaHttpServletResponseInstrumentation.java | 8 +- .../JakartaMultipartInstrumentation.java | 30 +- .../JakartaServletRequestCallSite.java | 41 +- ...pServletResponseInstrumentationTest.groovy | 12 +- ...JakartaMultipartInstrumentationTest.groovy | 31 +- .../JakartaServletRequestCallSiteTest.groovy | 24 +- .../test/java/foo/bar/smoketest/MockPart.java | 26 +- .../servlet/http/CookieInstrumentation.java | 4 +- .../request/HttpServletRequestCallSite.java | 57 +- ...HttpServletRequestInputStreamCallSite.java | 4 +- .../request/ServletRequestCallSite.java | 32 +- .../groovy/CookieInstrumentationTest.groovy | 4 +- .../HttpServletRequestCallSiteTest.groovy | 42 +- .../groovy/ServletRequestCallSiteTest.groovy | 4 +- .../TestGetParameterInstrumentation.groovy | 13 +- .../server/IastWebFluxTest.groovy | 11 +- .../iast/DataBufferAsInputStreamAdvice.java | 2 +- .../server/iast/HandleMatchAdvice.java | 11 +- .../iast/RequestHeaderMapResolveAdvice.java | 17 +- .../server/iast/TaintCookiesAdvice.java | 9 +- .../iast/TaintFluxElementsFunction.java | 2 +- .../server/iast/TaintGetBodyAdvice.java | 2 +- .../iast/TaintHttpHeadersGetAdvice.java | 6 +- .../iast/TaintHttpHeadersGetFirstAdvice.java | 2 +- ...aintHttpHeadersToSingleValueMapAdvice.java | 14 +- .../server/iast/TaintQueryParamsAdvice.java | 11 +- .../server/IastWebFluxTest.groovy | 11 +- ...lateAndMatrixVariablesInstrumentation.java | 13 +- ...ateVariablesUrlHandlerInstrumentation.java | 5 +- .../test/boot/SpringBootBasedTest.groovy | 13 +- .../UrlHandlerMappingTest.groovy | 5 +- .../springweb/EscapeUtilsCallSite.java | 4 +- .../groovy/EscapeUtilsCallSiteTest.groovy | 15 +- .../springweb6/HandleMatchAdvice.java | 13 +- .../InterceptorPreHandleAdvice.java | 5 +- .../boot/SpringBootBasedTest.groovy | 13 +- .../UrlHandlerMappingTest.groovy | 2 +- .../unbescape/EscapeUtilsCallSite.java | 2 +- .../unbescape/EscapeUtilsCallSiteTest.groovy | 23 +- ...tractHttpServerRequestInstrumentation.java | 6 +- .../vertx_3_4/core/BufferInstrumentation.java | 6 +- ...CaseInsensitiveHeadersInstrumentation.java | 39 +- .../core/HeadersAdaptorInstrumentation.java | 46 +- .../Http2ServerRequestInstrumentation.java | 2 +- .../HttpServerRequestInstrumentation.java | 2 +- .../server/CookieImplInstrumentation.java | 5 +- ...IastRoutingContextImplInstrumentation.java | 16 +- .../server/PathParameterPublishingHelper.java | 5 +- .../core/BufferInstrumentationTest.groovy | 4 +- ...sensitiveHeadersInstrumentationTest.groovy | 45 +- .../HeadersAdaptorInstrumentationTest.groovy | 49 +- .../test/groovy/server/IastSourceTest.groovy | 30 +- .../test/java/server/IastSourceVerticle.java | 2 + .../core/VertxHttpHeadersInstrumentation.java | 45 +- ...VertxHttpHeadersInstrumentationTest.groovy | 38 +- ...tractHttpServerRequestInstrumentation.java | 18 +- .../vertx_4_0/core/BufferInstrumentation.java | 6 +- .../core/MultiMapInstrumentation.java | 43 +- .../server/CookieImplInstrumentation.java | 5 +- .../server/PathParameterPublishingHelper.java | 5 +- .../core/BufferInstrumentationTest.groovy | 4 +- .../core/MultiMapInstrumentationTest.groovy | 50 +- .../agent/test/base/HttpServerTest.groovy | 3 +- .../datadog/trace/api/iast/IastContext.java | 44 + .../trace/api/iast/InstrumentationBridge.java | 9 +- .../trace/api/iast/VulnerabilityMarks.java | 2 + .../iast/propagation/PropagationModule.java | 187 +++- .../trace/api/iast/source/WebModule.java | 34 - .../trace/api/iast/IastContextTest.groovy | 67 ++ .../api/iast/InstrumentationBridgeTest.groovy | 2 - 213 files changed, 2252 insertions(+), 2097 deletions(-) delete mode 100644 dd-java-agent/agent-iast/src/main/java/com/datadog/iast/source/WebModuleImpl.java delete mode 100644 dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/source/WebModuleTest.groovy delete mode 100644 dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectsLazyTest.groovy create mode 100644 internal-api/src/main/java/datadog/trace/api/iast/IastContext.java delete mode 100644 internal-api/src/main/java/datadog/trace/api/iast/source/WebModule.java create mode 100644 internal-api/src/test/groovy/datadog/trace/api/iast/IastContextTest.groovy diff --git a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderAppendBenchmark.java b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderAppendBenchmark.java index babe608c0f3..e4bb2ee9f46 100644 --- a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderAppendBenchmark.java +++ b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderAppendBenchmark.java @@ -1,6 +1,6 @@ package com.datadog.iast.propagation; -import static com.datadog.iast.model.Range.NOT_MARKED; +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED; import com.datadog.iast.IastRequestContext; import com.datadog.iast.model.Range; diff --git a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderBatchBenchmark.java b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderBatchBenchmark.java index 1d5f61a43bd..8bb5d4adb20 100644 --- a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderBatchBenchmark.java +++ b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderBatchBenchmark.java @@ -1,5 +1,6 @@ package com.datadog.iast.propagation; +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED; import static java.util.concurrent.TimeUnit.MICROSECONDS; import com.datadog.iast.IastRequestContext; @@ -34,8 +35,7 @@ protected StringBuilderBatchBenchmark.Context initializeContext() { final String value; if (current < limit) { value = - tainted( - context, UUID.randomUUID().toString(), new Range(3, 6, source(), Range.NOT_MARKED)); + tainted(context, UUID.randomUUID().toString(), new Range(3, 6, source(), NOT_MARKED)); } else { value = notTainted(UUID.randomUUID().toString()); } diff --git a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderInitBenchmark.java b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderInitBenchmark.java index a3c832355dc..ec11b927ff2 100644 --- a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderInitBenchmark.java +++ b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderInitBenchmark.java @@ -1,5 +1,7 @@ package com.datadog.iast.propagation; +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED; + import com.datadog.iast.IastRequestContext; import com.datadog.iast.model.Range; import datadog.trace.instrumentation.java.lang.StringBuilderCallSite; @@ -14,7 +16,7 @@ protected Context initializeContext() { final IastRequestContext context = new IastRequestContext(); final String notTainted = notTainted("I am not a tainted string"); final String tainted = - tainted(context, "I am a tainted string", new Range(3, 6, source(), Range.NOT_MARKED)); + tainted(context, "I am a tainted string", new Range(3, 6, source(), NOT_MARKED)); return new Context(context, notTainted, tainted); } diff --git a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderToStringBenchmark.java b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderToStringBenchmark.java index 707e9b04611..1a8468610a4 100644 --- a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderToStringBenchmark.java +++ b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringBuilderToStringBenchmark.java @@ -1,5 +1,7 @@ package com.datadog.iast.propagation; +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED; + import com.datadog.iast.IastRequestContext; import com.datadog.iast.model.Range; import datadog.trace.instrumentation.java.lang.StringBuilderCallSite; @@ -18,7 +20,7 @@ protected Context initializeContext() { tainted( context, new StringBuilder("I am a tainted string builder"), - new Range(5, 7, source(), Range.NOT_MARKED)); + new Range(5, 7, source(), NOT_MARKED)); return new Context(context, notTaintedBuilder, taintedBuilder); } diff --git a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringConcatBenchmark.java b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringConcatBenchmark.java index 4dded1b999e..0858263ff00 100644 --- a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringConcatBenchmark.java +++ b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringConcatBenchmark.java @@ -1,5 +1,7 @@ package com.datadog.iast.propagation; +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED; + import com.datadog.iast.IastRequestContext; import com.datadog.iast.model.Range; import datadog.trace.instrumentation.java.lang.StringCallSite; @@ -13,7 +15,7 @@ protected StringConcatBenchmark.Context initializeContext() { final IastRequestContext context = new IastRequestContext(); final String notTainted = notTainted("I am not a tainted string"); final String tainted = - tainted(context, "I am a tainted string", new Range(3, 5, source(), Range.NOT_MARKED)); + tainted(context, "I am a tainted string", new Range(3, 5, source(), NOT_MARKED)); return new StringConcatBenchmark.Context(context, notTainted, tainted); } diff --git a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringConcatFactoryBatchBenchmark.java b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringConcatFactoryBatchBenchmark.java index 42e1741f361..59f0353ec51 100644 --- a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringConcatFactoryBatchBenchmark.java +++ b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringConcatFactoryBatchBenchmark.java @@ -1,5 +1,6 @@ package com.datadog.iast.propagation; +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED; import static java.util.concurrent.TimeUnit.MICROSECONDS; import com.datadog.iast.IastRequestContext; @@ -54,7 +55,7 @@ protected StringConcatFactoryBatchBenchmark.Context initializeContext() { double current = i / (double) stringCount; final String value; if (current < limit) { - value = tainted(context, "Yep, tainted", new Range(3, 5, source(), Range.NOT_MARKED)); + value = tainted(context, "Yep, tainted", new Range(3, 5, source(), NOT_MARKED)); } else { value = notTainted("Nop, tainted"); } diff --git a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringConcatFactoryBenchmark.java b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringConcatFactoryBenchmark.java index 6f99576fa56..9008bea3ad4 100644 --- a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringConcatFactoryBenchmark.java +++ b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringConcatFactoryBenchmark.java @@ -1,5 +1,7 @@ package com.datadog.iast.propagation; +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED; + import com.datadog.iast.IastRequestContext; import com.datadog.iast.model.Range; import datadog.trace.api.iast.InstrumentationBridge; @@ -13,8 +15,7 @@ public class StringConcatFactoryBenchmark protected StringConcatFactoryBenchmark.Context initializeContext() { final IastRequestContext context = new IastRequestContext(); final String notTainted = notTainted("Nop, tainted"); - final String tainted = - tainted(context, "Yep, tainted", new Range(3, 5, source(), Range.NOT_MARKED)); + final String tainted = tainted(context, "Yep, tainted", new Range(3, 5, source(), NOT_MARKED)); return new StringConcatFactoryBenchmark.Context(context, notTainted, tainted); } diff --git a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringJoinBenchmark.java b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringJoinBenchmark.java index 5b7b4d87a69..3f8bb4b6cfd 100644 --- a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringJoinBenchmark.java +++ b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringJoinBenchmark.java @@ -1,6 +1,6 @@ package com.datadog.iast.propagation; -import static com.datadog.iast.model.Range.NOT_MARKED; +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED; import com.datadog.iast.IastRequestContext; import com.datadog.iast.model.Range; diff --git a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringSubsequenceBenchmark.java b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringSubsequenceBenchmark.java index 78e8f030be8..c8ba76a2add 100644 --- a/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringSubsequenceBenchmark.java +++ b/dd-java-agent/agent-iast/src/jmh/java/com/datadog/iast/propagation/StringSubsequenceBenchmark.java @@ -1,6 +1,6 @@ package com.datadog.iast.propagation; -import static com.datadog.iast.model.Range.NOT_MARKED; +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED; import com.datadog.iast.IastRequestContext; import com.datadog.iast.model.Range; diff --git a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/GrpcRequestMessageHandler.java b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/GrpcRequestMessageHandler.java index 7091ffc23af..8c167fbed7b 100644 --- a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/GrpcRequestMessageHandler.java +++ b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/GrpcRequestMessageHandler.java @@ -2,7 +2,7 @@ import datadog.trace.api.gateway.Flow; import datadog.trace.api.gateway.RequestContext; -import datadog.trace.api.gateway.RequestContextSlot; +import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.InstrumentationBridge; import datadog.trace.api.iast.SourceTypes; import datadog.trace.api.iast.propagation.PropagationModule; @@ -29,9 +29,9 @@ public class GrpcRequestMessageHandler implements BiFunction apply(final RequestContext ctx, final Object o) { final PropagationModule module = InstrumentationBridge.PROPAGATION; if (module != null && o != null) { - final IastRequestContext iastCtx = ctx.getData(RequestContextSlot.IAST); + final IastContext iastCtx = IastContext.Provider.get(ctx); module.taintDeeply( - iastCtx, SourceTypes.GRPC_BODY, o, GrpcRequestMessageHandler::isProtobufArtifact); + iastCtx, o, SourceTypes.GRPC_BODY, GrpcRequestMessageHandler::isProtobufArtifact); } return Flow.ResultFlow.empty(); } diff --git a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/IastRequestContext.java b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/IastRequestContext.java index a5472fb3b00..733a4a97aa6 100644 --- a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/IastRequestContext.java +++ b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/IastRequestContext.java @@ -4,15 +4,15 @@ import com.datadog.iast.overhead.OverheadContext; import com.datadog.iast.taint.TaintedObjects; import datadog.trace.api.gateway.RequestContext; -import datadog.trace.api.gateway.RequestContextSlot; +import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.telemetry.IastMetricCollector; import datadog.trace.api.iast.telemetry.IastMetricCollector.HasMetricCollector; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.Nonnull; import javax.annotation.Nullable; -public class IastRequestContext implements HasMetricCollector { +public class IastRequestContext implements IastContext, HasMetricCollector { private final VulnerabilityBatch vulnerabilityBatch; private final AtomicBoolean spanDataIsSet; @@ -85,6 +85,7 @@ public OverheadContext getOverheadContext() { return overheadContext; } + @Nonnull public TaintedObjects getTaintedObjects() { return taintedObjects; } @@ -97,22 +98,20 @@ public IastMetricCollector getMetricCollector() { @Nullable public static IastRequestContext get() { - return get(AgentTracer.activeSpan()); + return asRequestContext(IastContext.Provider.get()); } @Nullable public static IastRequestContext get(final AgentSpan span) { - if (span == null) { - return null; - } - return get(span.getRequestContext()); + return asRequestContext(IastContext.Provider.get(span)); } @Nullable public static IastRequestContext get(final RequestContext reqCtx) { - if (reqCtx == null) { - return null; - } - return reqCtx.getData(RequestContextSlot.IAST); + return asRequestContext(IastContext.Provider.get(reqCtx)); + } + + private static IastRequestContext asRequestContext(final IastContext ctx) { + return ctx instanceof IastRequestContext ? (IastRequestContext) ctx : null; } } diff --git a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/IastSystem.java b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/IastSystem.java index b5c3e7131f4..23b6b0d00e8 100644 --- a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/IastSystem.java +++ b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/IastSystem.java @@ -23,7 +23,6 @@ import com.datadog.iast.sink.XContentTypeModuleImpl; import com.datadog.iast.sink.XPathInjectionModuleImpl; import com.datadog.iast.sink.XssModuleImpl; -import com.datadog.iast.source.WebModuleImpl; import com.datadog.iast.telemetry.TelemetryRequestEndedHandler; import com.datadog.iast.telemetry.TelemetryRequestStartedHandler; import datadog.trace.api.Config; @@ -90,7 +89,6 @@ private static Consumer registerModule(final Dependencies dependenci private static Stream iastModules() { return Stream.of( - new WebModuleImpl(), new StringModuleImpl(), new FastCodecModule(), new SqlInjectionModuleImpl(), diff --git a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/model/Range.java b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/model/Range.java index 97caac40bd3..a8e1d43b28a 100644 --- a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/model/Range.java +++ b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/model/Range.java @@ -1,5 +1,7 @@ package com.datadog.iast.model; +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED; + import com.datadog.iast.model.json.SourceIndex; import com.datadog.iast.util.Ranged; import java.util.Objects; @@ -9,8 +11,6 @@ public final class Range implements Ranged { - public static final int NOT_MARKED = 0; - private final @Nonnegative int start; private final @Nonnegative int length; private final @Nonnull @SourceIndex Source source; diff --git a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/model/Source.java b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/model/Source.java index 6dc5956adc7..f3f7d5d84e6 100644 --- a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/model/Source.java +++ b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/model/Source.java @@ -11,6 +11,10 @@ public final class Source implements Taintable.Source { private final String name; private final String value; + public Source(final byte origin, final CharSequence name, final CharSequence value) { + this(origin, name == null ? null : name.toString(), value == null ? null : value.toString()); + } + public Source(final byte origin, final String name, final String value) { this.origin = origin; this.name = name; diff --git a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/model/VulnerabilityType.java b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/model/VulnerabilityType.java index deb7888adaf..12f3b276043 100644 --- a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/model/VulnerabilityType.java +++ b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/model/VulnerabilityType.java @@ -1,6 +1,6 @@ package com.datadog.iast.model; -import static com.datadog.iast.model.Range.NOT_MARKED; +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED; import datadog.trace.api.iast.VulnerabilityMarks; import datadog.trace.api.iast.VulnerabilityTypes; diff --git a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/propagation/PropagationModuleImpl.java b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/propagation/PropagationModuleImpl.java index cae08236901..e9e9baa410e 100644 --- a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/propagation/PropagationModuleImpl.java +++ b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/propagation/PropagationModuleImpl.java @@ -1,9 +1,8 @@ package com.datadog.iast.propagation; -import static com.datadog.iast.model.Range.NOT_MARKED; import static com.datadog.iast.taint.Ranges.highestPriorityRange; -import static com.datadog.iast.taint.Tainteds.canBeTainted; import static com.datadog.iast.util.ObjectVisitor.State.CONTINUE; +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED; import com.datadog.iast.IastRequestContext; import com.datadog.iast.model.Range; @@ -11,346 +10,492 @@ import com.datadog.iast.taint.Ranges; import com.datadog.iast.taint.TaintedObject; import com.datadog.iast.taint.TaintedObjects; +import com.datadog.iast.taint.Tainteds; import com.datadog.iast.util.ObjectVisitor; -import com.datadog.iast.util.ObjectVisitor.State; -import com.datadog.iast.util.ObjectVisitor.Visitor; -import datadog.trace.api.iast.SourceTypes; +import datadog.trace.api.Config; +import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.Taintable; import datadog.trace.api.iast.propagation.PropagationModule; -import java.util.Collection; -import java.util.List; -import java.util.Map; import java.util.function.Predicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class PropagationModuleImpl implements PropagationModule { + /** Prevent copy of values bigger than this threshold */ + private static final int MAX_VALUE_LENGTH = Config.get().getIastTruncationMaxValueLength(); + @Override - public void taintIfInputIsTainted(@Nullable final Object toTaint, @Nullable final Object input) { - if (toTaint == null || input == null) { - return; - } - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(true); - final Source source = highestPriorityTaintedSource(taintedObjects, input); - if (source != null) { - taintObject(taintedObjects, toTaint, source); - } + public void taint(@Nullable final Object target, final byte origin) { + taint(target, origin, null); } @Override - public void taintIfInputIsTainted(@Nullable final String toTaint, @Nullable final Object input) { - if (!canBeTainted(toTaint) || input == null) { - return; - } - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(true); - final Source source = highestPriorityTaintedSource(taintedObjects, input); - if (source != null) { - taintString(taintedObjects, toTaint, source); - } + public void taint( + @Nullable final Object target, final byte origin, @Nullable final CharSequence name) { + taint(target, origin, name, sourceValue(target)); } @Override - public void taintIfInputIsTainted( + public void taint( + @Nullable final Object target, final byte origin, - @Nullable final String name, - @Nullable final String toTaint, - @Nullable final Object input) { - if (!canBeTainted(toTaint) || input == null) { + @Nullable final CharSequence name, + @Nullable final CharSequence value) { + if (!canBeTainted(target)) { return; } - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(true); - if (isTainted(taintedObjects, input)) { - taintString(taintedObjects, toTaint, new Source(origin, name, toTaint)); - } + taint(LazyContext.build(), target, origin, name, value); } @Override - public void taintIfInputIsTainted( - final byte origin, - @Nullable final String name, - @Nullable final Collection toTaintCollection, - @Nullable final Object input) { - if (toTaintCollection == null || toTaintCollection.isEmpty() || input == null) { - return; - } - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(true); - if (isTainted(taintedObjects, input)) { - for (final String toTaint : toTaintCollection) { - if (canBeTainted(toTaint)) { - taintString(taintedObjects, toTaint, new Source(origin, name, toTaint)); - } - } - } + public void taint( + @Nullable final IastContext ctx, @Nullable final Object target, final byte origin) { + taint(ctx, target, origin, null); } @Override - public void taintIfInputIsTainted( + public void taint( + @Nullable final IastContext ctx, + @Nullable final Object target, final byte origin, - @Nullable final Collection toTaintCollection, - @Nullable final Object input) { - if (toTaintCollection == null || toTaintCollection.isEmpty() || input == null) { - return; - } - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(true); - if (isTainted(taintedObjects, input)) { - for (final String toTaint : toTaintCollection) { - if (canBeTainted(toTaint)) { - taintString(taintedObjects, toTaint, new Source(origin, toTaint, toTaint)); - } - } - } + @Nullable final CharSequence name) { + taint(ctx, target, origin, name, sourceValue(target)); } @Override - public void taintIfInputIsTainted( + public void taint( + @Nullable final IastContext ctx, + @Nullable final Object target, final byte origin, - @Nullable final List> toTaintCollection, - @Nullable final Object input) { - if (toTaintCollection == null || toTaintCollection.isEmpty() || input == null) { + @Nullable final CharSequence name, + @Nullable final CharSequence value) { + if (!canBeTainted(target)) { return; } - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(true); - if (isTainted(taintedObjects, input)) { - for (final Map.Entry entry : toTaintCollection) { - final String name = entry.getKey(); - if (canBeTainted(name)) { - taintString( - taintedObjects, name, new Source(SourceTypes.namedSource(origin), name, name)); - } - final String toTaint = entry.getValue(); - if (canBeTainted(toTaint)) { - taintString(taintedObjects, toTaint, new Source(origin, name, toTaint)); - } - } - } + internalTaint(ctx, target, new Source(origin, name, sourceValue(target, value)), NOT_MARKED); } @Override - public void taintIfAnyInputIsTainted( - @Nullable final Object toTaint, @Nullable final Object... inputs) { - if (toTaint == null || inputs == null || inputs.length == 0) { + public void taintIfTainted(@Nullable final Object target, @Nullable final Object input) { + taintIfTainted(target, input, false, NOT_MARKED); + } + + @Override + public void taintIfTainted( + @Nullable final Object target, @Nullable final Object input, boolean keepRanges, int mark) { + if (!canBeTainted(target) || !canBeTainted(input)) { return; } - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(true); - for (final Object input : inputs) { - final Source source = highestPriorityTaintedSource(taintedObjects, input); - if (source != null) { - taintObject(taintedObjects, toTaint, source); - return; - } - } + taintIfTainted(LazyContext.build(), target, input, keepRanges, mark); } @Override - public void taint(final byte source, @Nullable final String name, @Nullable final String value) { - if (!canBeTainted(value)) { + public void taintIfTainted( + @Nullable final IastContext ctx, + @Nullable final Object target, + @Nullable final Object input) { + taintIfTainted(ctx, target, input, false, NOT_MARKED); + } + + @Override + public void taintIfTainted( + @Nullable final IastContext ctx, + @Nullable final Object target, + @Nullable final Object input, + boolean keepRanges, + int mark) { + if (!canBeTainted(target) || !canBeTainted(input)) { return; } - final IastRequestContext ctx = IastRequestContext.get(); - if (ctx == null) { - return; + if (keepRanges) { + internalTaint(ctx, target, getRanges(ctx, input), mark); + } else { + internalTaint(ctx, target, highestPrioritySource(ctx, input), mark); } - final TaintedObjects taintedObjects = ctx.getTaintedObjects(); - taintedObjects.taintInputString(value, new Source(source, name, value)); } @Override - public void taint( - @Nullable final Object ctx_, - final byte source, - @Nullable final String name, - @Nullable final String value) { - if (ctx_ == null || !canBeTainted(value)) { - return; - } - final IastRequestContext ctx = (IastRequestContext) ctx_; - final TaintedObjects taintedObjects = ctx.getTaintedObjects(); - taintedObjects.taintInputString(value, new Source(source, name, value)); + public void taintIfTainted( + @Nullable final Object target, @Nullable final Object input, final byte origin) { + taintIfTainted(target, input, origin, null); } @Override - public void taintDeeply(@Nullable final Object ctx_, final byte source, @Nonnull final Object o) { - taintDeeply(ctx_, source, o, ObjectVisitor::inspectClass); + public void taintIfTainted( + @Nullable final Object target, + @Nullable final Object input, + final byte origin, + @Nullable final CharSequence name) { + taintIfTainted(target, input, origin, name, sourceValue(target)); } @Override - public void taintDeeply( - @Nullable final Object ctx_, - final byte source, - @Nonnull final Object o, - @Nonnull final Predicate> classFilter) { - if (ctx_ == null) { + public void taintIfTainted( + @Nullable final Object target, + @Nullable final Object input, + final byte origin, + @Nullable final CharSequence name, + @Nullable final CharSequence value) { + if (!canBeTainted(target) || !canBeTainted(input)) { return; } - final IastRequestContext ctx = (IastRequestContext) ctx_; - final TaintedObjects taintedObjects = ctx.getTaintedObjects(); - ObjectVisitor.visit(o, new TaintingVisitor(taintedObjects, source), classFilter); + taintIfTainted(LazyContext.build(), target, input, origin, name, value); } @Override - public void taintObjectIfInputIsTaintedKeepingRanges( - @Nullable final Object toTaint, @Nullable Object input) { - if (toTaint == null || input == null) { - return; - } - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(true); - final Range[] ranges = getTaintedRanges(taintedObjects, input); - if (ranges != null && ranges.length > 0) { - taintedObjects.taint(toTaint, ranges); - } + public void taintIfTainted( + @Nullable final IastContext ctx, + @Nullable final Object target, + @Nullable final Object input, + final byte origin) { + taintIfTainted(ctx, target, input, origin, null); + } + + @Override + public void taintIfTainted( + @Nullable final IastContext ctx, + @Nullable final Object target, + @Nullable final Object input, + final byte origin, + @Nullable final CharSequence name) { + taintIfTainted(ctx, target, input, origin, name, sourceValue(target)); } @Override - public void taintObject(final byte origin, @Nullable final Object toTaint) { - if (toTaint == null) { + public void taintIfTainted( + @Nullable final IastContext ctx, + @Nullable final Object target, + @Nullable final Object input, + final byte origin, + @Nullable final CharSequence name, + @Nullable final CharSequence value) { + if (!canBeTainted(target) || !canBeTainted(input)) { return; } - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(false); - if (taintedObjects == null) { - return; + if (isTainted(ctx, input)) { + internalTaint(ctx, target, new Source(origin, name, sourceValue(target, value)), NOT_MARKED); } - final Source source = new Source(origin, null, null); - taintObject(taintedObjects, toTaint, source); } @Override - public void taintObjects(final byte origin, @Nullable final Object[] toTaintArray) { - if (toTaintArray == null || toTaintArray.length == 0) { + public void taintIfAnyTainted(@Nullable final Object target, @Nullable final Object[] inputs) { + taintIfAnyTainted(target, inputs, false, NOT_MARKED); + } + + @Override + public void taintIfAnyTainted( + @Nullable final Object target, + @Nullable final Object[] inputs, + final boolean keepRanges, + final int mark) { + if (!canBeTainted(target) || !canBeTainted(inputs)) { return; } - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(true); - final Source source = new Source(origin, null, null); - for (final Object toTaint : toTaintArray) { - taintObject(taintedObjects, toTaint, source); - } + taintIfAnyTainted(LazyContext.build(), target, inputs, keepRanges, mark); } @Override - public boolean isTainted(@Nullable Object obj) { - if (obj instanceof Taintable) { - return ((Taintable) obj).$DD$isTainted(); - } + public void taintIfAnyTainted( + @Nullable final IastContext ctx, + @Nullable final Object target, + @Nullable final Object[] inputs) { + taintIfAnyTainted(ctx, target, inputs, false, NOT_MARKED); + } - if (obj == null) { - return false; + @Override + public void taintIfAnyTainted( + @Nullable final IastContext ctx, + @Nullable final Object target, + @Nullable final Object[] inputs, + final boolean keepRanges, + final int mark) { + if (!canBeTainted(target) || !canBeTainted(inputs)) { + return; + } + if (keepRanges) { + final Range[] ranges = getRangesInArray(ctx, inputs); + if (ranges != null) { + internalTaint(ctx, target, ranges, mark); + } + } else { + final Source source = highestPrioritySourceInArray(ctx, inputs); + if (source != null) { + internalTaint(ctx, target, source, mark); + } } - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(true); - return taintedObjects.get(obj) != null; } @Override - public void taintObjects( - final byte origin, @Nullable final Collection toTaintCollection) { - if (toTaintCollection == null || toTaintCollection.isEmpty()) { + public void taintDeeply( + @Nullable final Object target, final byte origin, final Predicate> classFilter) { + if (!canBeTainted(target)) { return; } - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(true); - final Source source = new Source(origin, null, null); - for (final Object toTaint : toTaintCollection) { - taintObject(taintedObjects, toTaint, source); - } + taintDeeply(LazyContext.build(), target, origin, classFilter); } @Override - public void taintObject( - byte origin, @Nullable String name, @Nullable String value, @Nullable Object t) { - if (t == null) { + public void taintDeeply( + @Nullable final IastContext ctx, + @Nullable final Object target, + final byte origin, + final Predicate> classFilter) { + if (!canBeTainted(target)) { return; } - if (t instanceof Taintable) { - ((Taintable) t).$$DD$setSource(new Source(origin, name, value)); + final TaintedObjects to = getTaintedObjects(ctx); + if (to == null) { + return; + } + if (target instanceof CharSequence) { + internalTaint(ctx, target, new Source(origin, null, sourceValue(target)), NOT_MARKED); } else { - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(); - taintObject(taintedObjects, t, new Source(origin, name, value)); + ObjectVisitor.visit(target, new TaintingVisitor(to, origin), classFilter); } } + @Nullable + @Override + public Taintable.Source findSource(@Nullable final Object target) { + return target == null ? null : findSource(LazyContext.build(), target); + } + + @Nullable @Override - public Taintable.Source firstTaintedSource(@Nullable final Object input) { - if (input == null) { + public Taintable.Source findSource( + @Nullable final IastContext ctx, @Nullable final Object target) { + if (target == null) { return null; } - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(true); - return highestPriorityTaintedSource(taintedObjects, input); + return highestPrioritySource(ctx, target); } @Override - public void taintIfInputIsTaintedWithMarks( - @Nullable final String toTaint, @Nullable final Object input, final int mark) { - if (!canBeTainted(toTaint) || input == null) { - return; + public boolean isTainted(@Nullable final Object target) { + return target != null && isTainted(LazyContext.build(), target); + } + + @Override + public boolean isTainted(@Nullable final IastContext ctx, @Nullable final Object target) { + return target != null && findSource(ctx, target) != null; + } + + /** + * Compares origin and value to check if they are the same reference in order to prevent retaining + * references + * + * @see #sourceValue(Object) + */ + @Nullable + private static CharSequence sourceValue( + @Nullable final Object origin, @Nullable final CharSequence value) { + if (value != null && origin == value) { + return sourceValue(value); + } + return value; + } + + /** + * This method will prevent the code from creating a strong reference to what should remain weak + */ + @Nullable + private static CharSequence sourceValue(@Nullable final Object target) { + if (target instanceof String) { + final String string = (String) target; + if (MAX_VALUE_LENGTH > string.length()) { + return String.copyValueOf(string.toCharArray()); + } else { + final char[] chars = new char[MAX_VALUE_LENGTH]; + string.getChars(0, MAX_VALUE_LENGTH, chars, 0); + return String.copyValueOf(chars); + } + } else if (target instanceof CharSequence) { + final CharSequence charSequence = (CharSequence) target; + if (MAX_VALUE_LENGTH > charSequence.length()) { + return charSequence.toString(); + } else { + final CharSequence subSequence = charSequence.subSequence(0, MAX_VALUE_LENGTH); + return subSequence.toString(); + } + } + return null; + } + + private static boolean canBeTainted(@Nullable final Object target) { + if (target == null) { + return false; + } + if (target instanceof CharSequence) { + return Tainteds.canBeTainted((CharSequence) target); } - final TaintedObjects taintedObjects = TaintedObjects.activeTaintedObjects(true); - final Range[] ranges = getTaintedRanges(taintedObjects, input); - if (ranges != null && ranges.length > 0) { - Range priorityRange = highestPriorityRange(ranges); - taintedObjects.taintInputString( - toTaint, priorityRange.getSource(), priorityRange.getMarks() | mark); + return true; + } + + private static boolean canBeTainted(@Nullable final Object[] target) { + if (target == null || target.length == 0) { + return false; } + return true; } - private static void taintString( - final TaintedObjects taintedObjects, final String toTaint, final Source source) { - taintedObjects.taintInputString(toTaint, source); + @Nullable + private static TaintedObjects getTaintedObjects(final @Nullable IastContext ctx) { + IastRequestContext iastCtx = null; + if (ctx instanceof IastRequestContext) { + iastCtx = (IastRequestContext) ctx; + } else if (ctx instanceof LazyContext) { + iastCtx = ((LazyContext) ctx).getDelegate(); + } + return iastCtx == null ? null : iastCtx.getTaintedObjects(); } - private static void taintObject( - final TaintedObjects taintedObjects, final Object toTaint, final Source source) { - if (toTaint instanceof Taintable) { - ((Taintable) toTaint).$$DD$setSource(source); - } else { - taintedObjects.taintInputObject(toTaint, source); + @Nullable + private static Range[] getRangesInArray( + final @Nullable IastContext ctx, final @Nonnull Object[] objects) { + for (final Object object : objects) { + final Range[] ranges = getRanges(ctx, object); + if (ranges != null) { + return ranges; + } + } + return null; + } + + @Nullable + private static Range[] getRanges(final @Nullable IastContext ctx, final @Nonnull Object object) { + if (object instanceof Taintable) { + final Source source = highestPrioritySource(ctx, object); + if (source == null) { + return null; + } else { + return new Range[] {new Range(0, Integer.MAX_VALUE, source, NOT_MARKED)}; + } } + final TaintedObjects to = getTaintedObjects(ctx); + if (to == null) { + return null; + } + final TaintedObject tainted = to.get(object); + return tainted == null ? null : tainted.getRanges(); } - private static boolean isTainted(final TaintedObjects taintedObjects, final Object object) { - return highestPriorityTaintedSource(taintedObjects, object) != null; + @Nullable + private static Source highestPrioritySourceInArray( + final @Nullable IastContext ctx, final @Nonnull Object[] objects) { + for (final Object object : objects) { + final Source source = highestPrioritySource(ctx, object); + if (source != null) { + return source; + } + } + return null; } - private static Source highestPriorityTaintedSource( - final TaintedObjects taintedObjects, final Object object) { + @Nullable + private static Source highestPrioritySource( + final @Nullable IastContext ctx, final @Nonnull Object object) { if (object instanceof Taintable) { return (Source) ((Taintable) object).$$DD$getSource(); } else { - final TaintedObject tainted = taintedObjects.get(object); - final Range[] ranges = tainted == null ? null : tainted.getRanges(); + final Range[] ranges = getRanges(ctx, object); return ranges != null && ranges.length > 0 ? highestPriorityRange(ranges).getSource() : null; } } - private static Range[] getTaintedRanges( - final TaintedObjects taintedObjects, final Object object) { - if (object instanceof Taintable) { - Source source = (Source) ((Taintable) object).$$DD$getSource(); - if (source == null) { - return null; + private static void internalTaint( + @Nullable final IastContext ctx, + @Nonnull final Object value, + @Nullable final Source source, + int mark) { + if (source == null) { + return; + } + if (value instanceof Taintable) { + ((Taintable) value).$$DD$setSource(source); + } else { + final TaintedObjects to = getTaintedObjects(ctx); + if (to == null) { + return; + } + if (value instanceof CharSequence) { + to.taint(value, Ranges.forCharSequence((CharSequence) value, source, mark)); } else { - return Ranges.forObject(source, NOT_MARKED); + to.taint(value, Ranges.forObject(source, mark)); } + } + } + + private static void internalTaint( + @Nullable final IastContext ctx, + @Nonnull final Object value, + @Nullable final Range[] ranges, + final int mark) { + if (ranges == null || ranges.length == 0) { + return; + } + if (value instanceof Taintable) { + ((Taintable) value).$$DD$setSource(ranges[0].getSource()); } else { - final TaintedObject tainted = taintedObjects.get(object); - return tainted == null ? null : tainted.getRanges(); + final TaintedObjects to = getTaintedObjects(ctx); + if (to != null) { + final Range[] markedRanges = markRanges(ranges, mark); + to.taint(value, markedRanges); + } + } + } + + @Nonnull + private static Range[] markRanges(@Nonnull final Range[] ranges, final int mark) { + if (mark == NOT_MARKED) { + return ranges; + } + final Range[] result = new Range[ranges.length]; + for (int i = 0; i < ranges.length; i++) { + final Range range = ranges[i]; + final int newMark = range.getMarks() | mark; + result[i] = new Range(range.getStart(), range.getLength(), range.getSource(), newMark); + } + return result; + } + + private static class LazyContext implements IastContext { + + private boolean fetched; + private IastRequestContext delegate; + + private IastRequestContext getDelegate() { + if (!fetched) { + fetched = true; + delegate = IastRequestContext.get(); + } + return delegate; + } + + public static IastContext build() { + return new LazyContext(); } } - private static class TaintingVisitor implements Visitor { + private static class TaintingVisitor implements ObjectVisitor.Visitor { private final TaintedObjects taintedObjects; - private final byte source; + private final byte origin; - private TaintingVisitor(final TaintedObjects taintedObjects, final byte source) { + private TaintingVisitor(@Nonnull final TaintedObjects taintedObjects, final byte origin) { this.taintedObjects = taintedObjects; - this.source = source; + this.origin = origin; } @Nonnull @Override - public State visit(@Nonnull final String path, @Nonnull final Object value) { - if (value instanceof String) { - final String stringValue = (String) value; - if (canBeTainted(stringValue)) { - taintedObjects.taintInputString(stringValue, new Source(source, path, stringValue)); + public ObjectVisitor.State visit(@Nonnull final String path, @Nonnull final Object value) { + if (value instanceof CharSequence) { + final CharSequence charSequence = (CharSequence) value; + if (canBeTainted(charSequence)) { + final Source source = new Source(origin, path, sourceValue(value)); + taintedObjects.taint( + charSequence, Ranges.forCharSequence(charSequence, source, NOT_MARKED)); } } return CONTINUE; diff --git a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/source/WebModuleImpl.java b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/source/WebModuleImpl.java deleted file mode 100644 index 1b4ba92fd35..00000000000 --- a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/source/WebModuleImpl.java +++ /dev/null @@ -1,156 +0,0 @@ -package com.datadog.iast.source; - -import static com.datadog.iast.taint.Tainteds.canBeTainted; - -import com.datadog.iast.IastRequestContext; -import com.datadog.iast.model.Source; -import com.datadog.iast.taint.TaintedObjects; -import datadog.trace.api.iast.SourceTypes; -import datadog.trace.api.iast.source.WebModule; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.Map; -import javax.annotation.Nullable; - -public class WebModuleImpl implements WebModule { - - @Override - public void onParameterNames(@Nullable final Collection paramNames) { - onNamed(paramNames, SourceTypes.REQUEST_PARAMETER_NAME); - } - - @Override - public void onParameterValues( - @Nullable final String paramName, @Nullable final String[] paramValues) { - onNamed(paramName, paramValues, SourceTypes.REQUEST_PARAMETER_VALUE); - } - - @Override - public void onParameterValues( - @Nullable final String paramName, @Nullable final Collection paramValues) { - onNamed(paramName, paramValues, SourceTypes.REQUEST_PARAMETER_VALUE); - } - - @Override - public void onParameterValues(@Nullable final Map values) { - onNamed(values, SourceTypes.REQUEST_PARAMETER_VALUE); - } - - @Override - public void onHeaderNames(@Nullable final Collection headerNames) { - onNamed(headerNames, SourceTypes.REQUEST_HEADER_NAME); - } - - @Override - public void onHeaderValues( - @Nullable final String headerName, @Nullable final Collection headerValues) { - onNamed(headerName, headerValues, SourceTypes.REQUEST_HEADER_VALUE); - } - - @Override - public void onMultipartNames(@Nullable final Collection headerNames) { - onNamed(headerNames, SourceTypes.REQUEST_MULTIPART_PARAMETER); - } - - @Override - public void onMultipartValues( - @Nullable final String headerName, @Nullable final Collection headerValues) { - onNamed(headerName, headerValues, SourceTypes.REQUEST_MULTIPART_PARAMETER); - } - - @Override - public void onCookieNames(@Nullable Iterable cookieNames) { - onNamed(cookieNames, SourceTypes.REQUEST_COOKIE_NAME); - } - - @Override - public void onGetPathInfo(@Nullable String s) { - onNamed(Collections.singleton(s), SourceTypes.REQUEST_PATH); - } - - private static void onNamed(@Nullable final Iterable names, final byte source) { - if (names == null) { - return; - } - Iterator iterator = names.iterator(); - if (!iterator.hasNext()) { - return; - } - - final IastRequestContext ctx = IastRequestContext.get(); - if (ctx == null) { - return; - } - final TaintedObjects taintedObjects = ctx.getTaintedObjects(); - do { - String name = iterator.next(); - if (canBeTainted(name)) { - taintedObjects.taintInputString(name, new Source(source, name, name)); - } - } while (iterator.hasNext()); - } - - private static void onNamed( - @Nullable final String name, @Nullable final Iterable values, final byte source) { - if (values == null) { - return; - } - Iterator iterator = values.iterator(); - if (!iterator.hasNext()) { - return; - } - - final IastRequestContext ctx = IastRequestContext.get(); - if (ctx == null) { - return; - } - final TaintedObjects taintedObjects = ctx.getTaintedObjects(); - do { - String value = iterator.next(); - if (canBeTainted(value)) { - taintedObjects.taintInputString(value, new Source(source, name, value)); - } - } while (iterator.hasNext()); - } - - private static void onNamed( - @Nullable final String name, @Nullable final String[] values, final byte source) { - if (values == null || values.length == 0) { - return; - } - final IastRequestContext ctx = IastRequestContext.get(); - if (ctx == null) { - return; - } - final TaintedObjects taintedObjects = ctx.getTaintedObjects(); - for (final String value : values) { - if (canBeTainted(value)) { - taintedObjects.taintInputString(value, new Source(source, name, value)); - } - } - } - - private static void onNamed(@Nullable final Map values, final byte source) { - if (values == null || values.isEmpty()) { - return; - } - final IastRequestContext ctx = IastRequestContext.get(); - if (ctx == null) { - return; - } - final TaintedObjects taintedObjects = ctx.getTaintedObjects(); - final byte nameSource = SourceTypes.namedSource(source); - for (final Map.Entry entry : values.entrySet()) { - final String name = entry.getKey(); - if (canBeTainted(name)) { - taintedObjects.taintInputString(name, new Source(nameSource, name, name)); - } - for (final String value : entry.getValue()) { - if (canBeTainted(value)) { - taintedObjects.taintInputString(value, new Source(source, name, value)); - } - } - } - } -} diff --git a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/taint/Ranges.java b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/taint/Ranges.java index aa6543d1b59..dd39adc8283 100644 --- a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/taint/Ranges.java +++ b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/taint/Ranges.java @@ -1,7 +1,7 @@ package com.datadog.iast.taint; -import static com.datadog.iast.model.Range.NOT_MARKED; import static com.datadog.iast.taint.TaintedObject.MAX_RANGE_COUNT; +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED; import com.datadog.iast.model.Range; import com.datadog.iast.model.Source; @@ -41,11 +41,20 @@ public Range[] ranges(final Object value) { private Ranges() {} + public static Range[] forCharSequence( + final @Nonnull CharSequence obj, final @Nonnull Source source) { + return forCharSequence(obj, source, NOT_MARKED); + } + public static Range[] forCharSequence( final @Nonnull CharSequence obj, final @Nonnull Source source, final int mark) { return new Range[] {new Range(0, obj.length(), source, mark)}; } + public static Range[] forObject(final @Nonnull Source source) { + return forObject(source, NOT_MARKED); + } + public static Range[] forObject(final @Nonnull Source source, final int mark) { return new Range[] {new Range(0, Integer.MAX_VALUE, source, mark)}; } @@ -175,6 +184,7 @@ public static int[] getIncludedRangesInterval( return new int[] {start, end}; } + @Nonnull public static Range highestPriorityRange(@Nonnull final Range[] ranges) { /* * This approach is better but not completely correct ideally the highest priority should use the following patterns: diff --git a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/taint/TaintedObjects.java b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/taint/TaintedObjects.java index a292802992a..dc73bd3712b 100644 --- a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/taint/TaintedObjects.java +++ b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/taint/TaintedObjects.java @@ -1,13 +1,10 @@ package com.datadog.iast.taint; -import static com.datadog.iast.model.Range.NOT_MARKED; import static datadog.trace.api.ConfigDefaults.DEFAULT_IAST_MAX_CONCURRENT_REQUESTS; import static java.util.Collections.emptyIterator; -import com.datadog.iast.IastRequestContext; import com.datadog.iast.IastSystem; import com.datadog.iast.model.Range; -import com.datadog.iast.model.Source; import com.datadog.iast.model.json.TaintedObjectEncoding; import datadog.trace.api.Config; import java.util.ArrayList; @@ -16,19 +13,17 @@ import java.util.UUID; import java.util.concurrent.ArrayBlockingQueue; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +@SuppressWarnings("UnusedReturnValue") public interface TaintedObjects extends Iterable { - TaintedObject taintInputString(@Nonnull String obj, @Nonnull Source source, int mark); - - TaintedObject taintInputCharSequence(@Nonnull CharSequence obj, @Nonnull Source source, int mark); - - TaintedObject taintInputObject(@Nonnull Object obj, @Nonnull Source source, int mark); - + @Nullable TaintedObject taint(@Nonnull Object obj, @Nonnull Range[] ranges); + @Nullable TaintedObject get(@Nonnull Object obj); void release(); @@ -39,18 +34,6 @@ public interface TaintedObjects extends Iterable { boolean isFlat(); - default TaintedObject taintInputString(@Nonnull String obj, @Nonnull Source source) { - return taintInputString(obj, source, NOT_MARKED); - } - - default TaintedObject taintInputCharSequence(@Nonnull CharSequence obj, @Nonnull Source source) { - return taintInputCharSequence(obj, source, NOT_MARKED); - } - - default TaintedObject taintInputObject(@Nonnull Object obj, @Nonnull Source source) { - return taintInputObject(obj, source, NOT_MARKED); - } - static TaintedObjects acquire() { TaintedObjectsImpl taintedObjects = TaintedObjectsImpl.pool.poll(); if (taintedObjects == null) { @@ -59,22 +42,6 @@ static TaintedObjects acquire() { return IastSystem.DEBUG ? new TaintedObjectsDebugAdapter(taintedObjects) : taintedObjects; } - static TaintedObjects activeTaintedObjects(boolean lazy) { - if (lazy) { - return new LazyTaintedObjects(); - } else { - final IastRequestContext ctx = IastRequestContext.get(); - if (ctx != null) { - return ctx.getTaintedObjects(); - } - return null; - } - } - - static TaintedObjects activeTaintedObjects() { - return activeTaintedObjects(false); - } - class TaintedObjectsImpl implements TaintedObjects { private static final ArrayBlockingQueue pool = @@ -92,35 +59,6 @@ private TaintedObjectsImpl(final @Nonnull TaintedMap map) { this.map = map; } - @Override - public TaintedObject taintInputString( - final @Nonnull String obj, final @Nonnull Source source, final int mark) { - final TaintedObject tainted = - new TaintedObject( - obj, Ranges.forCharSequence(obj, source, mark), map.getReferenceQueue()); - map.put(tainted); - return tainted; - } - - @Override - public TaintedObject taintInputCharSequence( - final @Nonnull CharSequence obj, final @Nonnull Source source, final int mark) { - final TaintedObject tainted = - new TaintedObject( - obj, Ranges.forCharSequence(obj, source, mark), map.getReferenceQueue()); - map.put(tainted); - return tainted; - } - - @Override - public TaintedObject taintInputObject( - @Nonnull Object obj, @Nonnull Source source, final int mark) { - final TaintedObject tainted = - new TaintedObject(obj, Ranges.forObject(source, mark), map.getReferenceQueue()); - map.put(tainted); - return tainted; - } - @Override public TaintedObject taint(final @Nonnull Object obj, final @Nonnull Range[] ranges) { final TaintedObject tainted = new TaintedObject(obj, ranges, map.getReferenceQueue()); @@ -154,13 +92,14 @@ public boolean isFlat() { return map.isFlat(); } + @Nonnull @Override public Iterator iterator() { return map.iterator(); } } - class TaintedObjectsDebugAdapter implements TaintedObjects { + final class TaintedObjectsDebugAdapter implements TaintedObjects { static final Logger LOGGER = LoggerFactory.getLogger(TaintedObjects.class); private final TaintedObjectsImpl delegated; @@ -172,30 +111,7 @@ public TaintedObjectsDebugAdapter(final TaintedObjectsImpl delegated) { LOGGER.debug("new: id={}", id); } - @Override - public TaintedObject taintInputString( - final @Nonnull String obj, final @Nonnull Source source, final int mark) { - final TaintedObject tainted = delegated.taintInputString(obj, source, mark); - logTainted(tainted); - return tainted; - } - - @Override - public TaintedObject taintInputCharSequence( - @Nonnull CharSequence obj, @Nonnull Source source, int mark) { - final TaintedObject tainted = delegated.taintInputCharSequence(obj, source, mark); - logTainted(tainted); - return tainted; - } - - @Override - public TaintedObject taintInputObject( - @Nonnull Object obj, @Nonnull Source source, final int mark) { - final TaintedObject tainted = delegated.taintInputObject(obj, source, mark); - logTainted(tainted); - return tainted; - } - + @Nullable @Override public TaintedObject taint(final @Nonnull Object obj, final @Nonnull Range[] ranges) { final TaintedObject tainted = delegated.taint(obj, ranges); @@ -203,6 +119,7 @@ public TaintedObject taint(final @Nonnull Object obj, final @Nonnull Range[] ran return tainted; } + @Nullable @Override public TaintedObject get(final @Nonnull Object obj) { return delegated.get(obj); @@ -239,6 +156,7 @@ public boolean isFlat() { return delegated.isFlat(); } + @Nonnull @Override public Iterator iterator() { return delegated.iterator(); @@ -255,110 +173,17 @@ private void logTainted(final TaintedObject tainted) { } } - class LazyTaintedObjects implements TaintedObjects { - private boolean fetched = false; - private TaintedObjects taintedObjects; - - @Override - public TaintedObject taintInputString( - @Nonnull final String obj, @Nonnull final Source source, final int mark) { - final TaintedObjects to = getTaintedObjects(); - return to == null ? null : to.taintInputString(obj, source, mark); - } - - @Override - public TaintedObject taintInputCharSequence( - @Nonnull CharSequence obj, @Nonnull Source source, int mark) { - final TaintedObjects to = getTaintedObjects(); - return to == null ? null : to.taintInputCharSequence(obj, source, mark); - } - - @Override - public TaintedObject taintInputObject( - @Nonnull final Object obj, @Nonnull final Source source, final int mark) { - final TaintedObjects to = getTaintedObjects(); - return to == null ? null : to.taintInputObject(obj, source, mark); - } - - @Override - public TaintedObject taint(@Nonnull final Object obj, @Nonnull final Range[] ranges) { - final TaintedObjects to = getTaintedObjects(); - return to == null ? null : to.taint(obj, ranges); - } - - @Override - public TaintedObject get(@Nonnull final Object obj) { - final TaintedObjects to = getTaintedObjects(); - return to == null ? null : to.get(obj); - } - - @Override - public void release() { - final TaintedObjects to = getTaintedObjects(); - if (to != null) { - to.release(); - } - } - - @Override - public Iterator iterator() { - final TaintedObjects to = getTaintedObjects(); - return to != null ? to.iterator() : emptyIterator(); - } - - @Override - public int getEstimatedSize() { - final TaintedObjects to = getTaintedObjects(); - return to != null ? to.getEstimatedSize() : 0; - } - - @Override - public boolean isFlat() { - final TaintedObjects to = getTaintedObjects(); - return to != null && to.isFlat(); - } - - @Override - public int count() { - final TaintedObjects to = getTaintedObjects(); - return to != null ? to.count() : 0; - } - - private TaintedObjects getTaintedObjects() { - if (!fetched) { - fetched = true; - taintedObjects = activeTaintedObjects(); - } - return taintedObjects; - } - } - - enum NoOp implements TaintedObjects { - INSTANCE; - - @Override - public TaintedObject taintInputString( - @Nonnull final String obj, @Nonnull final Source source, final int mark) { - return null; - } - - @Override - public TaintedObject taintInputCharSequence( - @Nonnull CharSequence obj, @Nonnull Source source, int mark) { - return null; - } + final class NoOp implements TaintedObjects { - @Override - public TaintedObject taintInputObject( - @Nonnull final Object obj, @Nonnull final Source source, final int mark) { - return null; - } + public static final TaintedObjects INSTANCE = new NoOp(); + @Nullable @Override public TaintedObject taint(@Nonnull final Object obj, @Nonnull final Range[] ranges) { return null; } + @Nullable @Override public TaintedObject get(@Nonnull final Object obj) { return null; diff --git a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/telemetry/taint/TaintedObjectsWithTelemetry.java b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/telemetry/taint/TaintedObjectsWithTelemetry.java index 951bd9ec15f..475a9fd795e 100644 --- a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/telemetry/taint/TaintedObjectsWithTelemetry.java +++ b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/telemetry/taint/TaintedObjectsWithTelemetry.java @@ -6,13 +6,13 @@ import com.datadog.iast.IastRequestContext; import com.datadog.iast.model.Range; -import com.datadog.iast.model.Source; import com.datadog.iast.taint.TaintedObject; import com.datadog.iast.taint.TaintedObjects; import datadog.trace.api.iast.telemetry.IastMetricCollector; import datadog.trace.api.iast.telemetry.Verbosity; import java.util.Iterator; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class TaintedObjectsWithTelemetry implements TaintedObjects { @@ -46,26 +46,6 @@ public void initContext(final IastRequestContext ctx) { this.ctx = ctx; } - @Override - public TaintedObject taintInputString( - @Nonnull String obj, @Nonnull Source source, final int mark) { - final TaintedObject result = delegate.taintInputString(obj, source, mark); - if (debug) { - IastMetricCollector.add(EXECUTED_TAINTED, 1, ctx); - } - return result; - } - - @Override - public TaintedObject taintInputCharSequence( - @Nonnull CharSequence obj, @Nonnull Source source, int mark) { - final TaintedObject result = delegate.taintInputCharSequence(obj, source, mark); - if (debug) { - IastMetricCollector.add(EXECUTED_TAINTED, 1, ctx); - } - return result; - } - @Override public TaintedObject taint(@Nonnull Object obj, @Nonnull Range[] ranges) { final TaintedObject result = delegate.taint(obj, ranges); @@ -75,16 +55,7 @@ public TaintedObject taint(@Nonnull Object obj, @Nonnull Range[] ranges) { return result; } - @Override - public TaintedObject taintInputObject( - @Nonnull Object obj, @Nonnull Source source, final int mark) { - final TaintedObject result = delegate.taintInputObject(obj, source, mark); - if (debug) { - IastMetricCollector.add(EXECUTED_TAINTED, 1, ctx); - } - return result; - } - + @Nullable @Override public TaintedObject get(@Nonnull Object obj) { return delegate.get(obj); @@ -102,6 +73,7 @@ public void release() { } } + @Nonnull @Override public Iterator iterator() { return delegate.iterator(); diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/GrpcRequestMessageHandlerTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/GrpcRequestMessageHandlerTest.groovy index d3c31196f87..e4081f96d7d 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/GrpcRequestMessageHandlerTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/GrpcRequestMessageHandlerTest.groovy @@ -62,7 +62,7 @@ class GrpcRequestMessageHandlerTest extends DDSpecification { handler.apply(ctx, target) then: - 1 * propagation.taintDeeply(iastCtx, SourceTypes.GRPC_BODY, target, _ as Predicate>) + 1 * propagation.taintDeeply(iastCtx, target, SourceTypes.GRPC_BODY, _ as Predicate>) } void 'the handler only takes into account protobuf v.#protobufVersion related messages'() { diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/RangeTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/RangeTest.groovy index 5a3561fe7f8..c4992278756 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/RangeTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/RangeTest.groovy @@ -35,7 +35,7 @@ class RangeTest extends DDSpecification { def 'shift zero'() { given: final source = new Source(SourceTypes.NONE, null, null) - final orig = new Range(0, 1, source, Range.NOT_MARKED) + final orig = new Range(0, 1, source, VulnerabilityMarks.NOT_MARKED) when: final result = orig.shift(0) diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/json/EvidenceEncodingTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/json/EvidenceEncodingTest.groovy index c946e53f70a..9b3d67e9339 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/json/EvidenceEncodingTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/json/EvidenceEncodingTest.groovy @@ -10,6 +10,8 @@ import datadog.trace.test.util.DDSpecification import org.skyscreamer.jsonassert.JSONAssert import spock.lang.Shared +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED + class EvidenceEncodingTest extends DDSpecification { private static final List SOURCES_SUITE = (0..2).collect { new Source((byte) it, "name$it", "value$it") } @@ -65,7 +67,7 @@ class EvidenceEncodingTest extends DDSpecification { } private static Range range(final int start, final int length, final Source source) { - return new Range(start, length, source, Range.NOT_MARKED) + return new Range(start, length, source, NOT_MARKED) } private static Source source(final int index) { diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/json/TaintedObjectEncodingTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/json/TaintedObjectEncodingTest.groovy index 55956358225..dfbc0fb8275 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/json/TaintedObjectEncodingTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/json/TaintedObjectEncodingTest.groovy @@ -10,6 +10,8 @@ import org.skyscreamer.jsonassert.JSONAssert import java.lang.ref.ReferenceQueue +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED + class TaintedObjectEncodingTest extends DDSpecification { @Override @@ -87,7 +89,7 @@ class TaintedObjectEncodingTest extends DDSpecification { private TaintedObject taintedObject(final String value, final byte sourceType, final String sourceName, final String sourceValue) { return new TaintedObject( value, - [new Range(0, value.length(), new Source(sourceType, sourceName, sourceValue), Range.NOT_MARKED)] as Range[], + [new Range(0, value.length(), new Source(sourceType, sourceName, sourceValue), NOT_MARKED)] as Range[], Mock(ReferenceQueue)) } } diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/json/VulnerabilityEncodingTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/json/VulnerabilityEncodingTest.groovy index eaff6eca472..4ade1c08a8d 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/json/VulnerabilityEncodingTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/model/json/VulnerabilityEncodingTest.groovy @@ -16,7 +16,7 @@ import org.skyscreamer.jsonassert.JSONAssert import java.util.regex.Matcher import java.util.regex.Pattern -import static com.datadog.iast.model.Range.NOT_MARKED +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED class VulnerabilityEncodingTest extends DDSpecification { diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/propagation/PropagationModuleTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/propagation/PropagationModuleTest.groovy index c8b45e183ec..b0dceae6377 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/propagation/PropagationModuleTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/propagation/PropagationModuleTest.groovy @@ -4,6 +4,9 @@ import com.datadog.iast.IastModuleImplTestBase import com.datadog.iast.IastRequestContext import com.datadog.iast.model.Range import com.datadog.iast.model.Source +import com.datadog.iast.taint.Ranges +import com.datadog.iast.taint.TaintedObject +import datadog.trace.api.Config import datadog.trace.api.gateway.RequestContext import datadog.trace.api.gateway.RequestContextSlot import datadog.trace.api.iast.SourceTypes @@ -11,25 +14,19 @@ import datadog.trace.api.iast.Taintable import datadog.trace.api.iast.VulnerabilityMarks import datadog.trace.api.iast.propagation.PropagationModule import datadog.trace.bootstrap.instrumentation.api.AgentSpan -import groovy.transform.CompileDynamic +import org.junit.Assume -import static com.datadog.iast.model.Range.NOT_MARKED -import static com.datadog.iast.taint.TaintUtils.addFromTaintFormat -import static com.datadog.iast.taint.TaintUtils.fromTaintFormat -import static datadog.trace.api.iast.VulnerabilityMarks.SQL_INJECTION_MARK -import static datadog.trace.api.iast.VulnerabilityMarks.XPATH_INJECTION_MARK -import static datadog.trace.api.iast.VulnerabilityMarks.XSS_MARK +import static com.datadog.iast.taint.Ranges.highestPriorityRange +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED -@CompileDynamic class PropagationModuleTest extends IastModuleImplTestBase { private PropagationModule module - private List objectHolder + private IastRequestContext ctx - def setup() { + void setup() { module = new PropagationModuleImpl() - objectHolder = [] ctx = new IastRequestContext() final reqCtx = Mock(RequestContext) { getData(RequestContextSlot.IAST) >> ctx @@ -40,54 +37,49 @@ class PropagationModuleTest extends IastModuleImplTestBase { tracer.activeSpan() >> span } - void '#method null or empty'() { - when: + void '#method(#args) not taintable'() { + when: 'there is no context by default' module.&"$method".call(args.toArray()) - then: + then: 'no mock calls should happen' + 0 * _ + + when: 'there is a context' + args.add(0, ctx) + module.&"$method".call(args.toArray()) + + then: 'no mock calls should happen' 0 * _ where: - method | args - 'taintIfInputIsTainted' | [null, null] - 'taintIfInputIsTainted' | [null, new Object()] - 'taintIfInputIsTainted' | [null, 'test'] - 'taintIfInputIsTainted' | [new Object(), null] - 'taintIfInputIsTainted' | [null as String, null] - 'taintIfInputIsTainted' | ['', null] - 'taintIfInputIsTainted' | ['', new Object()] - 'taintIfInputIsTainted' | [null as String, new Object()] - 'taintIfInputIsTainted' | ['test', null] - 'taintIfInputIsTainted' | [null as String, 'test'] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', null as String, null] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', '', null] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', '', new Object()] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', null as String, new Object()] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', 'test', null] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', null as String, 'test'] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, [].toSet(), 'test'] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, ['test'].toSet(), null] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, [:].entrySet().toList(), 'test'] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, [key: "value"].entrySet().toList(), null] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', null as Collection, 'test'] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', [], 'test'] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', ['value'], null] - 'taintObjectIfInputIsTaintedKeepingRanges' | [null, new Object()] - 'taintObjectIfInputIsTaintedKeepingRanges' | [new Object(), null] - 'taintIfAnyInputIsTainted' | [null, null] - 'taintIfAnyInputIsTainted' | [null, [].toArray()] - 'taintIfAnyInputIsTainted' | ['test', [].toArray()] - 'taint' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', null as String] - 'taint' | [SourceTypes.REQUEST_PARAMETER_VALUE, null as String, null as String] - 'taint' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', ''] - 'taintObjects' | [SourceTypes.REQUEST_PARAMETER_VALUE, null as Object[]] - 'taintObjects' | [SourceTypes.REQUEST_PARAMETER_VALUE, [] as Object[]] - 'taintObjects' | [SourceTypes.REQUEST_PARAMETER_VALUE, null as Collection] - 'taintIfInputIsTaintedWithMarks' | ['', null, VulnerabilityMarks.XSS_MARK] - 'taintIfInputIsTaintedWithMarks' | ['', new Object(), VulnerabilityMarks.XSS_MARK] - 'taintIfInputIsTaintedWithMarks' | [null as String, new Object(), VulnerabilityMarks.XSS_MARK] - 'taintIfInputIsTaintedWithMarks' | ['test', null, VulnerabilityMarks.XSS_MARK] - 'taintIfInputIsTaintedWithMarks' | [null as String, 'test', VulnerabilityMarks.XSS_MARK] + method | args + 'taint' | [null, SourceTypes.REQUEST_PARAMETER_VALUE] + 'taint' | [null, SourceTypes.REQUEST_PARAMETER_VALUE, 'name'] + 'taint' | [null, SourceTypes.REQUEST_PARAMETER_VALUE, 'name', 'value'] + 'taintIfTainted' | [null, 'test'] + 'taintIfTainted' | ['test', null] + 'taintIfTainted' | [null, 'test', false, NOT_MARKED] + 'taintIfTainted' | ['test', null, false, NOT_MARKED] + 'taintIfTainted' | [null, 'test'] + 'taintIfTainted' | ['test', null] + 'taintIfTainted' | [null, 'test', SourceTypes.REQUEST_PARAMETER_VALUE] + 'taintIfTainted' | ['test', null, SourceTypes.REQUEST_PARAMETER_VALUE] + 'taintIfTainted' | [null, 'test', SourceTypes.REQUEST_PARAMETER_VALUE, 'name'] + 'taintIfTainted' | ['test', null, SourceTypes.REQUEST_PARAMETER_VALUE, 'name'] + 'taintIfTainted' | [null, 'test', SourceTypes.REQUEST_PARAMETER_VALUE, 'name', 'value'] + 'taintIfTainted' | ['test', null, SourceTypes.REQUEST_PARAMETER_VALUE, 'name', 'value'] + 'taintIfAnyTainted' | [null, ['test'] as Object[]] + 'taintIfAnyTainted' | ['test', null] + 'taintIfAnyTainted' | ['test', [] as Object[]] + 'taintDeeply' | [ + null, + SourceTypes.REQUEST_PARAMETER_VALUE, + { + true + } + ] + 'findSource' | [null] + 'isTainted' | [null] } void '#method without span'() { @@ -99,379 +91,264 @@ class PropagationModuleTest extends IastModuleImplTestBase { 0 * _ where: - method | args - 'taintIfInputIsTainted' | [new Object(), new Object()] - 'taintIfInputIsTainted' | [new Object(), 'test'] - 'taintIfInputIsTainted' | ['test', new Object()] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', 'value', new Object()] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', ['value'], new Object()] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, ['value'].toSet(), new Object()] - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, [key: 'value'].entrySet().toList(), new Object()] - 'taintObjectIfInputIsTaintedKeepingRanges' | [new Object(), new Object()] - 'taintIfAnyInputIsTainted' | ['value', ['test', 'test2'].toArray()] - 'taint' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', 'value'] - 'taintObjects' | [SourceTypes.REQUEST_PARAMETER_VALUE, [new Object()] as Object[]] - 'taintObjects' | [SourceTypes.REQUEST_PARAMETER_VALUE, [new Object()]] - 'taintObjects' | [SourceTypes.REQUEST_PARAMETER_VALUE, [new Object()] as Collection] - 'taintIfInputIsTaintedWithMarks' | ['test', new Object(), VulnerabilityMarks.XSS_MARK] + method | args + 'taint' | ['test', SourceTypes.REQUEST_PARAMETER_VALUE] + 'taint' | ['test', SourceTypes.REQUEST_PARAMETER_VALUE, 'name'] + 'taint' | ['test', SourceTypes.REQUEST_PARAMETER_VALUE, 'name', 'value'] + 'taintIfTainted' | ['test', 'test'] + 'taintIfTainted' | ['test', 'test', false, NOT_MARKED] + 'taintIfTainted' | ['test', 'test', SourceTypes.REQUEST_PARAMETER_VALUE] + 'taintIfTainted' | ['test', 'test', SourceTypes.REQUEST_PARAMETER_VALUE, 'name'] + 'taintIfTainted' | ['test', 'test', SourceTypes.REQUEST_PARAMETER_VALUE, 'name', 'value'] + 'taintIfAnyTainted' | ['test', ['test']] + 'taintDeeply' | [ + 'test', + SourceTypes.REQUEST_PARAMETER_VALUE, + { + true + } + ] + 'findSource' | ['test'] + 'isTainted' | ['test'] } - void 'test propagation for #method'() { + void 'test taint'() { given: - final toTaint = toTaintClosure.call(args) - final targetMethod = module.&"$method" - final arguments = args.toArray() - final input = inputClosure.call(arguments) - - when: - targetMethod.call(arguments) - - then: - assertNotTainted(toTaint) + final value = (target instanceof CharSequence) ? target.toString() : null + final source = taintedSource(value) + final ranges = Ranges.forObject(source) when: - taint(input) - targetMethod.call(arguments) + module.taint(target, source.origin, source.name, source.value) then: - assertTainted(toTaint) + final tainted = getTaintedObject(target) + if (shouldTaint) { + assertTainted(tainted, ranges) + } else { + assert tainted == null + } where: - method | args | toTaintClosure | inputClosure - 'taintIfInputIsTainted' | [new Object(), 'I am an string'] | { - it[0] - } | { - it[1] - } - 'taintIfInputIsTainted' | [new Object(), new Object()] | { - it[0] - } | { - it[1] - } - 'taintIfInputIsTainted' | [new Object(), new MockTaintable()] | { - it[0] - } | { - it[1] - } - 'taintIfInputIsTainted' | ['Hello', 'I am an string'] | { - it[0] - } | { - it[1] - } - 'taintIfInputIsTainted' | ['Hello', new Object()] | { - it[0] - } | { - it[1] - } - 'taintIfInputIsTainted' | ['Hello', new MockTaintable()] | { - it[0] - } | { - it[1] - } - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', 'value', 'I am an string'] | { - it[2] - } | { - it[3] - } - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', 'value', new Object()] | { - it[2] - } | { - it[3] - } - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', 'value', new MockTaintable()] | { - it[2] - } | { - it[3] - } - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', ['value'], 'I am an string'] | { - it[2][0] - } | { - it[3] - } - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', ['value'], new Object()] | { - it[2][0] - } | { - it[3] - } - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, 'name', ['value'], new MockTaintable()] | { - it[2][0] - } | { - it[3] - } - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, ['value'].toSet(), 'I am an string'] | { - it[1][0] - } | { - it[2] - } - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, ['value'].toSet(), new Object()] | { - it[1][0] - } | { - it[2] - } - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, ['value'].toSet(), new MockTaintable()] | { - it[1][0] - } | { - it[2] - } - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, [name: 'value'].entrySet().toList(), 'I am an string'] | { - it[1][0].value - } | { - it[2] - } - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, [name: 'value'].entrySet().toList(), new Object()] | { - it[1][0].value - } | { - it[2] - } - 'taintIfInputIsTainted' | [SourceTypes.REQUEST_PARAMETER_VALUE, [name: 'value'].entrySet().toList(), new MockTaintable()] | { - it[1][0].value - } | { - it[2] - } - 'taintObjectIfInputIsTaintedKeepingRanges' | [new Object(), new Object()] | { - it[0] - } | { - it[1] - } - 'taintObjectIfInputIsTaintedKeepingRanges' | [new Object(), new MockTaintable()] | { - it[0] - } | { - it[1] - } - 'taintIfAnyInputIsTainted' | [new Object(), ['I am an string'].toArray()] | { - it[0] - } | { - it[1][0] - } - 'taintIfAnyInputIsTainted' | [new Object(), [new Object()].toArray()] | { - it[0] - } | { - it[1][0] - } - 'taintIfAnyInputIsTainted' | [new Object(), [new MockTaintable()].toArray()] | { - it[0] - } | { - it[1][0] - } - 'taintIfAnyInputIsTainted' | ['Hello', ['I am an string'].toArray()] | { - it[0] - } | { - it[1][0] - } - 'taintIfAnyInputIsTainted' | ['Hello', [new Object()].toArray()] | { - it[0] - } | { - it[1][0] - } - 'taintIfAnyInputIsTainted' | ['Hello', [new MockTaintable()].toArray()] | { - it[0] - } | { - it[1][0] - } - 'taintIfInputIsTaintedWithMarks' | ['Hello', 'I am an string', VulnerabilityMarks.XSS_MARK] | { - it[0] - } | { - it[1] - } - 'taintIfInputIsTaintedWithMarks' | ['Hello', new Object(), VulnerabilityMarks.XSS_MARK] | { - it[0] - } | { - it[1] - } - 'taintIfInputIsTaintedWithMarks' | ['Hello', new MockTaintable(), VulnerabilityMarks.XSS_MARK] | { - it[0] - } | { - it[1] - } + target | shouldTaint + string('string') | true + stringBuilder('stringBuilder') | true + date() | true + taintable() | true } - void 'test value source for #method'() { + void 'test taintIfTainted keeping ranges'() { given: - final span = Mock(AgentSpan) - tracer.activeSpan() >> span - final reqCtx = Mock(RequestContext) - span.getRequestContext() >> reqCtx - final ctx = new IastRequestContext() - reqCtx.getData(RequestContextSlot.IAST) >> ctx + def (target, input) = suite + final source = taintedSource() + final ranges = [new Range(0, 1, source, NOT_MARKED), new Range(1, 1, source, NOT_MARKED)] as Range[] - when: - module."$method"(source, name, value) + when: 'input is not tainted' + module.taintIfTainted(target, input, true, NOT_MARKED) then: - 1 * tracer.activeSpan() >> span - 1 * span.getRequestContext() >> reqCtx - 1 * reqCtx.getData(RequestContextSlot.IAST) >> ctx - 0 * _ - ctx.getTaintedObjects().get(name) == null - def to = ctx.getTaintedObjects().get(value) - to != null - to.get() == value - to.ranges.size() == 1 - to.ranges[0].start == 0 - to.ranges[0].length == value.length() - to.ranges[0].source == new Source(source, name, value) + assert getTaintedObject(target) == null + + when: 'input is tainted' + final taintedFrom = taintObject(input, ranges) + module.taintIfTainted(target, input, true, NOT_MARKED) + + then: + final tainted = getTaintedObject(target) + if (target instanceof Taintable) { + // only first range is kept + assertTainted(tainted, [taintedFrom.ranges[0]] as Range[]) + } else { + assertTainted(tainted, taintedFrom.ranges) + } where: - method | name | value | source - 'taint' | null | 'value' | SourceTypes.REQUEST_PARAMETER_VALUE - 'taint' | 'name' | 'value' | SourceTypes.REQUEST_PARAMETER_VALUE + suite << taintIfSuite() } - void 'taint with context for #method'() { - setup: - def ctx = new IastRequestContext() + void 'test taintIfTainted keeping ranges with a mark'() { + given: + def (target, input) = suite + Assume.assumeFalse(target instanceof Taintable) // taintable does not support multiple ranges or marks + final source = taintedSource() + final ranges = [new Range(0, 1, source, NOT_MARKED), new Range(1, 1, source, NOT_MARKED)] as Range[] + final mark = VulnerabilityMarks.UNVALIDATED_REDIRECT_MARK - when: - module."$method"(ctx as Object, source, name, value) + when: 'input is not tainted' + module.taintIfTainted(target, input, true, mark) then: - ctx.getTaintedObjects().get(name) == null - def to = ctx.getTaintedObjects().get(value) - to != null - to.get() == value - to.ranges.size() == 1 - to.ranges[0].start == 0 - to.ranges[0].length == value.length() - to.ranges[0].source == new Source(source, name, value) - 0 * _ + assert getTaintedObject(target) == null + + when: 'input is tainted' + final taintedFrom = taintObject(input, ranges) + module.taintIfTainted(target, input, true, mark) + + then: + final tainted = getTaintedObject(target) + assertTainted(tainted, taintedFrom.ranges, mark) where: - method | name | value | source - 'taint' | null | "value" | SourceTypes.REQUEST_PATH_PARAMETER - 'taint' | "" | "value" | SourceTypes.REQUEST_PATH_PARAMETER - 'taint' | "param" | "value" | SourceTypes.REQUEST_PATH_PARAMETER + suite << taintIfSuite() } - void 'test taintObject'() { - when: - module.taintObject(origin, toTaint) + void 'test taintIfTainted not keeping ranges'() { + given: + def (target, input) = suite + final source = taintedSource() + final ranges = [new Range(0, 1, source, NOT_MARKED), new Range(1, 1, source, NOT_MARKED)] as Range[] + + when: 'input is not tainted' + module.taintIfTainted(target, input, false, NOT_MARKED) + + then: + assert getTaintedObject(target) == null + + when: 'input is tainted' + final taintedFrom = taintObject(input, ranges) + module.taintIfTainted(target, input, false, NOT_MARKED) then: - assertTainted(toTaint) + final tainted = getTaintedObject(target) + assertTainted(tainted, [highestPriorityRange(taintedFrom.ranges)] as Range[]) where: - origin | toTaint - SourceTypes.REQUEST_PARAMETER_VALUE | new Object() - SourceTypes.REQUEST_PARAMETER_VALUE | new MockTaintable() + suite << taintIfSuite() } - void 'test taintObjects[array]'() { - when: - module.taintObjects(origin, new Object[]{ - toTaint - }) + void 'test taintIfTainted not keeping ranges with a mark'() { + given: + def (target, input) = suite + Assume.assumeFalse(target instanceof Taintable) // taintable does not support marks + final source = taintedSource() + final ranges = [new Range(0, 1, source, NOT_MARKED), new Range(1, 1, source, NOT_MARKED)] as Range[] + final mark = VulnerabilityMarks.LDAP_INJECTION_MARK + + when: 'input is not tainted' + module.taintIfTainted(target, input, false, mark) then: - assertTainted(toTaint) + assert getTaintedObject(target) == null + + when: 'input is tainted' + final taintedFrom = taintObject(input, ranges) + module.taintIfTainted(target, input, false, mark) + + then: + final tainted = getTaintedObject(target) + assertTainted(tainted, [highestPriorityRange(taintedFrom.ranges)] as Range[], mark) where: - origin | toTaint - SourceTypes.REQUEST_PARAMETER_VALUE | new Object() - SourceTypes.REQUEST_PARAMETER_VALUE | new MockTaintable() + suite << taintIfSuite() } - void 'onJsonFactoryCreateParser'() { + void 'test taintIfAnyTainted keeping ranges'() { given: - final taintedObjects = ctx.getTaintedObjects() - def shouldBeTainted = true + def (target, input) = suite + final inputs = ['test', input].toArray() + final source = taintedSource() + final ranges = [new Range(0, 1, source, NOT_MARKED), new Range(1, 1, source, NOT_MARKED)] as Range[] - def firstParam - if (param1 instanceof String) { - firstParam = addFromTaintFormat(taintedObjects, param1) - objectHolder.add(firstParam) - } else { - firstParam = param1 - } + when: 'input is not tainted' + module.taintIfAnyTainted(target, inputs, true, NOT_MARKED) + + then: + assert getTaintedObject(target) == null - def secondParam - if (param2 instanceof String) { - secondParam = addFromTaintFormat(taintedObjects, param2) - objectHolder.add(secondParam) - shouldBeTainted = fromTaintFormat(param2) != null + when: 'input is tainted' + final taintedFrom = taintObject(input, ranges) + module.taintIfAnyTainted(target, inputs, true, NOT_MARKED) + + then: + final tainted = getTaintedObject(target) + if (target instanceof Taintable) { + // only first range is kept + assertTainted(tainted, [taintedFrom.ranges[0]] as Range[]) } else { - secondParam = param2 + assertTainted(tainted, taintedFrom.ranges) } - if (shouldBeTainted) { - def ranges = new Range[1] - ranges[0] = new Range(0, Integer.MAX_VALUE, new Source((byte) 1, "test", "test"), NOT_MARKED) - taintedObjects.taint(secondParam, ranges) - } + where: + suite << taintIfSuite() + } - when: - module.taintIfInputIsTainted(firstParam, secondParam) + void 'test taintIfAnyTainted keeping ranges with a mark'() { + given: + def (target, input) = suite + Assume.assumeFalse(target instanceof Taintable) // taintable does not support multiple ranges or marks + final inputs = ['test', input].toArray() + final source = taintedSource() + final ranges = [new Range(0, 1, source, NOT_MARKED), new Range(1, 1, source, NOT_MARKED)] as Range[] + final mark = VulnerabilityMarks.UNVALIDATED_REDIRECT_MARK + + when: 'input is not tainted' + module.taintIfAnyTainted(target, inputs, true, mark) then: - def to = ctx.getTaintedObjects().get(param1) - if (shouldBeTainted) { - assert to != null - assert to.get() == param1 - if (param1 instanceof String) { - final ranges = to.getRanges() - assert ranges.length == 1 - assert ranges[0].start == 0 - assert ranges[0].length == param1.length() - } else { - final ranges = to.getRanges() - assert ranges.length == 1 - assert ranges[0].start == 0 - assert ranges[0].length == Integer.MAX_VALUE - } - } else { - assert to == null - } + assert getTaintedObject(target) == null + + when: 'input is tainted' + final taintedFrom = taintObject(input, ranges) + module.taintIfAnyTainted(target, inputs, true, mark) + + then: + final tainted = getTaintedObject(target) + assertTainted(tainted, taintedFrom.ranges, mark) where: - param1 | param2 - '123' | new Object() - new Object() | new Object() - new Object() | '123' - new Object() | '==>123<==' + suite << taintIfSuite() } - void 'test first tainted source'() { - when: - final before = module.firstTaintedSource(target) + void 'test taintIfAnyTainted not keeping ranges'() { + given: + def (target, input) = suite + final inputs = ['test', input].toArray() + final source = taintedSource() + final ranges = [new Range(0, 1, source, NOT_MARKED), new Range(1, 1, source, NOT_MARKED)] as Range[] + + when: 'input is not tainted' + module.taintIfAnyTainted(target, inputs, false, NOT_MARKED) then: - before == null + assert getTaintedObject(target) == null - when: - module.taintObject(origin, target) - final after = module.firstTaintedSource(target) + when: 'input is tainted' + final taintedFrom = taintObject(input, ranges) + module.taintIfAnyTainted(target, inputs, false, NOT_MARKED) then: - after.origin == origin + final tainted = getTaintedObject(target) + assertTainted(tainted, [highestPriorityRange(taintedFrom.ranges)] as Range[]) where: - target | origin - 'this is a string' | SourceTypes.REQUEST_PARAMETER_VALUE - new Object() | SourceTypes.REQUEST_PARAMETER_VALUE - new MockTaintable() | SourceTypes.REQUEST_PARAMETER_VALUE + suite << taintIfSuite() } - void 'test taintIfInputIsTaintedWithMarks marks ranges for #mark'() { + void 'test taintIfAnyTainted not keeping ranges with a mark'() { given: - final toTaint = 'this is a string' - final tainted = new Object() - ctx.getTaintedObjects().taint(tainted, previousRanges) - objectHolder.add(toTaint) + def (target, input) = suite + Assume.assumeFalse(target instanceof Taintable) // taintable does not support marks + final inputs = ['test', input].toArray() + final source = taintedSource() + final ranges = [new Range(0, 1, source, NOT_MARKED), new Range(1, 1, source, NOT_MARKED)] as Range[] + final mark = VulnerabilityMarks.LDAP_INJECTION_MARK - when: - module.taintIfInputIsTaintedWithMarks(toTaint, tainted, mark) + when: 'input is not tainted' + module.taintIfAnyTainted(target, inputs, false, mark) then: - final to = ctx.getTaintedObjects().get(toTaint) - final ranges = to.getRanges() - ranges != null && ranges.length == 1 - ranges[0].marks == expected + assert getTaintedObject(target) == null + + when: 'input is tainted' + final taintedFrom = taintObject(input, ranges) + module.taintIfAnyTainted(target, inputs, false, mark) + + then: + final tainted = getTaintedObject(target) + assertTainted(tainted, [highestPriorityRange(taintedFrom.ranges)] as Range[], mark) where: - previousRanges | mark | expected - [new Range(0, Integer.MAX_VALUE, getDefaultSource(), NOT_MARKED)] as Range[] | XSS_MARK | XSS_MARK - [new Range(0, 1, getDefaultSource(), SQL_INJECTION_MARK), new Range(2, 3, getDefaultSource(), NOT_MARKED)] as Range[] | XSS_MARK | XSS_MARK - [new Range(2, 3, getDefaultSource(), NOT_MARKED), new Range(0, 1, getDefaultSource(), SQL_INJECTION_MARK)] as Range[] | XSS_MARK | XSS_MARK - [new Range(2, 3, getDefaultSource(), XPATH_INJECTION_MARK), new Range(0, 1, getDefaultSource(), SQL_INJECTION_MARK)] as Range[] | XSS_MARK | getMarks(XPATH_INJECTION_MARK, XSS_MARK) + suite << taintIfSuite() } void 'test taint deeply'() { @@ -479,81 +356,208 @@ class PropagationModuleTest extends IastModuleImplTestBase { final target = [Hello: " World!", Age: 25] when: - module.taintDeeply(null, SourceTypes.GRPC_BODY, target) + module.taintDeeply(target, SourceTypes.GRPC_BODY, { true }) then: - ctx.getTaintedObjects().empty + final taintedObjects = ctx.taintedObjects + target.keySet().each { key -> + assert taintedObjects.get(key) != null + } + assert taintedObjects.get(target['Hello']) != null + assert taintedObjects.size() == 3 // two keys and one string value + } + + void 'test taint deeply char sequence'() { + given: + final target = stringBuilder('taint me') when: - module.taintDeeply(ctx, SourceTypes.GRPC_BODY, target) + module.taintDeeply(target, SourceTypes.GRPC_BODY, { true }) then: final taintedObjects = ctx.taintedObjects - target.keySet().each { - key -> - assert taintedObjects.get(key) != null + assert taintedObjects.size() == 1 + final tainted = taintedObjects.get(target) + assert tainted != null + final source = tainted.ranges[0].source + assert source.origin == SourceTypes.GRPC_BODY + assert source.value == target.toString() + } + + void 'test is tainted and find source'() { + given: + if (source != null) { + taintObject(target, source) } - assert taintedObjects.get(target['Hello']) != null - assert taintedObjects.size() == 3 // two keys and one string value + + when: + final tainted = module.isTainted(target) + + then: + tainted == (source != null) + + when: + final foundSource = module.findSource(target) + + then: + foundSource == source + + where: + target | source + string('string') | null + stringBuilder('stringBuilder') | null + date() | null + taintable() | null + string('string') | taintedSource() + stringBuilder('stringBuilder') | taintedSource() + date() | taintedSource() + taintable() | taintedSource() + } + + void 'test source names over threshold'() { + given: + final maxSize = Config.get().iastTruncationMaxValueLength + + when: + module.taint(target, SourceTypes.REQUEST_PARAMETER_VALUE) + + then: + final tainted = ctx.getTaintedObjects().get(target) + tainted != null + final sourceValue = tainted.ranges.first().source.value + sourceValue.length() <= target.length() + sourceValue.length() <= maxSize + + where: + target | _ + string((0..Config.get().getIastTruncationMaxValueLength() * 2).join('')) | _ + stringBuilder((0..Config.get().getIastTruncationMaxValueLength() * 2).join('')) | _ + } + + private List> taintIfSuite() { + return [ + Tuple.tuple(string('string'), string('string')), + Tuple.tuple(string('string'), stringBuilder('stringBuilder')), + Tuple.tuple(string('string'), date()), + Tuple.tuple(string('string'), taintable()), + Tuple.tuple(stringBuilder('stringBuilder'), string('string')), + Tuple.tuple(stringBuilder('stringBuilder'), stringBuilder('stringBuilder')), + Tuple.tuple(stringBuilder('stringBuilder'), date()), + Tuple.tuple(stringBuilder('stringBuilder'), taintable()), + Tuple.tuple(date(), string('string')), + Tuple.tuple(date(), stringBuilder('stringBuilder')), + Tuple.tuple(date(), date()), + Tuple.tuple(date(), taintable()), + Tuple.tuple(taintable(), string('string')), + Tuple.tuple(taintable(), stringBuilder('stringBuilder')), + Tuple.tuple(taintable(), date()), + Tuple.tuple(taintable(), taintable()) + ] + } + + private TaintedObject getTaintedObject(final Object target) { + if (target instanceof Taintable) { + final source = (target as Taintable).$$DD$getSource() as Source + return source == null ? null : new TaintedObject(target, Ranges.forObject(source), null) + } + return ctx.getTaintedObjects().get(target) } - private E taint(final E toTaint) { - final source = new Source(SourceTypes.REQUEST_PARAMETER_VALUE, null, null) - if (toTaint instanceof Taintable) { - toTaint.$$DD$setSource(source) + private TaintedObject taintObject(final Object target, Source source, int mark = NOT_MARKED) { + if (target instanceof Taintable) { + target.$$DD$setSource(source) + } else if (target instanceof CharSequence) { + ctx.getTaintedObjects().taint(target, Ranges.forCharSequence(target, source, mark)) } else { - ctx.taintedObjects.taintInputObject(toTaint, source) - objectHolder.add(toTaint) + ctx.getTaintedObjects().taint(target, Ranges.forObject(source, mark)) } - return toTaint + return getTaintedObject(target) } - private void assertTainted(final Object toTaint) { - final tainted = ctx.getTaintedObjects().get(toTaint) - if (toTaint instanceof Taintable) { - assert tainted == null - assert toTaint.$$DD$getSource() != null + private TaintedObject taintObject(final Object target, Range[] ranges) { + if (target instanceof Taintable) { + target.$$DD$setSource(ranges[0].getSource()) } else { - assert tainted != null + ctx.getTaintedObjects().taint(target, ranges) } + return getTaintedObject(target) } - private void assertNotTainted(final Object toTaint) { - final tainted = ctx.getTaintedObjects().get(toTaint) - assert tainted == null - if (toTaint instanceof Taintable) { - assert toTaint.$$DD$getSource() == null + private String string(String value, Source source = null, int mark = NOT_MARKED) { + final result = new String(value) + if (source != null) { + taintObject(result, source, mark) } + return result } - private static Source getDefaultSource() { - return new Source(SourceTypes.REQUEST_PARAMETER_VALUE, null, null) + private StringBuilder stringBuilder(String value, Source source = null, int mark = NOT_MARKED) { + final result = new StringBuilder(value) + if (source != null) { + taintObject(result, source, mark) + } + return result + } + + private Date date(Source source = null, int mark = NOT_MARKED) { + final result = new Date() + if (source != null) { + taintObject(result, source, mark) + } + return result } - private static int getMarks(int ... marks) { - int result = NOT_MARKED - for (int mark : marks) { - result = result | mark + private Taintable taintable(Source source = null) { + final result = new MockTaintable() + if (source != null) { + taintObject(result, source) } return result } - /** - * Mocking makes the test a bit more confusing*/ - private static final class MockTaintable implements Taintable { + private Source taintedSource(String value = 'value') { + return new Source(SourceTypes.REQUEST_PARAMETER_VALUE, 'name', value) + } + private static void assertTainted(final TaintedObject tainted, final Range[] ranges, final int mark = NOT_MARKED) { + assert tainted != null + final originalValue = tainted.get() + assert tainted.ranges.length == ranges.length + ranges.eachWithIndex { Range expected, int i -> + final range = tainted.ranges[i] + if (mark == NOT_MARKED) { + assert range.marks == expected.marks + } else { + assert (range.marks & mark) > 0 + } + final source = range.source + assert !source.value.is(originalValue): 'Weak value should not be retained by the source' + + final expectedSource = expected.source + assert source.origin == expectedSource.origin + assert source.name == expectedSource.name + assert source.value == expectedSource.value + } + } + + private static class MockTaintable implements Taintable { private Source source - @Override @SuppressWarnings('CodeNarc') + @Override Source $$DD$getSource() { return source } - @Override @SuppressWarnings('CodeNarc') + @Override void $$DD$setSource(Source source) { this.source = source } + + @Override + String toString() { + return Taintable.name + } } } diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/CommandInjectionModuleTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/CommandInjectionModuleTest.groovy index 9bc9148bb30..a4db2b305d2 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/CommandInjectionModuleTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/CommandInjectionModuleTest.groovy @@ -11,7 +11,7 @@ import datadog.trace.api.iast.sink.CommandInjectionModule import datadog.trace.bootstrap.instrumentation.api.AgentSpan import groovy.transform.CompileDynamic -import static com.datadog.iast.model.Range.NOT_MARKED +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED import static com.datadog.iast.taint.TaintUtils.addFromTaintFormat import static com.datadog.iast.taint.TaintUtils.taintFormat diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/LdapInjectionModuleTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/LdapInjectionModuleTest.groovy index 2159276a958..768bba078ff 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/LdapInjectionModuleTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/LdapInjectionModuleTest.groovy @@ -11,7 +11,7 @@ import datadog.trace.api.iast.sink.LdapInjectionModule import datadog.trace.bootstrap.instrumentation.api.AgentSpan import groovy.transform.CompileDynamic -import static com.datadog.iast.model.Range.NOT_MARKED +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED import static com.datadog.iast.taint.TaintUtils.addFromTaintFormat import static com.datadog.iast.taint.TaintUtils.taintFormat diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/PathTraversalModuleTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/PathTraversalModuleTest.groovy index d55e2975e7e..01a4c1d1dd6 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/PathTraversalModuleTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/PathTraversalModuleTest.groovy @@ -10,7 +10,7 @@ import datadog.trace.api.iast.VulnerabilityMarks import datadog.trace.api.iast.sink.PathTraversalModule import datadog.trace.bootstrap.instrumentation.api.AgentSpan -import static com.datadog.iast.model.Range.NOT_MARKED +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED import static com.datadog.iast.taint.TaintUtils.addFromTaintFormat import static com.datadog.iast.taint.TaintUtils.fromTaintFormat import static com.datadog.iast.taint.TaintUtils.getStringFromTaintFormat diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/SqlInjectionModuleTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/SqlInjectionModuleTest.groovy index 3041c928a2a..fe711a6a0c2 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/SqlInjectionModuleTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/SqlInjectionModuleTest.groovy @@ -10,7 +10,7 @@ import datadog.trace.api.iast.VulnerabilityMarks import datadog.trace.api.iast.sink.SqlInjectionModule import datadog.trace.bootstrap.instrumentation.api.AgentSpan -import static com.datadog.iast.model.Range.NOT_MARKED +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED import static com.datadog.iast.taint.TaintUtils.addFromTaintFormat import static com.datadog.iast.taint.TaintUtils.taintFormat import static datadog.trace.api.iast.sink.SqlInjectionModule.DATABASE_PARAMETER diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/SsrfModuleTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/SsrfModuleTest.groovy index 6884da0e616..bb914d2dcd2 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/SsrfModuleTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/SsrfModuleTest.groovy @@ -6,6 +6,7 @@ import com.datadog.iast.model.Range import com.datadog.iast.model.Source import com.datadog.iast.model.Vulnerability import com.datadog.iast.model.VulnerabilityType +import com.datadog.iast.taint.Ranges import datadog.trace.api.gateway.RequestContext import datadog.trace.api.gateway.RequestContextSlot import datadog.trace.api.iast.SourceTypes @@ -82,6 +83,6 @@ class SsrfModuleTest extends IastModuleImplTestBase { } private void taint(final Object value) { - ctx.getTaintedObjects().taintInputObject(value, new Source(SourceTypes.REQUEST_PARAMETER_VALUE, 'name', value.toString())) + ctx.getTaintedObjects().taint(value, Ranges.forObject(new Source(SourceTypes.REQUEST_PARAMETER_VALUE, 'name', value.toString()))) } } diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/TrustBoundaryViolationModuleTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/TrustBoundaryViolationModuleTest.groovy index e50a7b26674..7769e3e9262 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/TrustBoundaryViolationModuleTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/TrustBoundaryViolationModuleTest.groovy @@ -5,6 +5,7 @@ import com.datadog.iast.IastRequestContext import com.datadog.iast.model.Source import com.datadog.iast.model.Vulnerability import com.datadog.iast.model.VulnerabilityType +import com.datadog.iast.taint.Ranges import datadog.trace.api.gateway.RequestContext import datadog.trace.api.gateway.RequestContextSlot import datadog.trace.api.iast.InstrumentationBridge @@ -60,7 +61,7 @@ class TrustBoundaryViolationModuleTest extends IastModuleImplTestBase { given: Vulnerability savedVul final name = "name" - ctx.getTaintedObjects().taintInputString(name, new Source(SourceTypes.NONE, null, null)) + ctx.getTaintedObjects().taint(name, Ranges.forCharSequence(name, new Source(SourceTypes.NONE, null, null))) when: module.onSessionValue(name, "value") @@ -77,7 +78,7 @@ class TrustBoundaryViolationModuleTest extends IastModuleImplTestBase { Vulnerability savedVul final name = "name" final badValue = "theValue" - ctx.getTaintedObjects().taintInputString(badValue, new Source(SourceTypes.NONE, null, null)) + ctx.getTaintedObjects().taint(badValue, Ranges.forCharSequence(badValue, new Source(SourceTypes.NONE, null, null))) when: module.onSessionValue(name, badValue) @@ -95,7 +96,7 @@ class TrustBoundaryViolationModuleTest extends IastModuleImplTestBase { Vulnerability savedVul final name = "name" final badValue = "badValue" - ctx.getTaintedObjects().taintInputString(badValue, new Source(SourceTypes.NONE, null, null)) + ctx.getTaintedObjects().taint(badValue, Ranges.forCharSequence(badValue, new Source(SourceTypes.NONE, null, null))) final values = ["A", "B", badValue] when: @@ -113,7 +114,7 @@ class TrustBoundaryViolationModuleTest extends IastModuleImplTestBase { Vulnerability savedVul final name = "name" final badValue = "badValue" - ctx.getTaintedObjects().taintInputString(badValue, new Source(SourceTypes.NONE, null, null)) + ctx.getTaintedObjects().taint(badValue, Ranges.forCharSequence(badValue, new Source(SourceTypes.NONE, null, null))) final values = new String[3] values[0] = "A" values[1] = "B" @@ -134,7 +135,7 @@ class TrustBoundaryViolationModuleTest extends IastModuleImplTestBase { Vulnerability savedVul final name = "name" final badValue = "badValue" - ctx.getTaintedObjects().taintInputString(badValue, new Source(SourceTypes.NONE, null, null)) + ctx.getTaintedObjects().taint(badValue, Ranges.forCharSequence(badValue, new Source(SourceTypes.NONE, null, null))) final values = new LinkedHashMap() values.put("A", "A") values.put("B", "B") @@ -155,7 +156,7 @@ class TrustBoundaryViolationModuleTest extends IastModuleImplTestBase { Vulnerability savedVul final name = "name" final badValue = "badValue" - ctx.getTaintedObjects().taintInputString(badValue, new Source(SourceTypes.NONE, null, null)) + ctx.getTaintedObjects().taint(badValue, Ranges.forCharSequence(badValue, new Source(SourceTypes.NONE, null, null))) final value = new VisitableClass(name: badValue) when: diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/UnvalidatedRedirectModuleTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/UnvalidatedRedirectModuleTest.groovy index 708b665a902..f773870eb5e 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/UnvalidatedRedirectModuleTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/UnvalidatedRedirectModuleTest.groovy @@ -6,6 +6,7 @@ import com.datadog.iast.model.Range import com.datadog.iast.model.Source import com.datadog.iast.model.Vulnerability import com.datadog.iast.model.VulnerabilityType +import com.datadog.iast.taint.Ranges import datadog.trace.api.gateway.RequestContext import datadog.trace.api.gateway.RequestContextSlot import datadog.trace.api.iast.InstrumentationBridge @@ -14,7 +15,7 @@ import datadog.trace.api.iast.VulnerabilityMarks import datadog.trace.api.iast.sink.UnvalidatedRedirectModule import datadog.trace.bootstrap.instrumentation.api.AgentSpan -import static com.datadog.iast.model.Range.NOT_MARKED +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED import static com.datadog.iast.taint.TaintUtils.addFromTaintFormat import static com.datadog.iast.taint.TaintUtils.taintFormat @@ -64,7 +65,7 @@ class UnvalidatedRedirectModuleTest extends IastModuleImplTestBase { void 'iast module detects URI redirect (#value)'(final URI value, final String expected) { setup: - ctx.taintedObjects.taintInputObject(value, new Source(SourceTypes.NONE, null, null)) + ctx.taintedObjects.taint(value, Ranges.forObject(new Source(SourceTypes.NONE, null, null))) when: module.onURIRedirect(value) diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/XPathInjectionModuleTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/XPathInjectionModuleTest.groovy index 6a44afd10b8..819980331ae 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/XPathInjectionModuleTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/XPathInjectionModuleTest.groovy @@ -10,7 +10,7 @@ import datadog.trace.api.iast.VulnerabilityMarks import datadog.trace.api.iast.sink.XPathInjectionModule import datadog.trace.bootstrap.instrumentation.api.AgentSpan -import static com.datadog.iast.model.Range.NOT_MARKED +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED import static com.datadog.iast.taint.TaintUtils.addFromTaintFormat import static com.datadog.iast.taint.TaintUtils.taintFormat diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/XssModuleTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/XssModuleTest.groovy index ff0340a99df..6722b3faf3c 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/XssModuleTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/XssModuleTest.groovy @@ -5,6 +5,7 @@ import com.datadog.iast.IastRequestContext import com.datadog.iast.model.Source import com.datadog.iast.model.Vulnerability import com.datadog.iast.model.VulnerabilityType +import com.datadog.iast.taint.Ranges import datadog.trace.api.gateway.RequestContext import datadog.trace.api.gateway.RequestContextSlot import datadog.trace.api.iast.SourceTypes @@ -12,7 +13,7 @@ import datadog.trace.api.iast.VulnerabilityMarks import datadog.trace.api.iast.sink.XssModule import datadog.trace.bootstrap.instrumentation.api.AgentSpan -import static com.datadog.iast.model.Range.NOT_MARKED +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED import static com.datadog.iast.taint.TaintUtils.addFromTaintFormat import static com.datadog.iast.taint.TaintUtils.taintFormat @@ -65,7 +66,7 @@ class XssModuleTest extends IastModuleImplTestBase { void 'module detects char[] XSS'() { setup: if (tainted) { - ctx.taintedObjects.taintInputObject(buf, new Source(SourceTypes.NONE, '', ''), mark) + ctx.taintedObjects.taint(buf, Ranges.forObject(new Source(SourceTypes.NONE, '', ''), mark)) } when: diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/source/WebModuleTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/source/WebModuleTest.groovy deleted file mode 100644 index 1e93f5e3fb4..00000000000 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/source/WebModuleTest.groovy +++ /dev/null @@ -1,130 +0,0 @@ -package com.datadog.iast.source - -import com.datadog.iast.IastModuleImplTestBase -import com.datadog.iast.IastRequestContext -import com.datadog.iast.model.Source -import datadog.trace.api.gateway.RequestContext -import datadog.trace.api.gateway.RequestContextSlot -import datadog.trace.api.iast.SourceTypes -import datadog.trace.api.iast.source.WebModule -import datadog.trace.bootstrap.instrumentation.api.AgentSpan -import groovy.transform.CompileDynamic - -@CompileDynamic -class WebModuleTest extends IastModuleImplTestBase { - - private WebModule module - - def setup() { - module = new WebModuleImpl() - } - - void 'test #method: null or empty'() { - when: - module."$method"(*args) - - then: - 0 * _ - - where: - method | args - 'onParameterNames' | [null] - 'onParameterNames' | [[]] - 'onParameterValues' | [null, null] - 'onParameterValues' | ['', []] - 'onParameterValues' | [null, null as String[]] - 'onParameterValues' | ['', [] as String[]] - 'onParameterValues' | [[:]] - 'onHeaderNames' | [null] - 'onHeaderNames' | [[]] - 'onHeaderValues' | [null, null] - 'onHeaderValues' | ['', []] - 'onCookieNames' | [null] - 'onCookieNames' | [[]] - } - - void 'test #method: without span'() { - when: - module."$method"(*args) - - then: - 1 * tracer.activeSpan() >> null - 0 * _ - - where: - method | args - 'onParameterNames' | [['param']] - 'onParameterValues' | ['name', ['value']] - 'onParameterValues' | ['name', ['value'] as String[]] - 'onParameterValues' | [[name: ['value'] as String[]]] - 'onHeaderNames' | [['header']] - 'onHeaderValues' | ['name', ['value']] - 'onCookieNames' | [['name']] - 'onNamed' | ['name', ['v1'], (byte)0] - 'onNamed' | ['name', ['v1'] as String[], (byte)0] - 'onNamed' | [[name: 'v1'], (byte)0] - } - - void 'onNamed #variant'() { - given: - final span = Mock(AgentSpan) - tracer.activeSpan() >> span - final reqCtx = Mock(RequestContext) - span.getRequestContext() >> reqCtx - final ctx = new IastRequestContext() - reqCtx.getData(RequestContextSlot.IAST) >> ctx - - when: - module.onNamed(*args, SourceTypes.REQUEST_PARAMETER_NAME) - - then: - 1 * tracer.activeSpan() >> span - 1 * span.getRequestContext() >> reqCtx - 1 * reqCtx.getData(RequestContextSlot.IAST) >> ctx - 0 * _ - def tos = ctx.taintedObjects - def to = tos.get('foo') - to.ranges.size() == 1 - to.ranges[0].start == 0 - to.ranges[0].length == 3 - to.ranges[0].source == new Source(SourceTypes.REQUEST_PARAMETER_NAME, 'var', 'foo') - - where: - variant | args - 'collection' | ['var', ['foo']] - 'array' | ['var', ['foo'] as String[]] - 'map' | [[var: ['foo'] as String[]]] - } - - void 'test #method'() { - given: - final span = Mock(AgentSpan) - tracer.activeSpan() >> span - final reqCtx = Mock(RequestContext) - span.getRequestContext() >> reqCtx - final ctx = new IastRequestContext() - reqCtx.getData(RequestContextSlot.IAST) >> ctx - - when: - module."$method"([name]) - - then: - 1 * tracer.activeSpan() >> span - 1 * span.getRequestContext() >> reqCtx - 1 * reqCtx.getData(RequestContextSlot.IAST) >> ctx - 0 * _ - def to = ctx.getTaintedObjects().get(name) - to != null - to.get() == name - to.ranges.size() == 1 - to.ranges[0].start == 0 - to.ranges[0].length == name.length() - to.ranges[0].source == new Source(source, name, name) - - where: - method | name | source - 'onParameterNames' | 'param' | SourceTypes.REQUEST_PARAMETER_NAME - 'onHeaderNames' | 'param' | SourceTypes.REQUEST_HEADER_NAME - 'onCookieNames' | 'param' | SourceTypes.REQUEST_COOKIE_NAME - } -} diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/RangesTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/RangesTest.groovy index 3c80b068b92..60525afa7e2 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/RangesTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/RangesTest.groovy @@ -8,7 +8,7 @@ import datadog.trace.api.iast.SourceTypes import datadog.trace.api.iast.VulnerabilityMarks import datadog.trace.test.util.DDSpecification -import static com.datadog.iast.model.Range.NOT_MARKED +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED import static com.datadog.iast.taint.Ranges.mergeRanges import static com.datadog.iast.taint.Ranges.rangesProviderFor import static datadog.trace.api.iast.SourceTypes.REQUEST_HEADER_NAME diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintUtils.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintUtils.groovy index 7db1a75c673..88b21e9406b 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintUtils.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintUtils.groovy @@ -4,7 +4,7 @@ import com.datadog.iast.model.Range import com.datadog.iast.model.Source import datadog.trace.api.iast.SourceTypes -import static com.datadog.iast.model.Range.NOT_MARKED +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED class TaintUtils { @@ -76,7 +76,7 @@ class TaintUtils { if (value instanceof String) { return addFromTaintFormat(tos, value as String) } - tos.taintInputObject(value, new Source(SourceTypes.NONE, null, null)) + tos.taint(value, Ranges.forObject(new Source(SourceTypes.NONE, null, null))) return value } diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectTest.groovy index cb0351bf0b0..d809f5b8b69 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectTest.groovy @@ -7,7 +7,7 @@ import com.datadog.iast.model.Range import java.lang.ref.ReferenceQueue -import static com.datadog.iast.model.Range.NOT_MARKED +import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED import static datadog.trace.api.iast.SourceTypes.REQUEST_HEADER_NAME class TaintedObjectTest extends Specification { diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectsLazyTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectsLazyTest.groovy deleted file mode 100644 index 94c6f4b0171..00000000000 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectsLazyTest.groovy +++ /dev/null @@ -1,70 +0,0 @@ -package com.datadog.iast.taint - -import com.datadog.iast.IastModuleImplTestBase -import com.datadog.iast.IastRequestContext -import com.datadog.iast.model.Source -import datadog.trace.api.gateway.RequestContext -import datadog.trace.api.gateway.RequestContextSlot -import datadog.trace.bootstrap.instrumentation.api.AgentSpan - -class TaintedObjectsLazyTest extends IastModuleImplTestBase { - - private TaintedObjects delegate = Mock(TaintedObjects) - private IastRequestContext iastCtx - private RequestContext reqCtx - private AgentSpan span - - void setup() { - delegate = Mock(TaintedObjects) - iastCtx = Mock(IastRequestContext) - iastCtx.getTaintedObjects() >> delegate - reqCtx = Mock(RequestContext) - reqCtx.getData(RequestContextSlot.IAST) >> iastCtx - span = Mock(AgentSpan) - span.getRequestContext() >> reqCtx - } - - void 'get non lazy instance'() { - when: - final to = TaintedObjects.activeTaintedObjects() - - then: - 1 * tracer.activeSpan() >> span - !(to instanceof TaintedObjects.LazyTaintedObjects) - } - - void 'get lazy objects instance'() { - when: - final to = TaintedObjects.activeTaintedObjects(true) - - then: - to instanceof TaintedObjects.LazyTaintedObjects - - when: - to.&"$method".call(args as Object[]) - - then: 'first time the active tainted objects if fetched' - 1 * delegate._ - 1 * tracer.activeSpan() >> span - - when: - to.&"$method".call(args as Object[]) - - then: 'the active tainted objets is already fetched' - 1 * delegate._ - 0 * _ - - where: - method | args - 'getEstimatedSize' | [] - 'isFlat' | [] - 'taintInputString' | ['', new Source((byte) 0, null, null)] - 'taintInputCharSequence' | ['', new Source((byte) 0, null, null)] - 'taintInputObject' | ['', new Source((byte) 0, null, null)] - 'taint' | ['', Ranges.EMPTY] - 'get' | [''] - 'release' | [] - 'count' | [] - 'iterator' | [] - } -} diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectsLogTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectsLogTest.groovy index fd50a1f9de3..ea01efa3989 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectsLogTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectsLogTest.groovy @@ -3,7 +3,6 @@ package com.datadog.iast.taint import ch.qos.logback.classic.Level import ch.qos.logback.classic.Logger import com.datadog.iast.IastSystem -import com.datadog.iast.model.Range import com.datadog.iast.model.Source import datadog.trace.api.iast.SourceTypes import datadog.trace.test.util.DDSpecification @@ -35,7 +34,7 @@ class TaintedObjectsLogTest extends DDSpecification { final value = "A" when: - def tainted = taintedObjects.taintInputString(value, new Source(SourceTypes.NONE, null, null)) + def tainted = taintedObjects.taint(value, Ranges.forCharSequence(value, new Source(SourceTypes.NONE, null, null))) then: noExceptionThrown() @@ -54,10 +53,8 @@ class TaintedObjectsLogTest extends DDSpecification { IastSystem.DEBUG = true logger.level = Level.ALL TaintedObjects taintedObjects = TaintedObjects.acquire() - taintedObjects.taint('A', [new Range(0, 1, new Source(SourceTypes.NONE, null, null), Range.NOT_MARKED)] as Range[]) - taintedObjects.taintInputString('B', new Source(SourceTypes.REQUEST_PARAMETER_NAME, 'test', 'value')) - taintedObjects.taintInputCharSequence(new StringBuffer('B'), new Source(SourceTypes.REQUEST_PARAMETER_NAME, 'test', 'value')) - taintedObjects.taintInputObject(new Date(), new Source(SourceTypes.REQUEST_HEADER_VALUE, 'test', 'value')) + final obj = 'A' + taintedObjects.taint(obj, Ranges.forCharSequence(obj, new Source(SourceTypes.NONE, null, null))) when: taintedObjects.release() @@ -65,5 +62,21 @@ class TaintedObjectsLogTest extends DDSpecification { then: noExceptionThrown() } + + void "test TaintedObjects api calls"() { + given: + IastSystem.DEBUG = true + logger.level = Level.ALL + TaintedObjects taintedObjects = TaintedObjects.acquire() + final obj = 'A' + + when: + taintedObjects.taint(obj, Ranges.forCharSequence(obj, new Source(SourceTypes.NONE, null, null))) + + then: + taintedObjects.size() == 1 + taintedObjects.iterator().size() == 1 + !taintedObjects.flat + } } diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectsNoOpTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectsNoOpTest.groovy index 304a2e7187b..c8f1f91fe86 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectsNoOpTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/taint/TaintedObjectsNoOpTest.groovy @@ -12,15 +12,12 @@ class TaintedObjectsNoOpTest extends Specification { final toTaint = 'test' when: - final taintedString = instance.taintInputString(toTaint, new Source(0 as byte, 'test', 'test')) - final taintedCharSequence = instance.taintInputCharSequence(toTaint, new Source(0 as byte, 'test', 'test')) - final taintedObject = instance.taintInputObject(toTaint, new Source(0 as byte, 'test', 'test')) + final tainted = instance.taint(toTaint, Ranges.forCharSequence(toTaint, new Source(0 as byte, 'test', 'test'))) then: - taintedString == null - taintedCharSequence == null - taintedObject == null + tainted == null instance.get(toTaint) == null + instance.count() == 0 instance.estimatedSize == 0 instance.size() == 0 !instance.flat diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/telemetry/TelemetryRequestEndedHandlerTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/telemetry/TelemetryRequestEndedHandlerTest.groovy index 24e2ef187b5..30a61959d98 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/telemetry/TelemetryRequestEndedHandlerTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/telemetry/TelemetryRequestEndedHandlerTest.groovy @@ -3,6 +3,7 @@ package com.datadog.iast.telemetry import com.datadog.iast.IastRequestContext import com.datadog.iast.RequestEndedHandler import com.datadog.iast.model.Source +import com.datadog.iast.taint.Ranges import com.datadog.iast.taint.TaintedObjects import com.datadog.iast.telemetry.taint.TaintedObjectsWithTelemetry import datadog.trace.api.gateway.RequestContextSlot @@ -41,7 +42,7 @@ class TelemetryRequestEndedHandlerTest extends AbstractTelemetryCallbackTest { final handler = new TelemetryRequestEndedHandler(delegate) final toTaint = 'hello' final source = new Source(SourceTypes.REQUEST_PARAMETER_VALUE, 'name', 'value') - iastCtx.taintedObjects.taintInputString(toTaint, source) + iastCtx.taintedObjects.taint(toTaint, Ranges.forCharSequence(toTaint, source)) when: handler.apply(reqCtx, span) diff --git a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/telemetry/taint/TaintedObjectsWithTelemetryTest.groovy b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/telemetry/taint/TaintedObjectsWithTelemetryTest.groovy index bca55f07c11..c20ca32dace 100644 --- a/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/telemetry/taint/TaintedObjectsWithTelemetryTest.groovy +++ b/dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/telemetry/taint/TaintedObjectsWithTelemetryTest.groovy @@ -2,13 +2,11 @@ package com.datadog.iast.telemetry.taint import com.datadog.iast.IastRequestContext import com.datadog.iast.model.Range -import com.datadog.iast.model.Source import com.datadog.iast.taint.Ranges import com.datadog.iast.taint.TaintedObject import com.datadog.iast.taint.TaintedObjects import datadog.trace.api.gateway.RequestContext import datadog.trace.api.gateway.RequestContextSlot -import datadog.trace.api.iast.SourceTypes import datadog.trace.api.iast.telemetry.IastMetric import datadog.trace.api.iast.telemetry.IastMetricCollector import datadog.trace.api.iast.telemetry.Verbosity @@ -74,13 +72,11 @@ class TaintedObjectsWithTelemetryTest extends DDSpecification { final taintedObjects = TaintedObjectsWithTelemetry.build(verbosity, Mock(TaintedObjects)) when: - taintedObjects.taintInputString('test', new Source(SourceTypes.REQUEST_PARAMETER_VALUE, 'name', 'value')) - taintedObjects.taintInputObject(new Date(), new Source(SourceTypes.REQUEST_HEADER_VALUE, 'name', 'value')) taintedObjects.taint('test', new Range[0]) then: if (IastMetric.EXECUTED_TAINTED.isEnabled(verbosity)) { - 3 * mockCollector.addMetric(IastMetric.EXECUTED_TAINTED, _, 1) // two calls with one element + 1 * mockCollector.addMetric(IastMetric.EXECUTED_TAINTED, _, 1) } else { 0 * mockCollector.addMetric } diff --git a/dd-java-agent/agent-iast/src/testFixtures/groovy/com/datadog/iast/test/IastRequestContextPreparationTrait.groovy b/dd-java-agent/agent-iast/src/testFixtures/groovy/com/datadog/iast/test/IastRequestContextPreparationTrait.groovy index ffa956d5ded..6e0b849f6ca 100644 --- a/dd-java-agent/agent-iast/src/testFixtures/groovy/com/datadog/iast/test/IastRequestContextPreparationTrait.groovy +++ b/dd-java-agent/agent-iast/src/testFixtures/groovy/com/datadog/iast/test/IastRequestContextPreparationTrait.groovy @@ -3,16 +3,9 @@ package com.datadog.iast.test import com.datadog.iast.IastRequestContext import com.datadog.iast.IastSystem import com.datadog.iast.model.Range -import com.datadog.iast.model.Source import com.datadog.iast.taint.TaintedObject import com.datadog.iast.taint.TaintedObjects -import datadog.trace.api.gateway.CallbackProvider -import datadog.trace.api.gateway.EventType -import datadog.trace.api.gateway.Events -import datadog.trace.api.gateway.Flow -import datadog.trace.api.gateway.IGSpanInfo -import datadog.trace.api.gateway.RequestContext -import datadog.trace.api.gateway.RequestContextSlot +import datadog.trace.api.gateway.* import datadog.trace.api.iast.InstrumentationBridge import datadog.trace.bootstrap.instrumentation.api.AgentTracer import org.slf4j.Logger @@ -68,22 +61,12 @@ trait IastRequestContextPreparationTrait { List objects = Collections.synchronizedList([]) - TaintedObject taintInputString(String obj, Source source, int mark) { - objects << obj - this.delegate.taintInputString(obj, source, mark) - logTaint obj - } - - TaintedObject taintInputObject(Object obj, Source source, int mark) { - objects << obj - this.delegate.taintInputObject(obj, source, mark) - logTaint obj - } - + @Override TaintedObject taint(Object obj, Range[] ranges) { objects << obj - this.delegate.taintInputString(obj, ranges) + final tainted = this.delegate.taint(obj, ranges) logTaint obj + return tainted } private final static Logger LOGGER = LoggerFactory.getLogger("map tainted objects") diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/CookieHeaderInstrumentation.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/CookieHeaderInstrumentation.java index fa9e098b81e..1c8b35e4bb3 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/CookieHeaderInstrumentation.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/CookieHeaderInstrumentation.java @@ -12,13 +12,11 @@ import akka.http.scaladsl.model.headers.HttpCookiePair; import com.google.auto.service.AutoService; import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.InstrumentationBridge; import datadog.trace.api.iast.Source; import datadog.trace.api.iast.SourceTypes; import datadog.trace.api.iast.propagation.PropagationModule; -import datadog.trace.api.iast.source.WebModule; -import java.util.ArrayList; -import java.util.List; import net.bytebuddy.asm.Advice; import scala.collection.Iterator; import scala.collection.immutable.Seq; @@ -56,23 +54,22 @@ static class TaintAllCookiesAdvice { @Source(SourceTypes.REQUEST_COOKIE_VALUE) static void after( @Advice.This HttpHeader cookie, @Advice.Return Seq cookiePairs) { - WebModule mod = InstrumentationBridge.WEB; PropagationModule prop = InstrumentationBridge.PROPAGATION; - if (mod == null || prop == null || cookiePairs == null || cookiePairs.isEmpty()) { + if (prop == null || cookiePairs == null || cookiePairs.isEmpty()) { return; } if (!prop.isTainted(cookie)) { return; } + final IastContext ctx = IastContext.Provider.get(); Iterator iterator = cookiePairs.iterator(); - List cookieNames = new ArrayList<>(); while (iterator.hasNext()) { HttpCookiePair pair = iterator.next(); - cookieNames.add(pair.name()); - prop.taint(SourceTypes.REQUEST_COOKIE_VALUE, pair.name(), pair.value()); + final String name = pair.name(), value = pair.value(); + prop.taint(ctx, name, SourceTypes.REQUEST_COOKIE_NAME, name); + prop.taint(ctx, value, SourceTypes.REQUEST_COOKIE_VALUE, name); } - mod.onCookieNames(cookieNames); } } } diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/HeaderNameCallSite.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/HeaderNameCallSite.java index 6122b91b361..f7f330a14b5 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/HeaderNameCallSite.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/HeaderNameCallSite.java @@ -26,7 +26,7 @@ public static String after(@CallSite.This HttpHeader header, @CallSite.Return St return result; } try { - module.taintIfInputIsTainted(SourceTypes.REQUEST_HEADER_NAME, result, result, header); + module.taintIfTainted(result, header, SourceTypes.REQUEST_HEADER_NAME, result); } catch (final Throwable e) { module.onUnexpectedException("onHeaderNames threw", e); } diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/HttpHeaderSubclassesInstrumentation.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/HttpHeaderSubclassesInstrumentation.java index 2c9b5cb507b..57cd04ce127 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/HttpHeaderSubclassesInstrumentation.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/HttpHeaderSubclassesInstrumentation.java @@ -62,7 +62,7 @@ static void onExit(@Advice.This HttpHeader h, @Advice.Return String retVal) { return; } - propagation.taintIfInputIsTainted(retVal, h); + propagation.taintIfTainted(retVal, h); } } } diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/HttpRequestInstrumentation.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/HttpRequestInstrumentation.java index 16b124624f8..a0c5f432e85 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/HttpRequestInstrumentation.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/HttpRequestInstrumentation.java @@ -13,6 +13,7 @@ import akka.http.scaladsl.model.HttpRequest; import com.google.auto.service.AutoService; import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.InstrumentationBridge; import datadog.trace.api.iast.Propagation; import datadog.trace.api.iast.Source; @@ -70,6 +71,7 @@ static void onExit( return; } + final IastContext ctx = IastContext.Provider.get(); Iterator iterator = headers.iterator(); while (iterator.hasNext()) { HttpHeader h = iterator.next(); @@ -78,7 +80,7 @@ static void onExit( } // unfortunately, the call to h.value() is instrumented, but // because the call to taint() only happens after, the call is a noop - propagation.taintObject(SourceTypes.REQUEST_HEADER_VALUE, h.name(), h.value(), h); + propagation.taint(ctx, h, SourceTypes.REQUEST_HEADER_VALUE, h.name(), h.value()); } } } @@ -98,7 +100,7 @@ static void onExit( return; } - propagation.taintIfInputIsTainted(entity, thiz); + propagation.taintIfTainted(entity, thiz); } } } diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/PathMatcherInstrumentation.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/PathMatcherInstrumentation.java index 1a4dcf27ce2..80c3b2687f0 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/PathMatcherInstrumentation.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/PathMatcherInstrumentation.java @@ -11,6 +11,7 @@ import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.api.gateway.RequestContext; import datadog.trace.api.gateway.RequestContextSlot; +import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.InstrumentationBridge; import datadog.trace.api.iast.Source; import datadog.trace.api.iast.SourceTypes; @@ -68,11 +69,8 @@ static void onExit( } if (value instanceof String) { - module.taint( - reqCtx.getData(RequestContextSlot.IAST), - SourceTypes.REQUEST_PATH_PARAMETER, - null, - (String) value); + final IastContext ctx = reqCtx.getData(RequestContextSlot.IAST); + module.taint(ctx, value, SourceTypes.REQUEST_PATH_PARAMETER); } } } diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/RequestContextInstrumentation.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/RequestContextInstrumentation.java index 0cdd0266796..f18148a7ceb 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/RequestContextInstrumentation.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/RequestContextInstrumentation.java @@ -53,7 +53,7 @@ static void onExit( return; } - propagation.taintIfInputIsTainted(request, requestContext); + propagation.taintIfTainted(request, requestContext); } } } diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/UriInstrumentation.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/UriInstrumentation.java index 9faab16a32b..49a03c9bf32 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/UriInstrumentation.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/UriInstrumentation.java @@ -11,13 +11,12 @@ import akka.http.scaladsl.model.Uri; import com.google.auto.service.AutoService; import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.InstrumentationBridge; import datadog.trace.api.iast.Propagation; import datadog.trace.api.iast.Source; import datadog.trace.api.iast.SourceTypes; import datadog.trace.api.iast.propagation.PropagationModule; -import datadog.trace.api.iast.source.WebModule; -import java.util.Collections; import net.bytebuddy.asm.Advice; import scala.Tuple2; import scala.collection.Iterator; @@ -70,7 +69,7 @@ static void after(@Advice.This Uri uri, @Advice.Return scala.Option ret) if (mod == null || ret.isEmpty()) { return; } - mod.taintIfInputIsTainted(ret.get(), uri); + mod.taintIfTainted(ret.get(), uri); } } @@ -80,9 +79,8 @@ public static class TaintQueryAdvice { @Advice.OnMethodExit(suppress = Throwable.class) @Source(SourceTypes.REQUEST_PARAMETER_VALUE) static void after(@Advice.This /*Uri*/ Object uri, @Advice.Return Uri.Query ret) { - WebModule web = InstrumentationBridge.WEB; PropagationModule prop = InstrumentationBridge.PROPAGATION; - if (prop == null || web == null || ret.isEmpty()) { + if (prop == null || ret.isEmpty()) { return; } @@ -90,11 +88,13 @@ static void after(@Advice.This /*Uri*/ Object uri, @Advice.Return Uri.Query ret) return; } + final IastContext ctx = IastContext.Provider.get(); Iterator> iterator = ret.iterator(); while (iterator.hasNext()) { Tuple2 pair = iterator.next(); - web.onParameterNames(Collections.singleton(pair._1())); - prop.taint(SourceTypes.REQUEST_PARAMETER_VALUE, pair._1(), pair._2()); + final String name = pair._1(), value = pair._2(); + prop.taint(ctx, name, SourceTypes.REQUEST_PARAMETER_NAME, name); + prop.taint(ctx, value, SourceTypes.REQUEST_PARAMETER_VALUE, name); } } } diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintCookieFunction.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintCookieFunction.java index 2d9c23c51ba..0955a0a9e49 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintCookieFunction.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintCookieFunction.java @@ -16,10 +16,13 @@ public Tuple1 apply(Tuple1 v1) { HttpCookiePair httpCookiePair = v1._1(); PropagationModule mod = InstrumentationBridge.PROPAGATION; - if (mod == null) { + if (mod == null || httpCookiePair == null) { return v1; } - mod.taint(SourceTypes.REQUEST_COOKIE_VALUE, httpCookiePair.name(), httpCookiePair.value()); + final String name = httpCookiePair.name(); + final String value = httpCookiePair.value(); + mod.taint(name, SourceTypes.REQUEST_COOKIE_NAME, name); + mod.taint(value, SourceTypes.REQUEST_COOKIE_VALUE, name); return v1; } } diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintFutureHelper.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintFutureHelper.java index 922397f52f6..c1ff9dbc8c2 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintFutureHelper.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintFutureHelper.java @@ -10,7 +10,7 @@ public static Future wrapFuture( Future f, Object input, PropagationModule mod, ExecutionContext ec) { JFunction1 mapf = t -> { - mod.taintIfInputIsTainted(t, input); + mod.taintIfTainted(t, input); return t; }; return f.map(mapf, ec); diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintMapFunction.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintMapFunction.java index 40d75a9e15f..705b1921742 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintMapFunction.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintMapFunction.java @@ -1,9 +1,9 @@ package datadog.trace.instrumentation.akkahttp.iast.helpers; +import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.InstrumentationBridge; import datadog.trace.api.iast.SourceTypes; import datadog.trace.api.iast.propagation.PropagationModule; -import datadog.trace.api.iast.source.WebModule; import scala.Tuple1; import scala.Tuple2; import scala.collection.Iterator; @@ -19,18 +19,17 @@ public Tuple1> apply(Tuple1> v1) { Map m = v1._1; PropagationModule prop = InstrumentationBridge.PROPAGATION; - WebModule web = InstrumentationBridge.WEB; - if (web == null || prop == null || m == null) { + if (prop == null || m == null || m.isEmpty()) { return v1; } - java.util.List keysAsCollection = ScalaToJava.keySetAsCollection(m); - web.onParameterNames(keysAsCollection); - + final IastContext ctx = IastContext.Provider.get(); Iterator> iterator = m.iterator(); while (iterator.hasNext()) { Tuple2 e = iterator.next(); - prop.taint(SourceTypes.REQUEST_PARAMETER_VALUE, e._1(), e._2()); + final String name = e._1(), value = e._2(); + prop.taint(ctx, name, SourceTypes.REQUEST_PARAMETER_NAME, name); + prop.taint(ctx, value, SourceTypes.REQUEST_PARAMETER_VALUE, name); } return v1; diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintMultiMapFunction.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintMultiMapFunction.java index dcfe919d598..b72ac179c0b 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintMultiMapFunction.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintMultiMapFunction.java @@ -1,7 +1,9 @@ package datadog.trace.instrumentation.akkahttp.iast.helpers; +import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.InstrumentationBridge; -import datadog.trace.api.iast.source.WebModule; +import datadog.trace.api.iast.SourceTypes; +import datadog.trace.api.iast.propagation.PropagationModule; import scala.Tuple1; import scala.Tuple2; import scala.collection.Iterator; @@ -17,19 +19,21 @@ public class TaintMultiMapFunction public Tuple1>> apply(Tuple1>> v1) { Map> m = v1._1; - WebModule mod = InstrumentationBridge.WEB; - if (mod == null || m == null) { + PropagationModule mod = InstrumentationBridge.PROPAGATION; + if (mod == null || m == null || m.isEmpty()) { return v1; } - java.util.List keysAsCollection = ScalaToJava.keySetAsCollection(m); - mod.onParameterNames(keysAsCollection); - + final IastContext ctx = IastContext.Provider.get(); Iterator>> entriesIterator = m.iterator(); while (entriesIterator.hasNext()) { Tuple2> e = entriesIterator.next(); + final String name = e._1(); + mod.taint(ctx, name, SourceTypes.REQUEST_PARAMETER_NAME, name); List values = e._2(); - mod.onParameterValues(e._1(), ScalaToJava.listAsList(values)); + for (final String value : ScalaToJava.listAsList(values)) { + mod.taint(ctx, value, SourceTypes.REQUEST_PARAMETER_VALUE, name); + } } return v1; diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintOptionalCookieFunction.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintOptionalCookieFunction.java index 1f6d8bb62a2..620bea2ebf8 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintOptionalCookieFunction.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintOptionalCookieFunction.java @@ -17,13 +17,14 @@ public Tuple1> apply(Tuple1> v1) { Option httpCookiePair = v1._1(); PropagationModule mod = InstrumentationBridge.PROPAGATION; - if (mod == null || httpCookiePair.isEmpty()) { + if (mod == null || httpCookiePair == null || httpCookiePair.isEmpty()) { return v1; } - mod.taint( - SourceTypes.REQUEST_COOKIE_VALUE, - httpCookiePair.get().name(), - httpCookiePair.get().value()); + final HttpCookiePair cookie = httpCookiePair.get(); + final String name = cookie.name(); + final String value = cookie.value(); + mod.taint(name, SourceTypes.REQUEST_COOKIE_NAME, name); + mod.taint(value, SourceTypes.REQUEST_COOKIE_VALUE, name); return v1; } } diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintRequestContextFunction.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintRequestContextFunction.java index c4c8cbb2d5c..bc86e937643 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintRequestContextFunction.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintRequestContextFunction.java @@ -16,10 +16,10 @@ public Tuple1 apply(Tuple1 v1) { RequestContext reqCtx = v1._1(); PropagationModule mod = InstrumentationBridge.PROPAGATION; - if (mod == null) { + if (mod == null || reqCtx == null) { return v1; } - mod.taintObject(SourceTypes.REQUEST_BODY, reqCtx); + mod.taint(reqCtx, SourceTypes.REQUEST_BODY); return v1; } diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintRequestFunction.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintRequestFunction.java index e1b9a5e4af4..7894326ab89 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintRequestFunction.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintRequestFunction.java @@ -17,10 +17,10 @@ public Tuple1 apply(Tuple1 v1) { HttpRequest httpRequest = v1._1(); PropagationModule mod = InstrumentationBridge.PROPAGATION; - if (mod == null) { + if (mod == null || httpRequest == null) { return v1; } - mod.taintObject(SourceTypes.REQUEST_BODY, httpRequest); + mod.taint(httpRequest, SourceTypes.REQUEST_BODY); return v1; } diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintSeqFunction.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintSeqFunction.java index 0d41fc32e56..fdec8e35b4e 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintSeqFunction.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintSeqFunction.java @@ -1,9 +1,9 @@ package datadog.trace.instrumentation.akkahttp.iast.helpers; +import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.InstrumentationBridge; import datadog.trace.api.iast.SourceTypes; import datadog.trace.api.iast.propagation.PropagationModule; -import datadog.trace.api.iast.source.WebModule; import java.util.Collections; import java.util.IdentityHashMap; import java.util.Set; @@ -22,22 +22,22 @@ public class TaintSeqFunction public Tuple1>> apply(Tuple1>> v1) { Seq> seq = v1._1; - WebModule web = InstrumentationBridge.WEB; PropagationModule prop = InstrumentationBridge.PROPAGATION; - if (web == null || prop == null || seq == null) { + if (prop == null || seq == null || seq.isEmpty()) { return v1; } + final IastContext ctx = IastContext.Provider.get(); Iterator> iterator = seq.iterator(); - Set seenKeys = Collections.newSetFromMap(new IdentityHashMap()); + Set seenKeys = Collections.newSetFromMap(new IdentityHashMap<>()); while (iterator.hasNext()) { Tuple2 t = iterator.next(); String name = t._1(); String value = t._2(); if (seenKeys.add(name)) { - web.onParameterNames(Collections.singleton(name)); + prop.taint(ctx, name, SourceTypes.REQUEST_PARAMETER_NAME, name); } - prop.taint(SourceTypes.REQUEST_PARAMETER_VALUE, name, value); + prop.taint(ctx, value, SourceTypes.REQUEST_PARAMETER_VALUE, name); } return v1; diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintSingleParameterFunction.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintSingleParameterFunction.java index e3130be8062..f6c1073fff8 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintSingleParameterFunction.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintSingleParameterFunction.java @@ -1,5 +1,6 @@ package datadog.trace.instrumentation.akkahttp.iast.helpers; +import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.InstrumentationBridge; import datadog.trace.api.iast.SourceTypes; import datadog.trace.api.iast.propagation.PropagationModule; @@ -40,15 +41,16 @@ public Tuple1 apply(Tuple1 v1) { } if (value instanceof Iterable) { - Iterator iterator = ((Iterable) value).iterator(); + final IastContext ctx = IastContext.Provider.get(); + Iterator iterator = ((Iterable) value).iterator(); while (iterator.hasNext()) { Object o = iterator.next(); if (o instanceof String) { - mod.taint(SourceTypes.REQUEST_PARAMETER_VALUE, paramName, (String) o); + mod.taint(ctx, (String) o, SourceTypes.REQUEST_PARAMETER_VALUE, paramName); } } } else if (value instanceof String) { - mod.taint(SourceTypes.REQUEST_PARAMETER_VALUE, paramName, (String) value); + mod.taint((String) value, SourceTypes.REQUEST_PARAMETER_VALUE, paramName); } return v1; diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintUnmarshaller.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintUnmarshaller.java index 88d1580643a..d109cc92d9d 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintUnmarshaller.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintUnmarshaller.java @@ -28,7 +28,7 @@ public TaintUnmarshaller(PropagationModule propagationModule, Unmarshaller @Override public Future apply(A value, ExecutionContext ec, Materializer materializer) { - propagationModule.taintObject(SourceTypes.REQUEST_BODY, value); + propagationModule.taint(value, SourceTypes.REQUEST_BODY); return delegate.apply(value, ec, materializer); } diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintUriFunction.java b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintUriFunction.java index d11830b047e..5feb8315119 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintUriFunction.java +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/iast/helpers/TaintUriFunction.java @@ -18,7 +18,7 @@ public Tuple1 apply(Tuple1 v1) { if (mod == null) { return v1; } - mod.taintObject(SourceTypes.REQUEST_QUERY, uri); + mod.taint(uri, SourceTypes.REQUEST_QUERY); return v1; } diff --git a/dd-java-agent/instrumentation/akka-http-10.2-iast/src/main/java/datadog/trace/instrumentation/akkahttp102/iast/helpers/TaintParametersFunction.java b/dd-java-agent/instrumentation/akka-http-10.2-iast/src/main/java/datadog/trace/instrumentation/akkahttp102/iast/helpers/TaintParametersFunction.java index 213ee7638e1..87008ac5ab4 100644 --- a/dd-java-agent/instrumentation/akka-http-10.2-iast/src/main/java/datadog/trace/instrumentation/akkahttp102/iast/helpers/TaintParametersFunction.java +++ b/dd-java-agent/instrumentation/akka-http-10.2-iast/src/main/java/datadog/trace/instrumentation/akkahttp102/iast/helpers/TaintParametersFunction.java @@ -1,5 +1,6 @@ package datadog.trace.instrumentation.akkahttp102.iast.helpers; +import datadog.trace.api.iast.IastContext; import datadog.trace.api.iast.InstrumentationBridge; import datadog.trace.api.iast.SourceTypes; import datadog.trace.api.iast.propagation.PropagationModule; @@ -33,15 +34,16 @@ public Tuple1 apply(Tuple1 v1) { } if (value instanceof Iterable) { - Iterator iterator = ((Iterable) value).iterator(); + final IastContext ctx = IastContext.Provider.get(); + Iterator iterator = ((Iterable) value).iterator(); while (iterator.hasNext()) { Object o = iterator.next(); if (o instanceof String) { - mod.taint(SourceTypes.REQUEST_PARAMETER_VALUE, paramName, (String) o); + mod.taint(ctx, (String) o, SourceTypes.REQUEST_PARAMETER_VALUE, paramName); } } } else if (value instanceof String) { - mod.taint(SourceTypes.REQUEST_PARAMETER_VALUE, paramName, (String) value); + mod.taint((String) value, SourceTypes.REQUEST_PARAMETER_VALUE, paramName); } return v1; diff --git a/dd-java-agent/instrumentation/commons-httpclient-2/src/main/java/datadog/trace/instrumentation/commonshttpclient/IastHttpMethodBaseInstrumentation.java b/dd-java-agent/instrumentation/commons-httpclient-2/src/main/java/datadog/trace/instrumentation/commonshttpclient/IastHttpMethodBaseInstrumentation.java index 8371ad69442..11522f224fd 100644 --- a/dd-java-agent/instrumentation/commons-httpclient-2/src/main/java/datadog/trace/instrumentation/commonshttpclient/IastHttpMethodBaseInstrumentation.java +++ b/dd-java-agent/instrumentation/commons-httpclient-2/src/main/java/datadog/trace/instrumentation/commonshttpclient/IastHttpMethodBaseInstrumentation.java @@ -51,7 +51,7 @@ public static void afterCtor( @Advice.This final Object self, @Advice.Argument(0) final Object argument) { final PropagationModule module = InstrumentationBridge.PROPAGATION; if (module != null) { - module.taintIfInputIsTainted(self, argument); + module.taintIfTainted(self, argument); } } } diff --git a/dd-java-agent/instrumentation/commons-httpclient-2/src/test/groovy/IastCommonsHttpClientInstrumentationTest.groovy b/dd-java-agent/instrumentation/commons-httpclient-2/src/test/groovy/IastCommonsHttpClientInstrumentationTest.groovy index 3624973f60e..0a3f5ba5bc7 100644 --- a/dd-java-agent/instrumentation/commons-httpclient-2/src/test/groovy/IastCommonsHttpClientInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/commons-httpclient-2/src/test/groovy/IastCommonsHttpClientInstrumentationTest.groovy @@ -51,7 +51,7 @@ class IastCommonsHttpClientInstrumentationTest extends AgentTestRunner { private void mockPropagation() { final propagation = Mock(PropagationModule) { - taintIfInputIsTainted(_, _) >> { + taintIfTainted(_, _) >> { if (tainteds.containsKey(it[1])) { tainteds.put(it[0], null) } diff --git a/dd-java-agent/instrumentation/commons-lang-2/src/main/java/datadog/trace/instrumentation/commonslang/StringEscapeUtilsCallSite.java b/dd-java-agent/instrumentation/commons-lang-2/src/main/java/datadog/trace/instrumentation/commonslang/StringEscapeUtilsCallSite.java index 63e71ac0325..d7fdd84417d 100644 --- a/dd-java-agent/instrumentation/commons-lang-2/src/main/java/datadog/trace/instrumentation/commonslang/StringEscapeUtilsCallSite.java +++ b/dd-java-agent/instrumentation/commons-lang-2/src/main/java/datadog/trace/instrumentation/commonslang/StringEscapeUtilsCallSite.java @@ -6,7 +6,7 @@ import datadog.trace.api.iast.Propagation; import datadog.trace.api.iast.VulnerabilityMarks; import datadog.trace.api.iast.propagation.PropagationModule; -import javax.annotation.Nonnull; +import javax.annotation.Nullable; @Propagation @CallSite(spi = IastCallSites.class) @@ -21,11 +21,11 @@ public class StringEscapeUtilsCallSite { @CallSite.After( "java.lang.String org.apache.commons.lang.StringEscapeUtils.escapeXml(java.lang.String)") public static String afterEscape( - @CallSite.Argument(0) @Nonnull final String input, @CallSite.Return final String result) { + @CallSite.Argument(0) @Nullable final String input, @CallSite.Return final String result) { final PropagationModule module = InstrumentationBridge.PROPAGATION; if (module != null) { try { - module.taintIfInputIsTaintedWithMarks(result, input, VulnerabilityMarks.XSS_MARK); + module.taintIfTainted(result, input, false, VulnerabilityMarks.XSS_MARK); } catch (final Throwable e) { module.onUnexpectedException("afterEscape threw", e); } @@ -36,11 +36,11 @@ public static String afterEscape( @CallSite.After( "java.lang.String org.apache.commons.lang.StringEscapeUtils.escapeSql(java.lang.String)") public static String afterEscapeSQL( - @CallSite.Argument(0) @Nonnull final String input, @CallSite.Return final String result) { + @CallSite.Argument(0) @Nullable final String input, @CallSite.Return final String result) { final PropagationModule module = InstrumentationBridge.PROPAGATION; if (module != null) { try { - module.taintIfInputIsTaintedWithMarks(result, input, VulnerabilityMarks.SQL_INJECTION_MARK); + module.taintIfTainted(result, input, false, VulnerabilityMarks.SQL_INJECTION_MARK); } catch (final Throwable e) { module.onUnexpectedException("afterEscapeSQL threw", e); } diff --git a/dd-java-agent/instrumentation/commons-lang-2/src/test/groovy/datadog/trace/instrumentation/commonslang/StringEscapeUtilsCallSiteTest.groovy b/dd-java-agent/instrumentation/commons-lang-2/src/test/groovy/datadog/trace/instrumentation/commonslang/StringEscapeUtilsCallSiteTest.groovy index 7a4d9457619..348aaa57503 100644 --- a/dd-java-agent/instrumentation/commons-lang-2/src/test/groovy/datadog/trace/instrumentation/commonslang/StringEscapeUtilsCallSiteTest.groovy +++ b/dd-java-agent/instrumentation/commons-lang-2/src/test/groovy/datadog/trace/instrumentation/commonslang/StringEscapeUtilsCallSiteTest.groovy @@ -27,7 +27,7 @@ class StringEscapeUtilsCallSiteTest extends AgentTestRunner { then: result == expected - 1 * module.taintIfInputIsTaintedWithMarks(_ as String, args[0], mark) + 1 * module.taintIfTainted(_ as String, args[0], false, mark) 0 * _ where: diff --git a/dd-java-agent/instrumentation/commons-lang-3/src/main/java/datadog/trace/instrumentation/commonslang3/StringEscapeUtilsCallSite.java b/dd-java-agent/instrumentation/commons-lang-3/src/main/java/datadog/trace/instrumentation/commonslang3/StringEscapeUtilsCallSite.java index e151128f760..b52f57c57e9 100644 --- a/dd-java-agent/instrumentation/commons-lang-3/src/main/java/datadog/trace/instrumentation/commonslang3/StringEscapeUtilsCallSite.java +++ b/dd-java-agent/instrumentation/commons-lang-3/src/main/java/datadog/trace/instrumentation/commonslang3/StringEscapeUtilsCallSite.java @@ -6,7 +6,7 @@ import datadog.trace.api.iast.Propagation; import datadog.trace.api.iast.VulnerabilityMarks; import datadog.trace.api.iast.propagation.PropagationModule; -import javax.annotation.Nonnull; +import javax.annotation.Nullable; @Propagation @CallSite(spi = IastCallSites.class) @@ -23,11 +23,11 @@ public class StringEscapeUtilsCallSite { @CallSite.After( "java.lang.String org.apache.commons.lang3.StringEscapeUtils.escapeEcmaScript(java.lang.String)") public static String afterEscape( - @CallSite.Argument(0) @Nonnull final String input, @CallSite.Return final String result) { + @CallSite.Argument(0) @Nullable final String input, @CallSite.Return final String result) { final PropagationModule module = InstrumentationBridge.PROPAGATION; if (module != null) { try { - module.taintIfInputIsTaintedWithMarks(result, input, VulnerabilityMarks.XSS_MARK); + module.taintIfTainted(result, input, false, VulnerabilityMarks.XSS_MARK); } catch (final Throwable e) { module.onUnexpectedException("afterEscape threw", e); } @@ -38,11 +38,11 @@ public static String afterEscape( @CallSite.After( "java.lang.String org.apache.commons.lang3.StringEscapeUtils.escapeJson(java.lang.String)") public static String afterEscapeJson( - @CallSite.Argument(0) @Nonnull final String input, @CallSite.Return final String result) { + @CallSite.Argument(0) @Nullable final String input, @CallSite.Return final String result) { final PropagationModule module = InstrumentationBridge.PROPAGATION; if (module != null) { try { - module.taintIfInputIsTainted(result, input); + module.taintIfTainted(result, input); } catch (final Throwable e) { module.onUnexpectedException("afterEscapeJson threw", e); } diff --git a/dd-java-agent/instrumentation/commons-lang-3/src/test/groovy/datadog/trace/instrumentation/commonslang3/StringEscapeUtilsCallSiteTest.groovy b/dd-java-agent/instrumentation/commons-lang-3/src/test/groovy/datadog/trace/instrumentation/commonslang3/StringEscapeUtilsCallSiteTest.groovy index dc7147fe6e4..18730b70612 100644 --- a/dd-java-agent/instrumentation/commons-lang-3/src/test/groovy/datadog/trace/instrumentation/commonslang3/StringEscapeUtilsCallSiteTest.groovy +++ b/dd-java-agent/instrumentation/commons-lang-3/src/test/groovy/datadog/trace/instrumentation/commonslang3/StringEscapeUtilsCallSiteTest.groovy @@ -25,7 +25,7 @@ class StringEscapeUtilsCallSiteTest extends AgentTestRunner { then: result == expected - 1 * module.taintIfInputIsTaintedWithMarks(_ as String, args[0], VulnerabilityMarks.XSS_MARK) + 1 * module.taintIfTainted(_ as String, args[0], false, VulnerabilityMarks.XSS_MARK) 0 * _ where: @@ -47,7 +47,7 @@ class StringEscapeUtilsCallSiteTest extends AgentTestRunner { then: result == expected - 1 * module.taintIfInputIsTainted(_ as String, args[0]) + 1 * module.taintIfTainted(_ as String, args[0]) 0 * _ where: diff --git a/dd-java-agent/instrumentation/commons-text/src/main/java/datadog/trace/instrumentation/commonstext/StringEscapeUtilsCallSite.java b/dd-java-agent/instrumentation/commons-text/src/main/java/datadog/trace/instrumentation/commonstext/StringEscapeUtilsCallSite.java index 68620efd078..ebd592fa241 100644 --- a/dd-java-agent/instrumentation/commons-text/src/main/java/datadog/trace/instrumentation/commonstext/StringEscapeUtilsCallSite.java +++ b/dd-java-agent/instrumentation/commons-text/src/main/java/datadog/trace/instrumentation/commonstext/StringEscapeUtilsCallSite.java @@ -6,7 +6,7 @@ import datadog.trace.api.iast.Propagation; import datadog.trace.api.iast.VulnerabilityMarks; import datadog.trace.api.iast.propagation.PropagationModule; -import javax.annotation.Nonnull; +import javax.annotation.Nullable; @Propagation @CallSite(spi = IastCallSites.class) @@ -25,11 +25,11 @@ public class StringEscapeUtilsCallSite { @CallSite.After( "java.lang.String org.apache.commons.text.StringEscapeUtils.escapeXml11(java.lang.String)") public static String afterEscape( - @CallSite.Argument(0) @Nonnull final String input, @CallSite.Return final String result) { + @CallSite.Argument(0) @Nullable final String input, @CallSite.Return final String result) { final PropagationModule module = InstrumentationBridge.PROPAGATION; if (module != null) { try { - module.taintIfInputIsTaintedWithMarks(result, input, VulnerabilityMarks.XSS_MARK); + module.taintIfTainted(result, input, false, VulnerabilityMarks.XSS_MARK); } catch (final Throwable e) { module.onUnexpectedException("afterEscape threw", e); } @@ -40,11 +40,11 @@ public static String afterEscape( @CallSite.After( "java.lang.String org.apache.commons.text.StringEscapeUtils.escapeJson(java.lang.String)") public static String afterEscapeJson( - @CallSite.Argument(0) @Nonnull final String input, @CallSite.Return final String result) { + @CallSite.Argument(0) @Nullable final String input, @CallSite.Return final String result) { final PropagationModule module = InstrumentationBridge.PROPAGATION; if (module != null) { try { - module.taintIfInputIsTainted(result, input); + module.taintIfTainted(result, input); } catch (final Throwable e) { module.onUnexpectedException("afterEscapeJson threw", e); } diff --git a/dd-java-agent/instrumentation/commons-text/src/test/groovy/datadog/trace/instrumentation/commonstext/StringEscapeUtilsCallSiteTest.groovy b/dd-java-agent/instrumentation/commons-text/src/test/groovy/datadog/trace/instrumentation/commonstext/StringEscapeUtilsCallSiteTest.groovy index 12296ad58c2..440e31ea3be 100644 --- a/dd-java-agent/instrumentation/commons-text/src/test/groovy/datadog/trace/instrumentation/commonstext/StringEscapeUtilsCallSiteTest.groovy +++ b/dd-java-agent/instrumentation/commons-text/src/test/groovy/datadog/trace/instrumentation/commonstext/StringEscapeUtilsCallSiteTest.groovy @@ -25,7 +25,7 @@ class StringEscapeUtilsCallSiteTest extends AgentTestRunner { then: result == expected - 1 * module.taintIfInputIsTaintedWithMarks(_ as String, args[0], VulnerabilityMarks.XSS_MARK) + 1 * module.taintIfTainted(_ as String, args[0], false, VulnerabilityMarks.XSS_MARK) 0 * _ where: @@ -48,7 +48,7 @@ class StringEscapeUtilsCallSiteTest extends AgentTestRunner { then: result == expected - 1 * module.taintIfInputIsTainted(_ as String, args[0]) + 1 * module.taintIfTainted(_ as String, args[0]) 0 * _ where: diff --git a/dd-java-agent/instrumentation/freemarker/src/main/java/datadog/trace/instrumentation/freemarker/StringUtilCallSite.java b/dd-java-agent/instrumentation/freemarker/src/main/java/datadog/trace/instrumentation/freemarker/StringUtilCallSite.java index 8ee9bc4ea2a..391339f528b 100644 --- a/dd-java-agent/instrumentation/freemarker/src/main/java/datadog/trace/instrumentation/freemarker/StringUtilCallSite.java +++ b/dd-java-agent/instrumentation/freemarker/src/main/java/datadog/trace/instrumentation/freemarker/StringUtilCallSite.java @@ -29,7 +29,7 @@ public static String afterEscape( final PropagationModule module = InstrumentationBridge.PROPAGATION; if (module != null) { try { - module.taintIfInputIsTaintedWithMarks(result, input, VulnerabilityMarks.XSS_MARK); + module.taintIfTainted(result, input, false, VulnerabilityMarks.XSS_MARK); } catch (final Throwable e) { module.onUnexpectedException("afterEscape threw", e); } diff --git a/dd-java-agent/instrumentation/freemarker/src/test/groovy/datadog/trace/instrumentation/freemarker/StringUtilCallSiteTest.groovy b/dd-java-agent/instrumentation/freemarker/src/test/groovy/datadog/trace/instrumentation/freemarker/StringUtilCallSiteTest.groovy index 51b0512a5e3..18d86d6aef5 100644 --- a/dd-java-agent/instrumentation/freemarker/src/test/groovy/datadog/trace/instrumentation/freemarker/StringUtilCallSiteTest.groovy +++ b/dd-java-agent/instrumentation/freemarker/src/test/groovy/datadog/trace/instrumentation/freemarker/StringUtilCallSiteTest.groovy @@ -23,17 +23,17 @@ class StringUtilCallSiteTest extends AgentTestRunner { then: result == expected - 1 * module.taintIfInputIsTaintedWithMarks(_ as String, args[0], VulnerabilityMarks.XSS_MARK) + 1 * module.taintIfTainted(_ as String, args[0], false, VulnerabilityMarks.XSS_MARK) 0 * _ where: - method | args | expected - 'HTMLEnc' | ['"escape this < '] | '<htmlTag>"escape this < </htmlTag>' - 'XMLEnc' | ['"escape this < '] | '<xmlTag>"escape this < </xmlTag>' - 'XHTMLEnc' | ['"escape this < '] | '<htmlTag>"escape this < </htmlTag>' - 'javaStringEnc' | ['