diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/iast/NamedContext.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/iast/NamedContext.java index a78c0591f50..9132b703bf3 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/iast/NamedContext.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/iast/NamedContext.java @@ -19,6 +19,8 @@ public abstract class NamedContext { public abstract void taintName(@Nullable String name); + public abstract void setCurrentName(@Nullable final String name); + @Nonnull public static NamedContext getOrCreate( @Nonnull final ContextStore store, @Nonnull final E target) { @@ -47,6 +49,9 @@ public void taintValue(@Nullable final String value) {} @Override public void taintName(@Nullable final String name) {} + + @Override + public void setCurrentName(@Nullable final String name) {} } private static class NamedContextImpl extends NamedContext { @@ -78,6 +83,11 @@ public void taintName(@Nullable final String name) { } } + @Override + public void setCurrentName(@Nullable final String name) { + currentName = name; + } + private IastContext iastCtx() { if (!fetched) { fetched = true; diff --git a/dd-java-agent/instrumentation/akka-http/akka-http-10.0/build.gradle b/dd-java-agent/instrumentation/akka-http/akka-http-10.0/build.gradle index ed0efec6969..3ba83d351dd 100644 --- a/dd-java-agent/instrumentation/akka-http/akka-http-10.0/build.gradle +++ b/dd-java-agent/instrumentation/akka-http/akka-http-10.0/build.gradle @@ -119,6 +119,7 @@ dependencies { iastTestImplementation(testFixtures(project(':dd-java-agent:agent-iast'))) iastTestCompileOnly group: 'de.thetaphi', name: 'forbiddenapis', version: '3.4' iastTestRuntimeOnly project(':dd-java-agent:instrumentation:jackson-core') + iastTestRuntimeOnly project(':dd-java-agent:instrumentation:jackson-core:jackson-core-2.8') iastTestRuntimeOnly project(':dd-java-agent:instrumentation:iast-instrumenter') iastTestRuntimeOnly project(':dd-java-agent:instrumentation:akka-http:akka-http-10.2-iast') @@ -161,6 +162,7 @@ dependencies { latestDepIastTestImplementation group: 'com.typesafe.akka', name: 'akka-actor_2.13', version: '2.8.+' latestDepIastTestImplementation group: 'com.typesafe.akka', name: 'akka-http-jackson_2.13', version: '[10.+,10.5.2)' latestDepIastTestImplementation(testFixtures(project(':dd-java-agent:agent-iast'))) + latestDepIastTestImplementation project(':dd-java-agent:instrumentation:jackson-core:jackson-core-2.12') lagomTestImplementation libs.scala211 lagomTestImplementation group: 'com.typesafe.akka', name: 'akka-http_2.11', version: '10.0.0' diff --git a/dd-java-agent/instrumentation/iast-instrumenter/src/main/resources/datadog/trace/instrumentation/iastinstrumenter/iast_exclusion.trie b/dd-java-agent/instrumentation/iast-instrumenter/src/main/resources/datadog/trace/instrumentation/iastinstrumenter/iast_exclusion.trie index 7d7d95269b1..f3a05cf60ab 100644 --- a/dd-java-agent/instrumentation/iast-instrumenter/src/main/resources/datadog/trace/instrumentation/iastinstrumenter/iast_exclusion.trie +++ b/dd-java-agent/instrumentation/iast-instrumenter/src/main/resources/datadog/trace/instrumentation/iastinstrumenter/iast_exclusion.trie @@ -125,6 +125,8 @@ 1 graphql.* 1 ibm.security.* 1 io.dropwizard.* +2 io.ebean.* +2 io.ebeaninternal.* 1 io.github.lukehutch.fastclasspathscanner.* 1 io.grpc.* 1 io.leangen.geantyref.* diff --git a/dd-java-agent/instrumentation/jackson-core/build.gradle b/dd-java-agent/instrumentation/jackson-core/build.gradle index d542b451c3c..d3e04981923 100644 --- a/dd-java-agent/instrumentation/jackson-core/build.gradle +++ b/dd-java-agent/instrumentation/jackson-core/build.gradle @@ -26,4 +26,7 @@ dependencies { testImplementation(group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion) testImplementation(group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion) + + latestDepTestImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.+' + latestDepTestImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.+' } diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/build.gradle b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/build.gradle new file mode 100644 index 00000000000..efd10d501d3 --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/build.gradle @@ -0,0 +1,23 @@ +muzzle { + pass { + group = 'com.fasterxml.jackson.core' + module = 'jackson-core' + versions = "[2.12.0, 2.16.0)" + } +} + +apply from: "$rootDir/gradle/java.gradle" + +addTestSuiteForDir('latestDepTest', 'test') + +final jacksonVersion = '2.12.0' +dependencies { + compileOnly(group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion) + compileOnly(group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion) + + testImplementation(group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion) + testImplementation(group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion) + + latestDepTestImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.15.+' + latestDepTestImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.15.+' +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/src/main/java/com/fasterxml/jackson/core/json/JsonParser212Helper.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/src/main/java/com/fasterxml/jackson/core/json/JsonParser212Helper.java new file mode 100644 index 00000000000..23f4b75697f --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/src/main/java/com/fasterxml/jackson/core/json/JsonParser212Helper.java @@ -0,0 +1,11 @@ +package com.fasterxml.jackson.core.json; + +import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer212Helper; + +public final class JsonParser212Helper { + private JsonParser212Helper() {} + + public static boolean fetchIntern(UTF8StreamJsonParser jsonParser) { + return ByteQuadsCanonicalizer212Helper.fetchIntern(jsonParser._symbols); + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer212Helper.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer212Helper.java new file mode 100644 index 00000000000..67359ef7889 --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer212Helper.java @@ -0,0 +1,9 @@ +package com.fasterxml.jackson.core.sym; + +public final class ByteQuadsCanonicalizer212Helper { + private ByteQuadsCanonicalizer212Helper() {} + + public static boolean fetchIntern(ByteQuadsCanonicalizer symbols) { + return symbols._intern; + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/src/main/java/datadog/trace/instrumentation/jackson_2_12/core/JsonParserInstrumentation.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/src/main/java/datadog/trace/instrumentation/jackson_2_12/core/JsonParserInstrumentation.java new file mode 100644 index 00000000000..1edb74d59ae --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/src/main/java/datadog/trace/instrumentation/jackson_2_12/core/JsonParserInstrumentation.java @@ -0,0 +1,103 @@ +package datadog.trace.instrumentation.jackson_2_12.core; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.hasClassNamed; +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.declaresMethod; +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.extendsClass; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.*; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf; +import static java.util.Collections.singletonMap; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.json.JsonParser212Helper; +import com.fasterxml.jackson.core.json.UTF8StreamJsonParser; +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.api.iast.Propagation; +import datadog.trace.bootstrap.ContextStore; +import datadog.trace.bootstrap.InstrumentationContext; +import datadog.trace.bootstrap.instrumentation.iast.NamedContext; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumenterModule.class) +public class JsonParserInstrumentation extends InstrumenterModule.Iast + implements Instrumenter.ForTypeHierarchy { + + static final String TARGET_TYPE = "com.fasterxml.jackson.core.JsonParser"; + static final ElementMatcher.Junction VERSION_POST_2_8_0_AND_PRE_2_12_0 = + hasClassNamed("com.fasterxml.jackson.core.StreamReadCapability") + .and(not(hasClassNamed("com.fasterxml.jackson.core.StreamWriteConstraints"))); + + public JsonParserInstrumentation() { + super("jackson", "jackson-2_12"); + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + final String className = JsonParserInstrumentation.class.getName(); + transformer.applyAdvice( + namedOneOf("getCurrentName", "nextFieldName") + .and(isPublic()) + .and(takesNoArguments()) + .and(returns(String.class)), + className + "$NameAdvice"); + } + + @Override + public String hierarchyMarkerType() { + return TARGET_TYPE; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return declaresMethod(namedOneOf("getCurrentName", "nextFieldName")) + .and( + extendsClass(named(hierarchyMarkerType())) + .and(namedNoneOf("com.fasterxml.jackson.core.base.ParserMinimalBase"))); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return VERSION_POST_2_8_0_AND_PRE_2_12_0; + } + + @Override + public Map contextStore() { + return singletonMap(TARGET_TYPE, "datadog.trace.bootstrap.instrumentation.iast.NamedContext"); + } + + @Override + public String[] helperClassNames() { + return new String[] { + "com.fasterxml.jackson.core.json" + ".JsonParser212Helper", + "com.fasterxml.jackson.core.sym" + ".ByteQuadsCanonicalizer212Helper", + }; + } + + public static class NameAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + @Propagation + public static void onExit(@Advice.This JsonParser jsonParser, @Advice.Return String result) { + if (jsonParser != null + && result != null + && jsonParser.getCurrentToken() == JsonToken.FIELD_NAME) { + final ContextStore store = + InstrumentationContext.get(JsonParser.class, NamedContext.class); + final NamedContext context = NamedContext.getOrCreate(store, jsonParser); + if (jsonParser instanceof UTF8StreamJsonParser + && JsonParser212Helper.fetchIntern((UTF8StreamJsonParser) jsonParser)) { + context.setCurrentName(result); + return; + } + context.taintName(result); + } + } + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/src/test/groovy/datadog/trace/instrumentation/jackson212/core/JsonParserInstrumentationTest.groovy b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/src/test/groovy/datadog/trace/instrumentation/jackson212/core/JsonParserInstrumentationTest.groovy new file mode 100644 index 00000000000..ddc746baf3c --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.12/src/test/groovy/datadog/trace/instrumentation/jackson212/core/JsonParserInstrumentationTest.groovy @@ -0,0 +1,98 @@ +package datadog.trace.instrumentation.jackson212.core + +import com.fasterxml.jackson.databind.ObjectMapper +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.api.iast.InstrumentationBridge +import datadog.trace.api.iast.SourceTypes +import datadog.trace.api.iast.Taintable +import datadog.trace.api.iast.propagation.PropagationModule +import groovy.json.JsonOutput + +import java.nio.charset.Charset + +class JsonParserInstrumentationTest extends AgentTestRunner { + + private final static String JSON_STRING = '{"root":"root_value","nested":["array_0","array_1"]}' + + @Override + protected void configurePreAgent() { + injectSysConfig("dd.iast.enabled", "true") + } + + void 'test json parsing (tainted)'() { + given: + final source = new SourceImpl(origin: SourceTypes.REQUEST_BODY, name: 'body', value: JSON_STRING) + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper().readerFor(Map) + + when: + final taintedResult = reader.readValue(target) as Map + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> source + 1 * module.taintString(_, 'root', source.origin, 'root', JSON_STRING) + 1 * module.taintString(_, 'nested', source.origin, 'nested', JSON_STRING) + 0 * _ + + where: + target << [JSON_STRING] + } + + void 'test json parsing (tainted but field names)'() { + given: + final source = new SourceImpl(origin: SourceTypes.REQUEST_BODY, name: 'body', value: JSON_STRING) + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper() + + when: + final taintedResult = reader.readValue(target, Map) + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> source + 0 * _ + + where: + target << [new ByteArrayInputStream(JSON_STRING.getBytes(Charset.defaultCharset()))] + } + + void 'test json parsing (not tainted)'() { + given: + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper().readerFor(Map) + + when: + final taintedResult = reader.readValue(target) as Map + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> null + 0 * _ + + where: + target << testSuite() + } + + private static List testSuite() { + return [JSON_STRING, new ByteArrayInputStream(JSON_STRING.getBytes(Charset.defaultCharset()))] + } + + private static class SourceImpl implements Taintable.Source { + byte origin + String name + String value + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/build.gradle b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/build.gradle new file mode 100644 index 00000000000..6afae303181 --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/build.gradle @@ -0,0 +1,23 @@ +muzzle { + pass { + group = 'com.fasterxml.jackson.core' + module = 'jackson-core' + versions = "[2.16.0,)" + } +} + +apply from: "$rootDir/gradle/java.gradle" + +addTestSuiteForDir('latestDepTest', 'test') + +final jacksonVersion = '2.16.0' +dependencies { + compileOnly(group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion) + compileOnly(group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion) + + testImplementation(group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion) + testImplementation(group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion) + + latestDepTestImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.+' + latestDepTestImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.+' +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/src/main/java/com/fasterxml/jackson/core/json/JsonParser216Helper.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/src/main/java/com/fasterxml/jackson/core/json/JsonParser216Helper.java new file mode 100644 index 00000000000..d2ef89cc015 --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/src/main/java/com/fasterxml/jackson/core/json/JsonParser216Helper.java @@ -0,0 +1,11 @@ +package com.fasterxml.jackson.core.json; + +import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer216Helper; + +public final class JsonParser216Helper { + private JsonParser216Helper() {} + + public static boolean fetchInterner(UTF8StreamJsonParser jsonParser) { + return ByteQuadsCanonicalizer216Helper.fetchInterner(jsonParser._symbols); + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer216Helper.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer216Helper.java new file mode 100644 index 00000000000..7c3a6794650 --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer216Helper.java @@ -0,0 +1,9 @@ +package com.fasterxml.jackson.core.sym; + +public final class ByteQuadsCanonicalizer216Helper { + private ByteQuadsCanonicalizer216Helper() {} + + public static boolean fetchInterner(ByteQuadsCanonicalizer symbols) { + return symbols._interner != null; + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/src/main/java/datadog/trace/instrumentation/jackson_2_16/core/JsonParserInstrumentation.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/src/main/java/datadog/trace/instrumentation/jackson_2_16/core/JsonParserInstrumentation.java new file mode 100644 index 00000000000..e472c1c6b4c --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/src/main/java/datadog/trace/instrumentation/jackson_2_16/core/JsonParserInstrumentation.java @@ -0,0 +1,102 @@ +package datadog.trace.instrumentation.jackson_2_16.core; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.hasClassNamed; +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.declaresMethod; +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.extendsClass; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.*; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf; +import static java.util.Collections.singletonMap; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.json.JsonParser216Helper; +import com.fasterxml.jackson.core.json.UTF8StreamJsonParser; +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.api.iast.Propagation; +import datadog.trace.bootstrap.ContextStore; +import datadog.trace.bootstrap.InstrumentationContext; +import datadog.trace.bootstrap.instrumentation.iast.NamedContext; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumenterModule.class) +public class JsonParserInstrumentation extends InstrumenterModule.Iast + implements Instrumenter.ForTypeHierarchy { + + static final String TARGET_TYPE = "com.fasterxml.jackson.core.JsonParser"; + static final ElementMatcher.Junction VERSION_POST_2_16_0 = + hasClassNamed("com.fasterxml.jackson.core.StreamWriteConstraints"); + + public JsonParserInstrumentation() { + super("jackson", "jackson-2_16"); + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + final String className = JsonParserInstrumentation.class.getName(); + transformer.applyAdvice( + namedOneOf("getCurrentName", "nextFieldName") + .and(isPublic()) + .and(takesNoArguments()) + .and(returns(String.class)), + className + "$NameAdvice"); + } + + @Override + public String hierarchyMarkerType() { + return TARGET_TYPE; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return declaresMethod(namedOneOf("getCurrentName", "nextFieldName")) + .and( + extendsClass(named(hierarchyMarkerType())) + .and(namedNoneOf("com.fasterxml.jackson.core.base.ParserMinimalBase"))); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return VERSION_POST_2_16_0; + } + + @Override + public Map contextStore() { + return singletonMap(TARGET_TYPE, "datadog.trace.bootstrap.instrumentation.iast.NamedContext"); + } + + @Override + public String[] helperClassNames() { + return new String[] { + "com.fasterxml.jackson.core.json" + ".JsonParser216Helper", + "com.fasterxml.jackson.core.sym" + ".ByteQuadsCanonicalizer216Helper", + }; + } + + public static class NameAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + @Propagation + public static void onExit(@Advice.This JsonParser jsonParser, @Advice.Return String result) { + if (jsonParser != null + && result != null + && jsonParser.getCurrentToken() == JsonToken.FIELD_NAME) { + final ContextStore store = + InstrumentationContext.get(JsonParser.class, NamedContext.class); + final NamedContext context = NamedContext.getOrCreate(store, jsonParser); + if (jsonParser instanceof UTF8StreamJsonParser + && JsonParser216Helper.fetchInterner((UTF8StreamJsonParser) jsonParser)) { + context.setCurrentName(result); + return; + } + context.taintName(result); + } + } + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/src/test/groovy/Json2ParserInstrumentationTest.groovy b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/src/test/groovy/datadog/trace/instrumentation/jackson216/core/JsonParserInstrumentationTest.groovy similarity index 72% rename from dd-java-agent/instrumentation/jackson-core/src/test/groovy/Json2ParserInstrumentationTest.groovy rename to dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/src/test/groovy/datadog/trace/instrumentation/jackson216/core/JsonParserInstrumentationTest.groovy index 52027c39a75..4f5d2a9a375 100644 --- a/dd-java-agent/instrumentation/jackson-core/src/test/groovy/Json2ParserInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.16/src/test/groovy/datadog/trace/instrumentation/jackson216/core/JsonParserInstrumentationTest.groovy @@ -1,3 +1,5 @@ +package datadog.trace.instrumentation.jackson216.core + import com.fasterxml.jackson.databind.ObjectMapper import datadog.trace.agent.test.AgentTestRunner import datadog.trace.api.iast.InstrumentationBridge @@ -8,7 +10,7 @@ import groovy.json.JsonOutput import java.nio.charset.Charset -class Json2ParserInstrumentationTest extends AgentTestRunner { +class JsonParserInstrumentationTest extends AgentTestRunner { private final static String JSON_STRING = '{"root":"root_value","nested":{"nested_array":["array_0","array_1"]}}' @@ -34,15 +36,34 @@ class Json2ParserInstrumentationTest extends AgentTestRunner { _ * module.taintObjectIfTainted(_, _) _ * module.findSource(_) >> source 1 * module.taintString(_, 'root', source.origin, 'root', JSON_STRING) - 1 * module.taintString(_, 'root_value', source.origin, 'root', JSON_STRING) 1 * module.taintString(_, 'nested', source.origin, 'nested', JSON_STRING) 1 * module.taintString(_, 'nested_array', source.origin, 'nested_array', JSON_STRING) - 1 * module.taintString(_, 'array_0', source.origin, 'nested_array', JSON_STRING) - 1 * module.taintString(_, 'array_1', source.origin, 'nested_array', JSON_STRING) 0 * _ where: - target << testSuite() + target << [JSON_STRING] + } + + void 'test json parsing (tainted but field names)'() { + given: + final source = new SourceImpl(origin: SourceTypes.REQUEST_BODY, name: 'body', value: JSON_STRING) + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper() + + when: + final taintedResult = reader.readValue(target, Map) + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> source + 0 * _ + + where: + target << [new ByteArrayInputStream(JSON_STRING.getBytes(Charset.defaultCharset()))] } void 'test json parsing (not tainted)'() { diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/build.gradle b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/build.gradle new file mode 100644 index 00000000000..4e25df16206 --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/build.gradle @@ -0,0 +1,23 @@ +muzzle { + pass { + group = 'com.fasterxml.jackson.core' + module = 'jackson-core' + versions = "[2.6.0, 2.8.0)" + } +} + +apply from: "$rootDir/gradle/java.gradle" + +addTestSuiteForDir('latestDepTest', 'test') + +final jacksonVersion = '2.6.0' +dependencies { + compileOnly(group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion) + compileOnly(group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion) + + testImplementation(group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion) + testImplementation(group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion) + + latestDepTestImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.7.+' + latestDepTestImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.7.+' +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/src/main/java/com/fasterxml/jackson/core/json/JsonParser26Helper.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/src/main/java/com/fasterxml/jackson/core/json/JsonParser26Helper.java new file mode 100644 index 00000000000..c429359ff7b --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/src/main/java/com/fasterxml/jackson/core/json/JsonParser26Helper.java @@ -0,0 +1,11 @@ +package com.fasterxml.jackson.core.json; + +import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer26Helper; + +public final class JsonParser26Helper { + private JsonParser26Helper() {} + + public static boolean fetchIntern(UTF8StreamJsonParser jsonParser) { + return ByteQuadsCanonicalizer26Helper.fetchIntern(jsonParser._symbols); + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer26Helper.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer26Helper.java new file mode 100644 index 00000000000..6e3a3bf26aa --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer26Helper.java @@ -0,0 +1,9 @@ +package com.fasterxml.jackson.core.sym; + +public final class ByteQuadsCanonicalizer26Helper { + private ByteQuadsCanonicalizer26Helper() {} + + public static boolean fetchIntern(ByteQuadsCanonicalizer symbols) { + return symbols._intern; + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/src/main/java/datadog/trace/instrumentation/jackson_2_6/core/JsonParserInstrumentation.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/src/main/java/datadog/trace/instrumentation/jackson_2_6/core/JsonParserInstrumentation.java new file mode 100644 index 00000000000..92e2160cbf1 --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/src/main/java/datadog/trace/instrumentation/jackson_2_6/core/JsonParserInstrumentation.java @@ -0,0 +1,103 @@ +package datadog.trace.instrumentation.jackson_2_6.core; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.hasClassNamed; +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.declaresMethod; +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.extendsClass; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.*; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf; +import static java.util.Collections.singletonMap; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.json.JsonParser26Helper; +import com.fasterxml.jackson.core.json.UTF8StreamJsonParser; +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.api.iast.Propagation; +import datadog.trace.bootstrap.ContextStore; +import datadog.trace.bootstrap.InstrumentationContext; +import datadog.trace.bootstrap.instrumentation.iast.NamedContext; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumenterModule.class) +public class JsonParserInstrumentation extends InstrumenterModule.Iast + implements Instrumenter.ForTypeHierarchy { + + static final String TARGET_TYPE = "com.fasterxml.jackson.core.JsonParser"; + static final ElementMatcher.Junction VERSION_POST_2_6_0_AND_PRE_2_8_0 = + hasClassNamed("com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer") + .and(not(hasClassNamed("com.fasterxml.jackson.core.JsonpCharacterEscapes"))); + + public JsonParserInstrumentation() { + super("jackson", "jackson-2_6"); + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + final String className = JsonParserInstrumentation.class.getName(); + transformer.applyAdvice( + namedOneOf("getCurrentName", "nextFieldName") + .and(isPublic()) + .and(takesNoArguments()) + .and(returns(String.class)), + className + "$NameAdvice"); + } + + @Override + public String hierarchyMarkerType() { + return TARGET_TYPE; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return declaresMethod(namedOneOf("getCurrentName", "nextFieldName")) + .and( + extendsClass(named(hierarchyMarkerType())) + .and(namedNoneOf("com.fasterxml.jackson.core.base.ParserMinimalBase"))); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return VERSION_POST_2_6_0_AND_PRE_2_8_0; + } + + @Override + public Map contextStore() { + return singletonMap(TARGET_TYPE, "datadog.trace.bootstrap.instrumentation.iast.NamedContext"); + } + + @Override + public String[] helperClassNames() { + return new String[] { + "com.fasterxml.jackson.core.json" + ".JsonParser26Helper", + "com.fasterxml.jackson.core.sym" + ".ByteQuadsCanonicalizer26Helper", + }; + } + + public static class NameAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + @Propagation + public static void onExit(@Advice.This JsonParser jsonParser, @Advice.Return String result) { + if (jsonParser != null + && result != null + && jsonParser.getCurrentToken() == JsonToken.FIELD_NAME) { + final ContextStore store = + InstrumentationContext.get(JsonParser.class, NamedContext.class); + final NamedContext context = NamedContext.getOrCreate(store, jsonParser); + if (jsonParser instanceof UTF8StreamJsonParser + && JsonParser26Helper.fetchIntern((UTF8StreamJsonParser) jsonParser)) { + context.setCurrentName(result); + return; + } + context.taintName(result); + } + } + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/src/test/groovy/datadog/trace/instrumentation/jackson26/core/JsonParserInstrumentationTest.groovy b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/src/test/groovy/datadog/trace/instrumentation/jackson26/core/JsonParserInstrumentationTest.groovy new file mode 100644 index 00000000000..ca5f92306f8 --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.6/src/test/groovy/datadog/trace/instrumentation/jackson26/core/JsonParserInstrumentationTest.groovy @@ -0,0 +1,98 @@ +package datadog.trace.instrumentation.jackson26.core + +import com.fasterxml.jackson.databind.ObjectMapper +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.api.iast.InstrumentationBridge +import datadog.trace.api.iast.SourceTypes +import datadog.trace.api.iast.Taintable +import datadog.trace.api.iast.propagation.PropagationModule +import groovy.json.JsonOutput + +import java.nio.charset.Charset + +class JsonParserInstrumentationTest extends AgentTestRunner { + + private final static String JSON_STRING = '{"root":"root_value","nested":{"nested_array":["array_0","array_1"]}}' + + @Override + protected void configurePreAgent() { + injectSysConfig("dd.iast.enabled", "true") + } + + void 'test json parsing (tainted)'() { + given: + final source = new SourceImpl(origin: SourceTypes.REQUEST_BODY, name: 'body', value: JSON_STRING) + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper().readerFor(Map) + + when: + final taintedResult = reader.readValue(target) as Map + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> source + 1 * module.taintString(_, 'root', source.origin, 'root', JSON_STRING) + 1 * module.taintString(_, 'nested', source.origin, 'nested', JSON_STRING) + 0 * _ + + where: + target << [JSON_STRING] + } + + void 'test json parsing (tainted but field names)'() { + given: + final source = new SourceImpl(origin: SourceTypes.REQUEST_BODY, name: 'body', value: JSON_STRING) + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper() + + when: + final taintedResult = reader.readValue(target, Map) + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> source + 0 * _ + + where: + target << [new ByteArrayInputStream(JSON_STRING.getBytes(Charset.defaultCharset()))] + } + + void 'test json parsing (not tainted)'() { + given: + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper().readerFor(Map) + + when: + final taintedResult = reader.readValue(target) as Map + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> null + 0 * _ + + where: + target << testSuite() + } + + private static List testSuite() { + return [JSON_STRING, new ByteArrayInputStream(JSON_STRING.getBytes(Charset.defaultCharset()))] + } + + private static class SourceImpl implements Taintable.Source { + byte origin + String name + String value + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/build.gradle b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/build.gradle new file mode 100644 index 00000000000..ddf352eb161 --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/build.gradle @@ -0,0 +1,23 @@ +muzzle { + pass { + group = 'com.fasterxml.jackson.core' + module = 'jackson-core' + versions = "[2.8.0, 2.12.0)" + } +} + +apply from: "$rootDir/gradle/java.gradle" + +addTestSuiteForDir('latestDepTest', 'test') + +final jacksonVersion = '2.8.0' +dependencies { + compileOnly(group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion) + compileOnly(group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion) + + testImplementation(group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion) + testImplementation(group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion) + + latestDepTestImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.11.+' + latestDepTestImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.11.+' +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/src/main/java/com/fasterxml/jackson/core/json/JsonParser28Helper.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/src/main/java/com/fasterxml/jackson/core/json/JsonParser28Helper.java new file mode 100644 index 00000000000..1564447784a --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/src/main/java/com/fasterxml/jackson/core/json/JsonParser28Helper.java @@ -0,0 +1,11 @@ +package com.fasterxml.jackson.core.json; + +import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer28Helper; + +public final class JsonParser28Helper { + private JsonParser28Helper() {} + + public static boolean fetchIntern(UTF8StreamJsonParser jsonParser) { + return ByteQuadsCanonicalizer28Helper.fetchIntern(jsonParser._symbols); + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer28Helper.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer28Helper.java new file mode 100644 index 00000000000..e681978d13c --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer28Helper.java @@ -0,0 +1,37 @@ +package com.fasterxml.jackson.core.sym; + +import java.lang.reflect.Field; +import java.lang.reflect.UndeclaredThrowableException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class ByteQuadsCanonicalizer28Helper { + private ByteQuadsCanonicalizer28Helper() {} + + private static final Logger log = LoggerFactory.getLogger(ByteQuadsCanonicalizer28Helper.class); + + private static final Field INTERN = prepareIntern(); + + private static Field prepareIntern() { + Field _intern = null; + try { + _intern = ByteQuadsCanonicalizer.class.getDeclaredField("_intern"); + _intern.setAccessible(true); + } catch (Throwable e) { + log.debug("Failed to get ByteQuadsCanonicalizer _intern field", e); + return null; + } + return _intern; + } + + public static boolean fetchIntern(ByteQuadsCanonicalizer symbols) { + if (INTERN == null) { + return false; + } + try { + return (boolean) INTERN.get(symbols); + } catch (IllegalAccessException e) { + throw new UndeclaredThrowableException(e); + } + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/src/main/java/datadog/trace/instrumentation/jackson_2_8/core/JsonParserInstrumentation.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/src/main/java/datadog/trace/instrumentation/jackson_2_8/core/JsonParserInstrumentation.java new file mode 100644 index 00000000000..6a88cbab97b --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/src/main/java/datadog/trace/instrumentation/jackson_2_8/core/JsonParserInstrumentation.java @@ -0,0 +1,103 @@ +package datadog.trace.instrumentation.jackson_2_8.core; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.hasClassNamed; +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.declaresMethod; +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.extendsClass; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.*; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf; +import static java.util.Collections.singletonMap; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.json.JsonParser28Helper; +import com.fasterxml.jackson.core.json.UTF8StreamJsonParser; +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.api.iast.Propagation; +import datadog.trace.bootstrap.ContextStore; +import datadog.trace.bootstrap.InstrumentationContext; +import datadog.trace.bootstrap.instrumentation.iast.NamedContext; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumenterModule.class) +public class JsonParserInstrumentation extends InstrumenterModule.Iast + implements Instrumenter.ForTypeHierarchy { + + static final String TARGET_TYPE = "com.fasterxml.jackson.core.JsonParser"; + static final ElementMatcher.Junction VERSION_POST_2_8_0_AND_PRE_2_12_0 = + hasClassNamed("com.fasterxml.jackson.core.JsonpCharacterEscapes") + .and(not(hasClassNamed("com.fasterxml.jackson.core.StreamReadCapability"))); + + public JsonParserInstrumentation() { + super("jackson", "jackson-2_8"); + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + final String className = JsonParserInstrumentation.class.getName(); + transformer.applyAdvice( + namedOneOf("getCurrentName", "nextFieldName") + .and(isPublic()) + .and(takesNoArguments()) + .and(returns(String.class)), + className + "$NameAdvice"); + } + + @Override + public String hierarchyMarkerType() { + return TARGET_TYPE; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return declaresMethod(namedOneOf("getCurrentName", "nextFieldName")) + .and( + extendsClass(named(hierarchyMarkerType())) + .and(namedNoneOf("com.fasterxml.jackson.core.base.ParserMinimalBase"))); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return VERSION_POST_2_8_0_AND_PRE_2_12_0; + } + + @Override + public Map contextStore() { + return singletonMap(TARGET_TYPE, "datadog.trace.bootstrap.instrumentation.iast.NamedContext"); + } + + @Override + public String[] helperClassNames() { + return new String[] { + "com.fasterxml.jackson.core.json" + ".JsonParser28Helper", + "com.fasterxml.jackson.core.sym" + ".ByteQuadsCanonicalizer28Helper", + }; + } + + public static class NameAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + @Propagation + public static void onExit(@Advice.This JsonParser jsonParser, @Advice.Return String result) { + if (jsonParser != null + && result != null + && jsonParser.getCurrentToken() == JsonToken.FIELD_NAME) { + final ContextStore store = + InstrumentationContext.get(JsonParser.class, NamedContext.class); + final NamedContext context = NamedContext.getOrCreate(store, jsonParser); + if (jsonParser instanceof UTF8StreamJsonParser + && JsonParser28Helper.fetchIntern((UTF8StreamJsonParser) jsonParser)) { + context.setCurrentName(result); + return; + } + context.taintName(result); + } + } + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/src/test/groovy/datadog/trace/instrumentation/jackson28/core/JsonParserInstrumentationTest.groovy b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/src/test/groovy/datadog/trace/instrumentation/jackson28/core/JsonParserInstrumentationTest.groovy new file mode 100644 index 00000000000..30b10ac5e2a --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2.8/src/test/groovy/datadog/trace/instrumentation/jackson28/core/JsonParserInstrumentationTest.groovy @@ -0,0 +1,98 @@ +package datadog.trace.instrumentation.jackson28.core + +import com.fasterxml.jackson.databind.ObjectMapper +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.api.iast.InstrumentationBridge +import datadog.trace.api.iast.SourceTypes +import datadog.trace.api.iast.Taintable +import datadog.trace.api.iast.propagation.PropagationModule +import groovy.json.JsonOutput + +import java.nio.charset.Charset + +class JsonParserInstrumentationTest extends AgentTestRunner { + + private final static String JSON_STRING = '{"root":"root_value","nested":{"nested_array":["array_0","array_1"]}}' + + @Override + protected void configurePreAgent() { + injectSysConfig("dd.iast.enabled", "true") + } + + void 'test json parsing (tainted)'() { + given: + final source = new SourceImpl(origin: SourceTypes.REQUEST_BODY, name: 'body', value: JSON_STRING) + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper().readerFor(Map) + + when: + final taintedResult = reader.readValue(target) as Map + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> source + 1 * module.taintString(_, 'root', source.origin, 'root', JSON_STRING) + 1 * module.taintString(_, 'nested', source.origin, 'nested', JSON_STRING) + 0 * _ + + where: + target << [JSON_STRING] + } + + void 'test json parsing (tainted but field names)'() { + given: + final source = new SourceImpl(origin: SourceTypes.REQUEST_BODY, name: 'body', value: JSON_STRING) + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper() + + when: + final taintedResult = reader.readValue(target, Map) + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> source + 0 * _ + + where: + target << [new ByteArrayInputStream(JSON_STRING.getBytes(Charset.defaultCharset()))] + } + + void 'test json parsing (not tainted)'() { + given: + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper().readerFor(Map) + + when: + final taintedResult = reader.readValue(target) as Map + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> null + 0 * _ + + where: + target << testSuite() + } + + private static List testSuite() { + return [JSON_STRING, new ByteArrayInputStream(JSON_STRING.getBytes(Charset.defaultCharset()))] + } + + private static class SourceImpl implements Taintable.Source { + byte origin + String name + String value + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2/build.gradle b/dd-java-agent/instrumentation/jackson-core/jackson-core-2/build.gradle new file mode 100644 index 00000000000..d3be1e87367 --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2/build.gradle @@ -0,0 +1,23 @@ +muzzle { + pass { + group = 'com.fasterxml.jackson.core' + module = 'jackson-core' + versions = "[2.0.0, 2.6.0)" + } +} + +apply from: "$rootDir/gradle/java.gradle" + +addTestSuiteForDir('latestDepTest', 'test') + +final jacksonVersion = '2.0.0' +dependencies { + compileOnly(group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion) + compileOnly(group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion) + + testImplementation(group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion) + testImplementation(group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion) + + latestDepTestImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.5.+' + latestDepTestImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.5.+' +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2/src/main/java/com/fasterxml/jackson/core/json/JsonParser2Helper.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2/src/main/java/com/fasterxml/jackson/core/json/JsonParser2Helper.java new file mode 100644 index 00000000000..a0f4ebdac0d --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2/src/main/java/com/fasterxml/jackson/core/json/JsonParser2Helper.java @@ -0,0 +1,11 @@ +package com.fasterxml.jackson.core.json; + +import com.fasterxml.jackson.core.sym.BytesToNameCanonicalizer2Helper; + +public final class JsonParser2Helper { + private JsonParser2Helper() {} + + public static boolean fetchIntern(UTF8StreamJsonParser jsonParser) { + return BytesToNameCanonicalizer2Helper.fetchIntern(jsonParser._symbols); + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2/src/main/java/com/fasterxml/jackson/core/sym/BytesToNameCanonicalizer2Helper.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2/src/main/java/com/fasterxml/jackson/core/sym/BytesToNameCanonicalizer2Helper.java new file mode 100644 index 00000000000..6b4c1a2f919 --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2/src/main/java/com/fasterxml/jackson/core/sym/BytesToNameCanonicalizer2Helper.java @@ -0,0 +1,9 @@ +package com.fasterxml.jackson.core.sym; + +public final class BytesToNameCanonicalizer2Helper { + private BytesToNameCanonicalizer2Helper() {} + + public static boolean fetchIntern(BytesToNameCanonicalizer symbols) { + return symbols._intern; + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2/src/main/java/datadog/trace/instrumentation/jackson_2/core/JsonParserInstrumentation.java b/dd-java-agent/instrumentation/jackson-core/jackson-core-2/src/main/java/datadog/trace/instrumentation/jackson_2/core/JsonParserInstrumentation.java new file mode 100644 index 00000000000..add5a7a41b3 --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2/src/main/java/datadog/trace/instrumentation/jackson_2/core/JsonParserInstrumentation.java @@ -0,0 +1,102 @@ +package datadog.trace.instrumentation.jackson_2.core; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.hasClassNamed; +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.declaresMethod; +import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.extendsClass; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.*; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf; +import static java.util.Collections.singletonMap; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.json.JsonParser2Helper; +import com.fasterxml.jackson.core.json.UTF8StreamJsonParser; +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.api.iast.Propagation; +import datadog.trace.bootstrap.ContextStore; +import datadog.trace.bootstrap.InstrumentationContext; +import datadog.trace.bootstrap.instrumentation.iast.NamedContext; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumenterModule.class) +public class JsonParserInstrumentation extends InstrumenterModule.Iast + implements Instrumenter.ForTypeHierarchy { + + static final String TARGET_TYPE = "com.fasterxml.jackson.core.JsonParser"; + static final ElementMatcher.Junction VERSION_PRE_2_6_0 = + hasClassNamed("com.fasterxml.jackson.core.sym.BytesToNameCanonicalizer"); + + public JsonParserInstrumentation() { + super("jackson", "jackson-2"); + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + final String className = JsonParserInstrumentation.class.getName(); + transformer.applyAdvice( + namedOneOf("getCurrentName", "nextFieldName") + .and(isPublic()) + .and(takesNoArguments()) + .and(returns(String.class)), + className + "$NameAdvice"); + } + + @Override + public String hierarchyMarkerType() { + return TARGET_TYPE; + } + + @Override + public ElementMatcher hierarchyMatcher() { + return declaresMethod(namedOneOf("getCurrentName", "nextFieldName")) + .and( + extendsClass(named(hierarchyMarkerType())) + .and(namedNoneOf("com.fasterxml.jackson.core.base.ParserMinimalBase"))); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return VERSION_PRE_2_6_0; + } + + @Override + public Map contextStore() { + return singletonMap(TARGET_TYPE, "datadog.trace.bootstrap.instrumentation.iast.NamedContext"); + } + + @Override + public String[] helperClassNames() { + return new String[] { + "com.fasterxml.jackson.core.json" + ".JsonParser2Helper", + "com.fasterxml.jackson.core.sym" + ".BytesToNameCanonicalizer2Helper", + }; + } + + public static class NameAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + @Propagation + public static void onExit(@Advice.This JsonParser jsonParser, @Advice.Return String result) { + if (jsonParser != null + && result != null + && jsonParser.getCurrentToken() == JsonToken.FIELD_NAME) { + final ContextStore store = + InstrumentationContext.get(JsonParser.class, NamedContext.class); + final NamedContext context = NamedContext.getOrCreate(store, jsonParser); + if (jsonParser instanceof UTF8StreamJsonParser + && JsonParser2Helper.fetchIntern((UTF8StreamJsonParser) jsonParser)) { + context.setCurrentName(result); + return; + } + context.taintName(result); + } + } + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/jackson-core-2/src/test/groovy/datadog/trace/instrumentation/jackson2/core/JsonParserInstrumentationTest.groovy b/dd-java-agent/instrumentation/jackson-core/jackson-core-2/src/test/groovy/datadog/trace/instrumentation/jackson2/core/JsonParserInstrumentationTest.groovy new file mode 100644 index 00000000000..eb28615a3e0 --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/jackson-core-2/src/test/groovy/datadog/trace/instrumentation/jackson2/core/JsonParserInstrumentationTest.groovy @@ -0,0 +1,98 @@ +package datadog.trace.instrumentation.jackson2.core + +import com.fasterxml.jackson.databind.ObjectMapper +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.api.iast.InstrumentationBridge +import datadog.trace.api.iast.SourceTypes +import datadog.trace.api.iast.Taintable +import datadog.trace.api.iast.propagation.PropagationModule +import groovy.json.JsonOutput + +import java.nio.charset.Charset + +class JsonParserInstrumentationTest extends AgentTestRunner { + + private final static String JSON_STRING = '{"root":"root_value","nested":{"nested_array":["array_0","array_1"]}}' + + @Override + protected void configurePreAgent() { + injectSysConfig("dd.iast.enabled", "true") + } + + void 'test json parsing (tainted)'() { + given: + final source = new SourceImpl(origin: SourceTypes.REQUEST_BODY, name: 'body', value: JSON_STRING) + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper() + + when: + final taintedResult = reader.readValue(target, Map) + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> source + 1 * module.taintString(_, 'root', source.origin, 'root', JSON_STRING) + 1 * module.taintString(_, 'nested', source.origin, 'nested', JSON_STRING) + 0 * _ + + where: + target << [JSON_STRING] + } + + void 'test json parsing (tainted but field names)'() { + given: + final source = new SourceImpl(origin: SourceTypes.REQUEST_BODY, name: 'body', value: JSON_STRING) + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper() + + when: + final taintedResult = reader.readValue(target, Map) + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> source + 0 * _ + + where: + target << [new ByteArrayInputStream(JSON_STRING.getBytes(Charset.defaultCharset()))] + } + + void 'test json parsing (not tainted)'() { + given: + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper() + + when: + final taintedResult = reader.readValue(target, Map) + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> null + 0 * _ + + where: + target << testSuite() + } + + private static List testSuite() { + return [JSON_STRING, new ByteArrayInputStream(JSON_STRING.getBytes(Charset.defaultCharset()))] + } + + private static class SourceImpl implements Taintable.Source { + byte origin + String name + String value + } +} diff --git a/dd-java-agent/instrumentation/jackson-core/src/main/java/datadog/trace/instrumentation/jackson/core/Json2ParserInstrumentation.java b/dd-java-agent/instrumentation/jackson-core/src/main/java/datadog/trace/instrumentation/jackson/core/JsonParserInstrumentation.java similarity index 68% rename from dd-java-agent/instrumentation/jackson-core/src/main/java/datadog/trace/instrumentation/jackson/core/Json2ParserInstrumentation.java rename to dd-java-agent/instrumentation/jackson-core/src/main/java/datadog/trace/instrumentation/jackson/core/JsonParserInstrumentation.java index 713128d7302..3c74e8521fe 100644 --- a/dd-java-agent/instrumentation/jackson-core/src/main/java/datadog/trace/instrumentation/jackson/core/Json2ParserInstrumentation.java +++ b/dd-java-agent/instrumentation/jackson-core/src/main/java/datadog/trace/instrumentation/jackson/core/JsonParserInstrumentation.java @@ -23,30 +23,24 @@ import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumenterModule.class) -public class Json2ParserInstrumentation extends InstrumenterModule.Iast +public class JsonParserInstrumentation extends InstrumenterModule.Iast implements Instrumenter.ForTypeHierarchy { static final String TARGET_TYPE = "com.fasterxml.jackson.core.JsonParser"; - public Json2ParserInstrumentation() { + public JsonParserInstrumentation() { super("jackson", "jackson-2"); } @Override public void methodAdvice(MethodTransformer transformer) { - final String className = Json2ParserInstrumentation.class.getName(); + final String className = JsonParserInstrumentation.class.getName(); transformer.applyAdvice( namedOneOf("getText", "getValueAsString") .and(isPublic()) .and(takesNoArguments()) .and(returns(String.class)), className + "$TextAdvice"); - transformer.applyAdvice( - namedOneOf("getCurrentName", "nextFieldName") - .and(isPublic()) - .and(takesNoArguments()) - .and(returns(String.class)), - className + "$NameAdvice"); } @Override @@ -56,8 +50,7 @@ public String hierarchyMarkerType() { @Override public ElementMatcher hierarchyMatcher() { - return declaresMethod( - namedOneOf("getText", "getValueAsString", "getCurrentName", "nextFieldName")) + return declaresMethod(namedOneOf("getText", "getValueAsString")) .and( extendsClass(named(hierarchyMarkerType())) .and(namedNoneOf("com.fasterxml.jackson.core.base.ParserMinimalBase"))); @@ -86,27 +79,4 @@ public static void onExit(@Advice.This JsonParser jsonParser, @Advice.Return Str } } } - - /** - * Not all field names are caught by {@link JsonParser#getText()} or {@link - * JsonParser#getValueAsString()} - * - * @see JsonParser#getCurrentName() - * @see JsonParser#nextFieldName() - */ - public static class NameAdvice { - - @Advice.OnMethodExit(suppress = Throwable.class) - @Propagation - public static void onExit(@Advice.This JsonParser jsonParser, @Advice.Return String result) { - if (jsonParser != null - && result != null - && jsonParser.getCurrentToken() == JsonToken.FIELD_NAME) { - final ContextStore store = - InstrumentationContext.get(JsonParser.class, NamedContext.class); - final NamedContext context = NamedContext.getOrCreate(store, jsonParser); - context.taintName(result); - } - } - } } diff --git a/dd-java-agent/instrumentation/jackson-core/src/test/groovy/JsonParserInstrumentationTest.groovy b/dd-java-agent/instrumentation/jackson-core/src/test/groovy/JsonParserInstrumentationTest.groovy new file mode 100644 index 00000000000..964af4c23b9 --- /dev/null +++ b/dd-java-agent/instrumentation/jackson-core/src/test/groovy/JsonParserInstrumentationTest.groovy @@ -0,0 +1,100 @@ +import com.fasterxml.jackson.databind.ObjectMapper +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.api.iast.InstrumentationBridge +import datadog.trace.api.iast.SourceTypes +import datadog.trace.api.iast.Taintable +import datadog.trace.api.iast.propagation.PropagationModule +import groovy.json.JsonOutput + +import java.nio.charset.Charset + +class JsonParserInstrumentationTest extends AgentTestRunner { + + private final static String JSON_STRING = '{"root":"root_value","nested":{"nested_array":["array_0","array_1"]}}' + + @Override + protected void configurePreAgent() { + injectSysConfig("dd.iast.enabled", "true") + } + + void 'test json parsing (tainted)'() { + given: + final source = new SourceImpl(origin: SourceTypes.REQUEST_BODY, name: 'body', value: JSON_STRING) + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper().readerFor(Map) + + when: + final taintedResult = reader.readValue(target) as Map + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> source + 1 * module.taintString(_, 'root_value', source.origin, _, JSON_STRING) + 1 * module.taintString(_, 'array_0', source.origin, _, JSON_STRING) + 1 * module.taintString(_, 'array_1', source.origin, _, JSON_STRING) + 0 * _ + + where: + target << [JSON_STRING] + } + + void 'test json parsing (tainted but field names)'() { + given: + final source = new SourceImpl(origin: SourceTypes.REQUEST_BODY, name: 'body', value: JSON_STRING) + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper().readerFor(Map) + + when: + final taintedResult = reader.readValue(target) as Map + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> source + 1 * module.taintString(_, 'root_value', source.origin, _, JSON_STRING) + 1 * module.taintString(_, 'array_0', source.origin, _, JSON_STRING) + 1 * module.taintString(_, 'array_1', source.origin, _, JSON_STRING) + 0 * _ + + where: + target << [new ByteArrayInputStream(JSON_STRING.getBytes(Charset.defaultCharset()))] + } + + void 'test json parsing (not tainted)'() { + given: + final module = Mock(PropagationModule) + InstrumentationBridge.registerIastModule(module) + + and: + final reader = new ObjectMapper().readerFor(Map) + + when: + final taintedResult = reader.readValue(target) as Map + + then: + JsonOutput.toJson(taintedResult) == JSON_STRING + _ * module.taintObjectIfTainted(_, _) + _ * module.findSource(_) >> null + 0 * _ + + where: + target << testSuite() + } + + private static List testSuite() { + return [JSON_STRING, new ByteArrayInputStream(JSON_STRING.getBytes(Charset.defaultCharset()))] + } + + private static class SourceImpl implements Taintable.Source { + byte origin + String name + String value + } +} diff --git a/dd-java-agent/instrumentation/kafka-clients-0.11/build.gradle b/dd-java-agent/instrumentation/kafka-clients-0.11/build.gradle index 2d54acf8e7d..0168abcdd42 100644 --- a/dd-java-agent/instrumentation/kafka-clients-0.11/build.gradle +++ b/dd-java-agent/instrumentation/kafka-clients-0.11/build.gradle @@ -31,6 +31,7 @@ dependencies { testRuntimeOnly project(':dd-java-agent:instrumentation:java-lang') testRuntimeOnly project(':dd-java-agent:instrumentation:java-io') testRuntimeOnly project(':dd-java-agent:instrumentation:jackson-core') + testRuntimeOnly project(':dd-java-agent:instrumentation:jackson-core:jackson-core-2.8') testImplementation(group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.10') // Include latest version of kafka itself along with latest version of client libs. @@ -49,6 +50,7 @@ dependencies { iastLatestDepTest3RuntimeOnly project(':dd-java-agent:instrumentation:java-lang') iastLatestDepTest3RuntimeOnly project(':dd-java-agent:instrumentation:java-io') iastLatestDepTest3RuntimeOnly project(':dd-java-agent:instrumentation:jackson-core') + iastLatestDepTest3RuntimeOnly project(':dd-java-agent:instrumentation:jackson-core:jackson-core-2.12') iastLatestDepTest3Implementation(group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.15.3') iastLatestDepTest3Implementation(testFixtures(project(':dd-java-agent:agent-iast'))) diff --git a/dd-java-agent/instrumentation/kafka-clients-0.11/src/iastLatestDepTest3/groovy/iast/KafkaIastDeserializerTest.groovy b/dd-java-agent/instrumentation/kafka-clients-0.11/src/iastLatestDepTest3/groovy/iast/KafkaIastDeserializerTest.groovy index 418f50d7043..d92d70ecd55 100644 --- a/dd-java-agent/instrumentation/kafka-clients-0.11/src/iastLatestDepTest3/groovy/iast/KafkaIastDeserializerTest.groovy +++ b/dd-java-agent/instrumentation/kafka-clients-0.11/src/iastLatestDepTest3/groovy/iast/KafkaIastDeserializerTest.groovy @@ -124,10 +124,6 @@ class KafkaIastDeserializerTest extends IastAgentTestRunner { value(instanceOf(TestBean)) range(0, Integer.MAX_VALUE, source(origin as byte)) } - to.hasTaintedObject { - value('name') - range(0, 4, source(origin as byte, 'name', 'name')) - } to.hasTaintedObject { value('Mr Bean') range(0, 7, source(origin as byte, 'name', 'Mr Bean')) diff --git a/dd-java-agent/instrumentation/kafka-clients-0.11/src/test/groovy/iast/KafkaIastDeserializerForkedTest.groovy b/dd-java-agent/instrumentation/kafka-clients-0.11/src/test/groovy/iast/KafkaIastDeserializerForkedTest.groovy index 6e20dd8142c..fefca9feee0 100644 --- a/dd-java-agent/instrumentation/kafka-clients-0.11/src/test/groovy/iast/KafkaIastDeserializerForkedTest.groovy +++ b/dd-java-agent/instrumentation/kafka-clients-0.11/src/test/groovy/iast/KafkaIastDeserializerForkedTest.groovy @@ -122,10 +122,6 @@ class KafkaIastDeserializerForkedTest extends IastAgentTestRunner { value(instanceOf(TestBean)) range(0, Integer.MAX_VALUE, source(origin as byte)) } - to.hasTaintedObject { - value('name') - range(0, 4, source(origin as byte, 'name', 'name')) - } to.hasTaintedObject { value('Mr Bean') range(0, 7, source(origin as byte, 'name', 'Mr Bean')) diff --git a/dd-java-agent/instrumentation/pekko-http-1.0/build.gradle b/dd-java-agent/instrumentation/pekko-http-1.0/build.gradle index 4327f5aee5d..c3d1f9067dc 100644 --- a/dd-java-agent/instrumentation/pekko-http-1.0/build.gradle +++ b/dd-java-agent/instrumentation/pekko-http-1.0/build.gradle @@ -63,6 +63,7 @@ dependencies { iastTestImplementation(testFixtures(project(':dd-java-agent:agent-iast'))) iastTestCompileOnly group: 'de.thetaphi', name: 'forbiddenapis', version: '3.4' iastTestRuntimeOnly project(':dd-java-agent:instrumentation:jackson-core') + iastTestRuntimeOnly project(':dd-java-agent:instrumentation:jackson-core:jackson-core-2.12') iastTestRuntimeOnly project(':dd-java-agent:instrumentation:iast-instrumenter') latestDepTestImplementation libs.scala213 @@ -78,6 +79,7 @@ dependencies { latestDepIastTestImplementation group: 'com.github.pjfanning', name: 'pekko-http-jackson_2.13', version: '2.+' latestDepIastTestImplementation group: 'org.scala-lang.modules', name: 'scala-java8-compat_2.13', version: '1.0.+' latestDepIastTestImplementation(testFixtures(project(':dd-java-agent:agent-iast'))) + latestDepIastTestImplementation project(':dd-java-agent:instrumentation:jackson-core:jackson-core-2.16') } tasks.named("test").configure { diff --git a/dd-java-agent/instrumentation/spring-webflux-5/build.gradle b/dd-java-agent/instrumentation/spring-webflux-5/build.gradle index 9c7f7659ade..13a310b8f40 100644 --- a/dd-java-agent/instrumentation/spring-webflux-5/build.gradle +++ b/dd-java-agent/instrumentation/spring-webflux-5/build.gradle @@ -119,6 +119,7 @@ dependencies { iastTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-json', version: '2.0.+' iastTestImplementation(testFixtures(project(':dd-java-agent:agent-iast'))) iastTestImplementation project(':dd-java-agent:instrumentation:jackson-core') + iastTestImplementation project(':dd-java-agent:instrumentation:jackson-core:jackson-core-2.8') latestIast24TestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-webflux', version: '2.4.+' latestIast24TestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '2.4.+', { @@ -136,6 +137,7 @@ dependencies { } latestIastTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-reactor-netty', version: '2.+' latestIastTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-json', version: '2.+' + latestIastTestImplementation project(':dd-java-agent:instrumentation:jackson-core:jackson-core-2.12') } tasks.named("latestDepTest").configure { diff --git a/dd-java-agent/instrumentation/spring-webflux-5/src/iastTest/groovy/datadog/trace/instrumentation/springwebflux/server/IastWebFluxTest.groovy b/dd-java-agent/instrumentation/spring-webflux-5/src/iastTest/groovy/datadog/trace/instrumentation/springwebflux/server/IastWebFluxTest.groovy index 01851dcc5d1..97819067c09 100644 --- a/dd-java-agent/instrumentation/spring-webflux-5/src/iastTest/groovy/datadog/trace/instrumentation/springwebflux/server/IastWebFluxTest.groovy +++ b/dd-java-agent/instrumentation/spring-webflux-5/src/iastTest/groovy/datadog/trace/instrumentation/springwebflux/server/IastWebFluxTest.groovy @@ -275,14 +275,6 @@ class IastWebFluxTest extends IastRequestTestRunner { then: // source values take the value of the current object as the body is never converted to a CharSequence - toc.hasTaintedObject { - value 'var1' - range 0, 4, source(SourceTypes.REQUEST_BODY, 'var1', 'var1') - } - toc.hasTaintedObject { - value 'var2' - range 0, 4, source(SourceTypes.REQUEST_BODY, 'var2', 'var2') - } toc.hasTaintedObject { value 'foo' range 0, 3, source(SourceTypes.REQUEST_BODY, 'var1', 'foo') diff --git a/dd-java-agent/instrumentation/spring-webflux-6/build.gradle b/dd-java-agent/instrumentation/spring-webflux-6/build.gradle index 112dd7ace27..c22b01bd312 100644 --- a/dd-java-agent/instrumentation/spring-webflux-6/build.gradle +++ b/dd-java-agent/instrumentation/spring-webflux-6/build.gradle @@ -73,6 +73,7 @@ dependencies { iastTestImplementation(testFixtures(project(':dd-java-agent:agent-iast'))) iastTestImplementation project(':dd-java-agent:instrumentation:spring-webflux-5') iastTestImplementation project(':dd-java-agent:instrumentation:jackson-core') + iastTestImplementation project(':dd-java-agent:instrumentation:jackson-core:jackson-core-2.12') iastTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-webflux', version: '3.0.0' iastTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '3.0.0', { diff --git a/dd-java-agent/instrumentation/spring-webflux-6/src/iastTest/groovy/datadog/trace/instrumentation/springwebflux6/server/IastWebFluxTest.groovy b/dd-java-agent/instrumentation/spring-webflux-6/src/iastTest/groovy/datadog/trace/instrumentation/springwebflux6/server/IastWebFluxTest.groovy index 30a079dc146..4359ec06008 100644 --- a/dd-java-agent/instrumentation/spring-webflux-6/src/iastTest/groovy/datadog/trace/instrumentation/springwebflux6/server/IastWebFluxTest.groovy +++ b/dd-java-agent/instrumentation/spring-webflux-6/src/iastTest/groovy/datadog/trace/instrumentation/springwebflux6/server/IastWebFluxTest.groovy @@ -278,14 +278,6 @@ class IastWebFluxTest extends IastRequestTestRunner { then: // source values take the value of the current object as the body is never converted to a CharSequence - toc.hasTaintedObject { - value 'var1' - range 0, 4, source(SourceTypes.REQUEST_BODY, 'var1', 'var1') - } - toc.hasTaintedObject { - value 'var2' - range 0, 4, source(SourceTypes.REQUEST_BODY, 'var2', 'var2') - } toc.hasTaintedObject { value 'foo' range 0, 3, source(SourceTypes.REQUEST_BODY, 'var1', 'foo') diff --git a/dd-smoke-tests/iast-util/src/testFixtures/groovy/datadog/smoketest/AbstractIastVertxSmokeTest.groovy b/dd-smoke-tests/iast-util/src/testFixtures/groovy/datadog/smoketest/AbstractIastVertxSmokeTest.groovy index 2fb2e879112..6166922544d 100644 --- a/dd-smoke-tests/iast-util/src/testFixtures/groovy/datadog/smoketest/AbstractIastVertxSmokeTest.groovy +++ b/dd-smoke-tests/iast-util/src/testFixtures/groovy/datadog/smoketest/AbstractIastVertxSmokeTest.groovy @@ -166,9 +166,6 @@ abstract class AbstractIastVertxSmokeTest extends AbstractIastServerSmokeTest { client.newCall(request).execute() then: - hasTainted { tainted -> - tainted.value == 'my_key' && tainted.ranges[0].source.origin == 'http.request.body' - } hasTainted { tainted -> tainted.value == 'my_value' && tainted.ranges[0].source.origin == 'http.request.body' } diff --git a/settings.gradle b/settings.gradle index a48738f63c6..418c5143502 100644 --- a/settings.gradle +++ b/settings.gradle @@ -260,6 +260,11 @@ include ':dd-java-agent:instrumentation:iast-instrumenter' include ':dd-java-agent:instrumentation:ignite-2.0' include ':dd-java-agent:instrumentation:jackson-core' include ':dd-java-agent:instrumentation:jackson-core:jackson-core-1' +include ':dd-java-agent:instrumentation:jackson-core:jackson-core-2' +include ':dd-java-agent:instrumentation:jackson-core:jackson-core-2.6' +include ':dd-java-agent:instrumentation:jackson-core:jackson-core-2.8' +include ':dd-java-agent:instrumentation:jackson-core:jackson-core-2.12' +include ':dd-java-agent:instrumentation:jackson-core:jackson-core-2.16' include ':dd-java-agent:instrumentation:jacoco' include ':dd-java-agent:instrumentation:jakarta-jms' include ':dd-java-agent:instrumentation:jakarta-rs-annotations-3' @@ -489,3 +494,4 @@ include ':dd-java-agent:benchmark' include ':dd-java-agent:benchmark-integration' include ':dd-java-agent:benchmark-integration:jetty-perftest' include ':dd-java-agent:benchmark-integration:play-perftest' +