mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
[Java] Add taint tracking through Jackson deserialization
This commit is contained in:
@@ -50,6 +50,15 @@ library class JacksonWriteValueMethod extends Method, TaintPreservingCallable {
|
||||
}
|
||||
}
|
||||
|
||||
library class JacksonReadValueMethod extends Method, TaintPreservingCallable {
|
||||
JacksonReadValueMethod() {
|
||||
getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") and
|
||||
hasName("readValue")
|
||||
}
|
||||
|
||||
override predicate returnsTaintFrom(int arg) { arg = 0 }
|
||||
}
|
||||
|
||||
/** A type whose values are explicitly serialized in a call to a Jackson method. */
|
||||
library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializableType {
|
||||
ExplicitlyWrittenJacksonSerializableType() {
|
||||
@@ -135,6 +144,16 @@ class JacksonDeserializableField extends DeserializableField {
|
||||
}
|
||||
}
|
||||
|
||||
class JacksonDeserializableFieldAccess extends FieldAccess {
|
||||
JacksonDeserializableFieldAccess() { getField() instanceof JacksonDeserializableField }
|
||||
}
|
||||
|
||||
class JacksonDeseializedTaintStep extends AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
node2.asExpr().(JacksonDeserializableFieldAccess).getQualifier() = node1.asExpr()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `addMixInAnnotations` or `addMixIn` Jackson method.
|
||||
*
|
||||
|
||||
@@ -8,28 +8,44 @@ import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||
import com.fasterxml.jackson.databind.ObjectReader;
|
||||
|
||||
class Test {
|
||||
public static class Potato {
|
||||
private String name;
|
||||
|
||||
private String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
public static String taint() {
|
||||
return "tainted";
|
||||
}
|
||||
|
||||
public static void sink(Object any) {}
|
||||
|
||||
public static void jacksonObjectMapper() throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException {
|
||||
String s = taint();
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
File file = new File("testFile");
|
||||
om.writeValue(file, s);
|
||||
sink(file); //$hasTaintFlow
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
om.writeValue(out, s);
|
||||
sink(file); //$hasTaintFlow
|
||||
Writer writer = new StringWriter();
|
||||
om.writeValue(writer, s);
|
||||
sink(writer); //$hasTaintFlow
|
||||
JsonGenerator generator = new JsonFactory().createGenerator(new StringWriter());
|
||||
om.writeValue(generator, s);
|
||||
sink(generator); //$hasTaintFlow
|
||||
String t = om.writeValueAsString(s);
|
||||
System.out.println(t);
|
||||
sink(t); //$hasTaintFlow
|
||||
byte[] bs = om.writeValueAsBytes(s);
|
||||
String reconstructed = new String(bs, "utf-8");
|
||||
System.out.println(reconstructed);
|
||||
sink(bs); //$hasTaintFlow
|
||||
sink(reconstructed); //$hasTaintFlow
|
||||
}
|
||||
|
||||
public static void jacksonObjectWriter() throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException {
|
||||
@@ -37,16 +53,30 @@ class Test {
|
||||
ObjectWriter ow = new ObjectWriter();
|
||||
File file = new File("testFile");
|
||||
ow.writeValue(file, s);
|
||||
sink(file); //$hasTaintFlow
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
ow.writeValue(out, s);
|
||||
sink(out); //$hasTaintFlow
|
||||
Writer writer = new StringWriter();
|
||||
ow.writeValue(writer, s);
|
||||
sink(writer); //$hasTaintFlow
|
||||
JsonGenerator generator = new JsonFactory().createGenerator(new StringWriter());
|
||||
ow.writeValue(generator, s);
|
||||
sink(generator); //$hasTaintFlow
|
||||
String t = ow.writeValueAsString(s);
|
||||
System.out.println(t);
|
||||
sink(t); //$hasTaintFlow
|
||||
byte[] bs = ow.writeValueAsBytes(s);
|
||||
String reconstructed = new String(bs, "utf-8");
|
||||
System.out.println(reconstructed);
|
||||
sink(bs); //$hasTaintFlow
|
||||
sink(reconstructed); //$hasTaintFlow
|
||||
}
|
||||
|
||||
public static void jacksonObjectReader() throws java.io.IOException {
|
||||
String s = taint();
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
ObjectReader reader = om.readerFor(Potato.class);
|
||||
sink(reader.readValue(s)); //$hasTaintFlow
|
||||
sink(reader.readValue(s, Potato.class).name); //$hasTaintFlow
|
||||
sink(reader.readValue(s, Potato.class).getName()); //$hasTaintFlow
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:10:43:10:54 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:13:73:13:84 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:16:44:16:55 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:19:36:19:47 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:22:35:22:46 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:26:36:26:47 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:10:43:10:54 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:13:73:13:84 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:16:44:16:55 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:19:36:19:47 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:22:35:22:46 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:26:36:26:47 | value |
|
||||
| Test.java:18:14:18:20 | taint(...) |
|
||||
| Test.java:21:17:21:20 | file [post update] |
|
||||
| Test.java:21:23:21:23 | s |
|
||||
| Test.java:22:43:22:46 | file |
|
||||
| Test.java:23:17:23:19 | out [post update] |
|
||||
| Test.java:23:22:23:22 | s |
|
||||
| Test.java:25:17:25:22 | writer [post update] |
|
||||
| Test.java:25:25:25:25 | s |
|
||||
| Test.java:27:17:27:25 | generator [post update] |
|
||||
| Test.java:27:28:27:28 | s |
|
||||
| Test.java:28:14:28:37 | writeValueAsString(...) |
|
||||
| Test.java:28:36:28:36 | s |
|
||||
| Test.java:29:22:29:22 | t |
|
||||
| Test.java:30:15:30:37 | writeValueAsBytes(...) |
|
||||
| Test.java:30:36:30:36 | s |
|
||||
| Test.java:31:26:31:48 | new String(...) |
|
||||
| Test.java:31:37:31:38 | bs |
|
||||
| Test.java:32:22:32:34 | reconstructed |
|
||||
| Test.java:36:14:36:20 | taint(...) |
|
||||
| Test.java:39:17:39:20 | file [post update] |
|
||||
| Test.java:39:23:39:23 | s |
|
||||
| Test.java:40:43:40:46 | file |
|
||||
| Test.java:41:17:41:19 | out [post update] |
|
||||
| Test.java:41:22:41:22 | s |
|
||||
| Test.java:43:17:43:22 | writer [post update] |
|
||||
| Test.java:43:25:43:25 | s |
|
||||
| Test.java:45:17:45:25 | generator [post update] |
|
||||
| Test.java:45:28:45:28 | s |
|
||||
| Test.java:46:14:46:37 | writeValueAsString(...) |
|
||||
| Test.java:46:36:46:36 | s |
|
||||
| Test.java:47:22:47:22 | t |
|
||||
| Test.java:48:15:48:37 | writeValueAsBytes(...) |
|
||||
| Test.java:48:36:48:36 | s |
|
||||
| Test.java:49:26:49:48 | new String(...) |
|
||||
| Test.java:49:37:49:38 | bs |
|
||||
| Test.java:50:22:50:34 | reconstructed |
|
||||
|
||||
@@ -1,17 +1,34 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class Conf extends TaintTracking::Configuration {
|
||||
Conf() { this = "qltest:dataflow:jackson" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(MethodAccess).getMethod().hasName("taint")
|
||||
override predicate isSource(DataFlow::Node n) {
|
||||
n.asExpr().(MethodAccess).getMethod().hasName("taint")
|
||||
or
|
||||
n instanceof RemoteFlowSource
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { any() }
|
||||
override predicate isSink(DataFlow::Node n) {
|
||||
exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink, Conf config
|
||||
where config.hasFlow(source, sink)
|
||||
select sink
|
||||
class HasFlowTest extends InlineExpectationsTest {
|
||||
HasFlowTest() { this = "HasFlowTest" }
|
||||
|
||||
override string getARelevantTag() { result = "hasTaintFlow" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "hasTaintFlow" and
|
||||
exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) |
|
||||
sink.getLocation() = location and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -26,4 +26,8 @@ public class ObjectMapper {
|
||||
public String writeValueAsString(Object value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ObjectReader readerFor(Class<?> type) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.fasterxml.jackson.databind;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class ObjectReader {
|
||||
public ObjectReader forType(Class<?> valueType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(String src) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(String src, Class<T> valueType) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(byte[] content) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(byte[] content, Class<T> valueType) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(File src) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(InputStream src) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(InputStream src, Class<T> valueType) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(Reader src) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(Reader src, Class<T> valueType) throws IOException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user