Skip to content

Commit 56b1f15

Browse files
committed
[Java] Add taint tracking through Jackson deserialization
1 parent d7e560c commit 56b1f15

File tree

6 files changed

+125
-58
lines changed

6 files changed

+125
-58
lines changed

java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ library class JacksonWriteValueMethod extends Method, TaintPreservingCallable {
5050
}
5151
}
5252

53+
library class JacksonReadValueMethod extends Method, TaintPreservingCallable {
54+
JacksonReadValueMethod() {
55+
getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") and
56+
hasName("readValue")
57+
}
58+
59+
override predicate returnsTaintFrom(int arg) { arg = 0 }
60+
}
61+
5362
/** A type whose values are explicitly serialized in a call to a Jackson method. */
5463
library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializableType {
5564
ExplicitlyWrittenJacksonSerializableType() {
@@ -135,6 +144,16 @@ class JacksonDeserializableField extends DeserializableField {
135144
}
136145
}
137146

147+
class JacksonDeserializableFieldAccess extends FieldAccess {
148+
JacksonDeserializableFieldAccess() { getField() instanceof JacksonDeserializableField }
149+
}
150+
151+
class JacksonDeseializedTaintStep extends AdditionalTaintStep {
152+
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
153+
node2.asExpr().(JacksonDeserializableFieldAccess).getQualifier() = node1.asExpr()
154+
}
155+
}
156+
138157
/**
139158
* A call to the `addMixInAnnotations` or `addMixIn` Jackson method.
140159
*

java/ql/test/library-tests/dataflow/taint-jackson/Test.java

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,45 +8,75 @@
88
import com.fasterxml.jackson.core.JsonGenerator;
99
import com.fasterxml.jackson.databind.ObjectMapper;
1010
import com.fasterxml.jackson.databind.ObjectWriter;
11+
import com.fasterxml.jackson.databind.ObjectReader;
1112

1213
class Test {
14+
public static class Potato {
15+
private String name;
16+
17+
private String getName() {
18+
return name;
19+
}
20+
}
21+
1322
public static String taint() {
1423
return "tainted";
1524
}
1625

26+
public static void sink(Object any) {}
27+
1728
public static void jacksonObjectMapper() throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException {
1829
String s = taint();
1930
ObjectMapper om = new ObjectMapper();
2031
File file = new File("testFile");
2132
om.writeValue(file, s);
33+
sink(file); //$hasTaintFlow
2234
OutputStream out = new FileOutputStream(file);
2335
om.writeValue(out, s);
36+
sink(file); //$hasTaintFlow
2437
Writer writer = new StringWriter();
2538
om.writeValue(writer, s);
39+
sink(writer); //$hasTaintFlow
2640
JsonGenerator generator = new JsonFactory().createGenerator(new StringWriter());
2741
om.writeValue(generator, s);
42+
sink(generator); //$hasTaintFlow
2843
String t = om.writeValueAsString(s);
29-
System.out.println(t);
44+
sink(t); //$hasTaintFlow
3045
byte[] bs = om.writeValueAsBytes(s);
3146
String reconstructed = new String(bs, "utf-8");
32-
System.out.println(reconstructed);
47+
sink(bs); //$hasTaintFlow
48+
sink(reconstructed); //$hasTaintFlow
3349
}
3450

3551
public static void jacksonObjectWriter() throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException {
3652
String s = taint();
3753
ObjectWriter ow = new ObjectWriter();
3854
File file = new File("testFile");
3955
ow.writeValue(file, s);
56+
sink(file); //$hasTaintFlow
4057
OutputStream out = new FileOutputStream(file);
4158
ow.writeValue(out, s);
59+
sink(out); //$hasTaintFlow
4260
Writer writer = new StringWriter();
4361
ow.writeValue(writer, s);
62+
sink(writer); //$hasTaintFlow
4463
JsonGenerator generator = new JsonFactory().createGenerator(new StringWriter());
4564
ow.writeValue(generator, s);
65+
sink(generator); //$hasTaintFlow
4666
String t = ow.writeValueAsString(s);
47-
System.out.println(t);
67+
sink(t); //$hasTaintFlow
4868
byte[] bs = ow.writeValueAsBytes(s);
4969
String reconstructed = new String(bs, "utf-8");
50-
System.out.println(reconstructed);
70+
sink(bs); //$hasTaintFlow
71+
sink(reconstructed); //$hasTaintFlow
72+
}
73+
74+
public static void jacksonObjectReader() throws java.io.IOException {
75+
String s = taint();
76+
ObjectMapper om = new ObjectMapper();
77+
ObjectReader reader = om.readerFor(Potato.class);
78+
sink(reader.readValue(s)); //$hasTaintFlow
79+
sink(reader.readValue(s, Potato.class).name); //$hasTaintFlow
80+
sink(reader.readValue(s, Potato.class).getName()); //$hasTaintFlow
5181
}
5282
}
Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +0,0 @@
1-
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:10:43:10:54 | value |
2-
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:13:73:13:84 | value |
3-
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:16:44:16:55 | value |
4-
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:19:36:19:47 | value |
5-
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:22:35:22:46 | value |
6-
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:26:36:26:47 | value |
7-
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:10:43:10:54 | value |
8-
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:13:73:13:84 | value |
9-
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:16:44:16:55 | value |
10-
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:19:36:19:47 | value |
11-
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:22:35:22:46 | value |
12-
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:26:36:26:47 | value |
13-
| Test.java:18:14:18:20 | taint(...) |
14-
| Test.java:21:17:21:20 | file [post update] |
15-
| Test.java:21:23:21:23 | s |
16-
| Test.java:22:43:22:46 | file |
17-
| Test.java:23:17:23:19 | out [post update] |
18-
| Test.java:23:22:23:22 | s |
19-
| Test.java:25:17:25:22 | writer [post update] |
20-
| Test.java:25:25:25:25 | s |
21-
| Test.java:27:17:27:25 | generator [post update] |
22-
| Test.java:27:28:27:28 | s |
23-
| Test.java:28:14:28:37 | writeValueAsString(...) |
24-
| Test.java:28:36:28:36 | s |
25-
| Test.java:29:22:29:22 | t |
26-
| Test.java:30:15:30:37 | writeValueAsBytes(...) |
27-
| Test.java:30:36:30:36 | s |
28-
| Test.java:31:26:31:48 | new String(...) |
29-
| Test.java:31:37:31:38 | bs |
30-
| Test.java:32:22:32:34 | reconstructed |
31-
| Test.java:36:14:36:20 | taint(...) |
32-
| Test.java:39:17:39:20 | file [post update] |
33-
| Test.java:39:23:39:23 | s |
34-
| Test.java:40:43:40:46 | file |
35-
| Test.java:41:17:41:19 | out [post update] |
36-
| Test.java:41:22:41:22 | s |
37-
| Test.java:43:17:43:22 | writer [post update] |
38-
| Test.java:43:25:43:25 | s |
39-
| Test.java:45:17:45:25 | generator [post update] |
40-
| Test.java:45:28:45:28 | s |
41-
| Test.java:46:14:46:37 | writeValueAsString(...) |
42-
| Test.java:46:36:46:36 | s |
43-
| Test.java:47:22:47:22 | t |
44-
| Test.java:48:15:48:37 | writeValueAsBytes(...) |
45-
| Test.java:48:36:48:36 | s |
46-
| Test.java:49:26:49:48 | new String(...) |
47-
| Test.java:49:37:49:38 | bs |
48-
| Test.java:50:22:50:34 | reconstructed |
Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,34 @@
1+
import java
12
import semmle.code.java.dataflow.DataFlow
23
import semmle.code.java.dataflow.TaintTracking
34
import semmle.code.java.dataflow.FlowSources
5+
import TestUtilities.InlineExpectationsTest
46

57
class Conf extends TaintTracking::Configuration {
68
Conf() { this = "qltest:dataflow:jackson" }
79

8-
override predicate isSource(DataFlow::Node source) {
9-
source.asExpr().(MethodAccess).getMethod().hasName("taint")
10+
override predicate isSource(DataFlow::Node n) {
11+
n.asExpr().(MethodAccess).getMethod().hasName("taint")
12+
or
13+
n instanceof RemoteFlowSource
1014
}
1115

12-
override predicate isSink(DataFlow::Node sink) { any() }
16+
override predicate isSink(DataFlow::Node n) {
17+
exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
18+
}
1319
}
1420

15-
from DataFlow::Node source, DataFlow::Node sink, Conf config
16-
where config.hasFlow(source, sink)
17-
select sink
21+
class HasFlowTest extends InlineExpectationsTest {
22+
HasFlowTest() { this = "HasFlowTest" }
23+
24+
override string getARelevantTag() { result = "hasTaintFlow" }
25+
26+
override predicate hasActualResult(Location location, string element, string tag, string value) {
27+
tag = "hasTaintFlow" and
28+
exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) |
29+
sink.getLocation() = location and
30+
element = sink.toString() and
31+
value = ""
32+
)
33+
}
34+
}

java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,8 @@ public byte[] writeValueAsBytes(Object value) {
2626
public String writeValueAsString(Object value) {
2727
return null;
2828
}
29+
30+
public ObjectReader readerFor(Class<?> type) {
31+
return null;
32+
}
2933
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.fasterxml.jackson.databind;
2+
3+
import java.io.*;
4+
5+
public class ObjectReader {
6+
public ObjectReader forType(Class<?> valueType) {
7+
return null;
8+
}
9+
10+
public <T> T readValue(String src) {
11+
return null;
12+
}
13+
14+
public <T> T readValue(String src, Class<T> valueType) throws IOException {
15+
return null;
16+
}
17+
18+
public <T> T readValue(byte[] content) throws IOException {
19+
return null;
20+
}
21+
22+
public <T> T readValue(byte[] content, Class<T> valueType) throws IOException {
23+
return null;
24+
}
25+
26+
public <T> T readValue(File src) throws IOException {
27+
return null;
28+
}
29+
30+
public <T> T readValue(InputStream src) throws IOException {
31+
return null;
32+
}
33+
34+
public <T> T readValue(InputStream src, Class<T> valueType) throws IOException {
35+
return null;
36+
}
37+
38+
public <T> T readValue(Reader src) throws IOException {
39+
return null;
40+
}
41+
42+
public <T> T readValue(Reader src, Class<T> valueType) throws IOException {
43+
return null;
44+
}
45+
}

0 commit comments

Comments
 (0)