[Java] Add taint tracking through Jackson deserialization#5814
[Java] Add taint tracking through Jackson deserialization#5814aschackmull merged 10 commits intogithub:mainfrom
Conversation
Marcono1234
left a comment
There was a problem hiding this comment.
You probably have to add QLDoc to the newly added classes.
Do you think the following methods (and their overloads) would be interesting as well?
I think the only one that I'd add additionally would probably be |
Right, the other methods probably don't make much sense at the moment since there is no modeling for |
| 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 |
There was a problem hiding this comment.
This test fails as does this one:
https://github.com/github/codeql/pull/5814/files#diff-d947c905365a34b846b5ed166177d4087e649ee718455ec77981934a23c62117R94
Any insights into what's going on here would be greatly appreciated.
https://ghsecuritylab.slack.com/archives/CQJU6RN49/p1620056070330400
There was a problem hiding this comment.
Do you mean it has no result for the getName() call?
You are only modeling field access on types which are deserialized using Jackson, but not method accesses. That is probably the reason then.
(Or should the dataflow library be able to detect getter methods on its own?)
There was a problem hiding this comment.
I was under the impression that data flow would be able to track that the getName call because the declaration of getName itself makes a Field Access and that Field Access itself is considered an additional taint step.
There was a problem hiding this comment.
This ended up being resolved by using DataFlow::getFieldQualifier.
| | ../../../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 | |
There was a problem hiding this comment.
I hope this change is fine. Now that I know that inline tests exits, I'd much rather use those. They are significantly easier to reason about IMHO.
afa5374 to
62d0818
Compare
| } | ||
|
|
||
| /** A type whose values are explicitly deserialized in a call to a Jackson method. */ | ||
| private class ExplicitlyReadJacksonSerializableType extends JacksonDeserializableType { |
There was a problem hiding this comment.
The code here currently distinguishes between serialization and de-serialization. But here it's getting mixed up. This seems like the class should just be named ExplicitlyReadJacksonDeserializableType. Of course, that class already exists, so maybe just add the charpred defined here as an additional disjunct in the existing class?
aschackmull
left a comment
There was a problem hiding this comment.
Some minor comments, otherwise LGTM.
Co-authored-by: Marcono1234 <Marcono1234@users.noreply.github.com>
Co-authored-by: Marcono1234 <Marcono1234@users.noreply.github.com>
62d0818 to
bacc3ef
Compare
| } | ||
|
|
||
| /** A type whose values are explicitly deserialized in a call to a Jackson method. */ | ||
| private class ExplicitlyReadJacksonSerializableType extends JacksonDeserializableType { |
|
Do you think an That way, the |
…zability.qll Co-authored-by: Tony Torralba <atorralba@users.noreply.github.com>
|
@JLLeitschuh A clarifying followup question: In the |
|
According to the documentation, public static void jacksonTwoStepDeserialization() throws java.io.IOException {
String s = taint();
Map<String, Object> taintedParams = new HashMap<>();
taintedParams.put("name", s); // Value is tainted
ObjectMapper om = new ObjectMapper();
JsonNode jn = om.valueToTree(taintedParams); // The jn.get("name").asText() is tainted
sink(jn); //$hasTaintFlow
Potato p = om.convertValue(jn, Potato.class); // the p.name field is tainted
sink(p); //$hasTaintFlow // I just assume the whole object is tainted, because it's easier
sink(p.getName()); //$hasTaintFlow // This is the actual tainted data
}I'm not totally certain I understand your question, but this code comes from an actual example that we have in our production code at Gradle. |
|
I don't see any mention of a map in the doc you linked? I'd be looking to supplement that with to explicitly say that taint input can also be extracted from values of a map. What I'm trying to understand is whether this would complete the model or whether |
|
@aschackmull supplementing it with import java.util.Arrays;
import java.util.Collections;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonTest {
static class MyClass {
public String f;
}
public static void main(String args[]) {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.valueToTree(Arrays.asList("tainted"));
System.out.println(node);
node = mapper.valueToTree(Collections.singletonMap("tainted", 2));
System.out.println(node);
MyClass obj = new MyClass();
obj.f = "tainted";
node = mapper.valueToTree(obj);
System.out.println(node);
}
}So any |
Thanks for the confirmation. That was also what I expected. This is going to need some more dedicated support to model correctly. I've made an issue for tracking this: #5985 |
Adds support for tracking taint through the deserialization of Jackson instantiated POJO objects.
For example: