Skip to content

Commit c961816

Browse files
authored
Fix org.json iast instrumentation test for latest dependency (#8347)
What Does This Do Rework tests to be more accurate and avoid problems with new versions Remove redundant instrumentation as get methods always call opt methods Be more accurate with instrumented constructors as they call each other internally Improve java.io.InputStream propagation Motivation Fix latest dependency test for org.json (20250107)
1 parent 3a28d63 commit c961816

File tree

16 files changed

+325
-189
lines changed

16 files changed

+325
-189
lines changed

dd-java-agent/instrumentation/iast-instrumenter/src/main/resources/datadog/trace/instrumentation/iastinstrumenter/iast_exclusion.trie

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,8 @@
256256
1 org.jooq.*
257257
1 org.jruby.*
258258
1 org.json.*
259+
#Needed for JSON propagation
260+
2 org.json.JSONTokener
259261
1 org.jsoup.*
260262
1 org.junit.*
261263
1 org.jvnet.hk2.*

dd-java-agent/instrumentation/java-io/src/main/java/datadog/trace/instrumentation/java/lang/InputStreamReaderCallSite.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
@CallSite(spi = IastCallSites.class)
1313
public class InputStreamReaderCallSite {
1414

15+
@CallSite.After("void java.io.InputStreamReader.<init>(java.io.InputStream)")
1516
@CallSite.After(
1617
"void java.io.InputStreamReader.<init>(java.io.InputStream, java.nio.charset.Charset)")
1718
public static InputStreamReader afterInit(

dd-java-agent/instrumentation/java-io/src/test/groovy/datadog/trace/instrumentation/java/io/InputStreamReaderCallSiteTest.groovy

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,17 @@ class InputStreamReaderCallSiteTest extends BaseIoCallSiteTest{
1414
InstrumentationBridge.registerIastModule(iastModule)
1515

1616
when:
17-
TestInputStreamReaderSuite.init(new ByteArrayInputStream("test".getBytes()), Charset.defaultCharset())
17+
TestInputStreamReaderSuite.init(*args)
1818

1919
then:
2020
1 * iastModule.taintObjectIfTainted(_ as InputStreamReader, _ as InputStream)
2121
0 * _
22+
23+
where:
24+
args << [
25+
[new ByteArrayInputStream("test".getBytes()), Charset.defaultCharset()],
26+
// InputStream input
27+
[new ByteArrayInputStream("test".getBytes())]// Reader input
28+
]
2229
}
2330
}

dd-java-agent/instrumentation/java-io/src/test/java/foo/bar/TestInputStreamReaderSuite.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@ public class TestInputStreamReaderSuite {
99
public static InputStreamReader init(final InputStream in, Charset charset) {
1010
return new InputStreamReader(in, charset);
1111
}
12+
13+
public static InputStreamReader init(final InputStream in) {
14+
return new InputStreamReader(in);
15+
}
1216
}

dd-java-agent/instrumentation/org-json/build.gradle

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
11
muzzle {
22
pass {
3+
name = 'all'
34
group = 'org.json'
45
module = 'json'
56
versions = '[20070829, ]'
67
assertInverse = true
78
}
9+
pass {
10+
name = 'before_20241224'
11+
group = 'org.json'
12+
module = 'json'
13+
versions = '[20070829, 20241224)'
14+
assertInverse = true
15+
}
16+
pass {
17+
name = 'after_20241224'
18+
group = 'org.json'
19+
module = 'json'
20+
versions = '[20241224, ]'
21+
assertInverse = true
22+
}
823
}
924

1025
apply from: "$rootDir/gradle/java.gradle"
@@ -16,7 +31,7 @@ dependencies {
1631
testImplementation group: 'org.json', name: 'json', version: '20230227'
1732

1833
testRuntimeOnly project(':dd-java-agent:instrumentation:iast-instrumenter')
34+
testRuntimeOnly project(':dd-java-agent:instrumentation:java-io') //Needed for Reader
1935

20-
//FIXME: ASM
21-
latestDepTestImplementation group: 'org.json', name: 'json', version: '20240303'
36+
latestDepTestImplementation group: 'org.json', name: 'json', version: '+'
2237
}

dd-java-agent/instrumentation/org-json/gradle.lockfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ org.hamcrest:hamcrest-core:1.3=latestDepTestCompileClasspath,latestDepTestRuntim
116116
org.hamcrest:hamcrest:2.2=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
117117
org.jctools:jctools-core:3.3.0=instrumentPluginClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testRuntimeClasspath
118118
org.json:json:20230227=compileClasspath,testCompileClasspath,testRuntimeClasspath
119-
org.json:json:20240303=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath
119+
org.json:json:20250107=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath
120120
org.junit.jupiter:junit-jupiter-api:5.9.2=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
121121
org.junit.jupiter:junit-jupiter-engine:5.9.2=latestDepTestRuntimeClasspath,testRuntimeClasspath
122122
org.junit.platform:junit-platform-commons:1.9.2=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath

dd-java-agent/instrumentation/org-json/src/main/java/datadog/trace/instrumentation/json/JSONArrayInstrumentation.java

Lines changed: 8 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
66
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
77
import static net.bytebuddy.matcher.ElementMatchers.returns;
8+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
89
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
910

1011
import com.google.auto.service.AutoService;
@@ -14,8 +15,6 @@
1415
import datadog.trace.api.iast.Propagation;
1516
import datadog.trace.api.iast.propagation.PropagationModule;
1617
import net.bytebuddy.asm.Advice;
17-
import org.json.JSONArray;
18-
import org.json.JSONObject;
1918

2019
@AutoService(InstrumenterModule.class)
2120
public class JSONArrayInstrumentation extends InstrumenterModule.Iast
@@ -25,6 +24,11 @@ public JSONArrayInstrumentation() {
2524
super("org-json");
2625
}
2726

27+
@Override
28+
public String muzzleDirective() {
29+
return "all";
30+
}
31+
2832
@Override
2933
public String instrumentedType() {
3034
return "org.json.JSONArray";
@@ -33,18 +37,11 @@ public String instrumentedType() {
3337
@Override
3438
public void methodAdvice(MethodTransformer transformer) {
3539
transformer.applyAdvice(
36-
isConstructor().and(takesArguments(String.class)),
40+
isConstructor().and(takesArguments(0)).and(takesArgument(0, named("org.json.JSONTokener"))),
3741
getClass().getName() + "$ConstructorAdvice");
38-
transformer.applyAdvice(
39-
isMethod()
40-
.and(isPublic())
41-
.and(returns(Object.class))
42-
.and(named("get"))
43-
.and(takesArguments(1)),
44-
getClass().getName() + "$GetAdvice");
4542
transformer.applyAdvice(
4643
isMethod().and(isPublic()).and(returns(Object.class)).and(named("opt")),
47-
getClass().getName() + "$OptAdvice");
44+
packageName + ".OptAdvice");
4845
}
4946

5047
public static class ConstructorAdvice {
@@ -57,44 +54,4 @@ public static void afterInit(@Advice.This Object self, @Advice.Argument(0) final
5754
}
5855
}
5956
}
60-
61-
public static class GetAdvice {
62-
@Advice.OnMethodExit(suppress = Throwable.class)
63-
@Propagation
64-
public static void afterMethod(@Advice.This Object self, @Advice.Return final Object result) {
65-
boolean isString = result instanceof String;
66-
boolean isJson = !isString && (result instanceof JSONObject || result instanceof JSONArray);
67-
if (!isString && !isJson) {
68-
return;
69-
}
70-
final PropagationModule iastModule = InstrumentationBridge.PROPAGATION;
71-
if (iastModule != null) {
72-
if (isString) {
73-
iastModule.taintStringIfTainted((String) result, self);
74-
} else {
75-
iastModule.taintObjectIfTainted(result, self);
76-
}
77-
}
78-
}
79-
}
80-
81-
public static class OptAdvice {
82-
@Advice.OnMethodExit(suppress = Throwable.class)
83-
@Propagation
84-
public static void afterMethod(@Advice.This Object self, @Advice.Return final Object result) {
85-
boolean isString = result instanceof String;
86-
boolean isJson = !isString && (result instanceof JSONObject || result instanceof JSONArray);
87-
if (!isString && !isJson) {
88-
return;
89-
}
90-
final PropagationModule iastModule = InstrumentationBridge.PROPAGATION;
91-
if (iastModule != null) {
92-
if (isString) {
93-
iastModule.taintStringIfTainted((String) result, self);
94-
} else {
95-
iastModule.taintObjectIfTainted(result, self);
96-
}
97-
}
98-
}
99-
}
10057
}

dd-java-agent/instrumentation/org-json/src/main/java/datadog/trace/instrumentation/json/JSONCookieInstrumentation.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ public JSONCookieInstrumentation() {
1919
super("org-json");
2020
}
2121

22+
@Override
23+
public String muzzleDirective() {
24+
return "all";
25+
}
26+
2227
@Override
2328
public String instrumentedType() {
2429
return "org.json.Cookie";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package datadog.trace.instrumentation.json;
2+
3+
import static datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.hasClassNamed;
4+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
5+
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
6+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
7+
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
8+
import static net.bytebuddy.matcher.ElementMatchers.returns;
9+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
10+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
11+
12+
import com.google.auto.service.AutoService;
13+
import datadog.trace.agent.tooling.Instrumenter;
14+
import datadog.trace.agent.tooling.InstrumenterModule;
15+
import datadog.trace.api.iast.InstrumentationBridge;
16+
import datadog.trace.api.iast.Propagation;
17+
import datadog.trace.api.iast.propagation.PropagationModule;
18+
import java.util.Map;
19+
import net.bytebuddy.asm.Advice;
20+
import net.bytebuddy.matcher.ElementMatcher;
21+
22+
@AutoService(InstrumenterModule.class)
23+
public class JSONObject20241224Instrumentation extends InstrumenterModule.Iast
24+
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {
25+
public JSONObject20241224Instrumentation() {
26+
super("org-json");
27+
}
28+
29+
static final ElementMatcher.Junction<ClassLoader> AFTER_20241224 =
30+
hasClassNamed("org.json.StringBuilderWriter");
31+
32+
@Override
33+
public String muzzleDirective() {
34+
return "after_20241224";
35+
}
36+
37+
@Override
38+
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
39+
return AFTER_20241224;
40+
}
41+
42+
@Override
43+
public String instrumentedType() {
44+
return "org.json.JSONObject";
45+
}
46+
47+
@Override
48+
public void methodAdvice(MethodTransformer transformer) {
49+
// public JSONObject(JSONTokener x, JSONParserConfiguration jsonParserConfiguration)
50+
transformer.applyAdvice(
51+
isConstructor()
52+
.and(takesArguments(2))
53+
.and(takesArgument(0, named("org.json.JSONTokener")))
54+
.and(takesArgument(1, named("org.json.JSONParserConfiguration"))),
55+
getClass().getName() + "$ConstructorAdvice");
56+
// private JSONObject(Map<?, ?> m, int recursionDepth, JSONParserConfiguration
57+
// jsonParserConfiguration)
58+
transformer.applyAdvice(
59+
isConstructor()
60+
.and(takesArguments(3))
61+
.and(takesArgument(0, Map.class))
62+
.and(takesArgument(1, int.class))
63+
.and(takesArgument(2, named("org.json.JSONParserConfiguration"))),
64+
getClass().getName() + "$ConstructorAdvice");
65+
transformer.applyAdvice(
66+
isMethod()
67+
.and(isPublic())
68+
.and(returns(Object.class))
69+
.and(named("opt"))
70+
.and(takesArguments(String.class)),
71+
packageName + ".OptAdvice");
72+
}
73+
74+
public static class ConstructorAdvice {
75+
@Advice.OnMethodExit(suppress = Throwable.class)
76+
@Propagation
77+
public static void afterInit(@Advice.This Object self, @Advice.Argument(0) final Object input) {
78+
final PropagationModule iastModule = InstrumentationBridge.PROPAGATION;
79+
if (iastModule != null && input != null) {
80+
iastModule.taintObjectIfTainted(self, input);
81+
}
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)