forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathJackson.qll
More file actions
146 lines (131 loc) · 5.12 KB
/
Copy pathJackson.qll
File metadata and controls
146 lines (131 loc) · 5.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/**
* Provides classes and predicates for working with the Jackson serialization framework.
*/
import java
private import semmle.code.java.dataflow.DataFlow
private class ObjectMapper extends RefType {
ObjectMapper() {
this.getASupertype*().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper")
}
}
/** A builder for building Jackson's `JsonMapper`. */
class MapperBuilder extends RefType {
MapperBuilder() {
this.hasQualifiedName("com.fasterxml.jackson.databind.cfg", "MapperBuilder<JsonMapper,Builder>")
}
}
private class JsonFactory extends RefType {
JsonFactory() { this.hasQualifiedName("com.fasterxml.jackson.core", "JsonFactory") }
}
private class JsonParser extends RefType {
JsonParser() { this.hasQualifiedName("com.fasterxml.jackson.core", "JsonParser") }
}
/** A type descriptor in Jackson libraries. For example, `java.lang.Class`. */
class JacksonTypeDescriptorType extends RefType {
JacksonTypeDescriptorType() {
this instanceof TypeClass or
this.hasQualifiedName("com.fasterxml.jackson.databind", "JavaType") or
this.hasQualifiedName("com.fasterxml.jackson.core.type", "TypeReference")
}
}
/** A method in `ObjectMapper` that deserialize data. */
class ObjectMapperReadMethod extends Method {
ObjectMapperReadMethod() {
this.getDeclaringType() instanceof ObjectMapper and
this.hasName(["readValue", "readValues", "treeToValue"])
}
}
/** A call that enables the default typing in `ObjectMapper`. */
class EnableJacksonDefaultTyping extends MethodAccess {
EnableJacksonDefaultTyping() {
this.getMethod().getDeclaringType() instanceof ObjectMapper and
this.getMethod().hasName("enableDefaultTyping")
}
}
/** A qualifier of a call to one of the methods in `ObjectMapper` that deserialize data. */
class ObjectMapperReadQualifier extends DataFlow::ExprNode {
ObjectMapperReadQualifier() {
exists(MethodAccess ma | ma.getQualifier() = this.asExpr() |
ma.getMethod() instanceof ObjectMapperReadMethod
)
}
}
/** A source that sets a type validator. */
class SetPolymorphicTypeValidatorSource extends DataFlow::ExprNode {
SetPolymorphicTypeValidatorSource() {
exists(MethodAccess ma, Method m | m = ma.getMethod() |
(
m.getDeclaringType() instanceof ObjectMapper and
m.hasName("setPolymorphicTypeValidator")
or
m.getDeclaringType() instanceof MapperBuilder and
m.hasName("polymorphicTypeValidator")
) and
this.asExpr() = ma.getQualifier()
)
}
}
/**
* Holds if `fromNode` to `toNode` is a dataflow step that creates a Jackson parser.
*
* For example, a `createParser(userString)` call yields a `JsonParser`, which becomes dangerous
* if passed to an unsafely-configured `ObjectMapper`'s `readValue` method.
*/
predicate createJacksonJsonParserStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
exists(MethodAccess ma, Method m | m = ma.getMethod() |
(m.getDeclaringType() instanceof ObjectMapper or m.getDeclaringType() instanceof JsonFactory) and
m.hasName("createParser") and
ma.getArgument(0) = fromNode.asExpr() and
ma = toNode.asExpr()
)
}
/**
* Holds if `fromNode` to `toNode` is a dataflow step that creates a Jackson `TreeNode`.
*
* These are parse trees of user-supplied JSON, which may lead to arbitrary code execution
* if passed to an unsafely-configured `ObjectMapper`'s `treeToValue` method.
*/
predicate createJacksonTreeNodeStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
exists(MethodAccess ma, Method m | m = ma.getMethod() |
m.getDeclaringType() instanceof ObjectMapper and
m.hasName("readTree") and
ma.getArgument(0) = fromNode.asExpr() and
ma = toNode.asExpr()
)
or
exists(MethodAccess ma, Method m | m = ma.getMethod() |
m.getDeclaringType() instanceof JsonParser and
m.hasName("readValueAsTree") and
ma.getQualifier() = fromNode.asExpr() and
ma = toNode.asExpr()
)
}
/**
* Holds if `type` or one of its supertypes has a field with `JsonTypeInfo` annotation
* that enables polymorphic type handling.
*/
private predicate hasJsonTypeInfoAnnotation(RefType type) {
hasFieldWithJsonTypeAnnotation(type.getASupertype*()) or
hasJsonTypeInfoAnnotation(type.getAField().getType())
}
/**
* Holds if `type` has a field with `JsonTypeInfo` annotation
* that enables polymorphic type handling.
*/
private predicate hasFieldWithJsonTypeAnnotation(RefType type) {
exists(Annotation a |
type.getAField().getAnAnnotation() = a and
a.getType().hasQualifiedName("com.fasterxml.jackson.annotation", "JsonTypeInfo") and
a.getValue("use").(VarAccess).getVariable().hasName(["CLASS", "MINIMAL_CLASS"])
)
}
/**
* Holds if `call` is a method call to a Jackson deserialization method such as `ObjectMapper.readValue(String, Class)`,
* and the target deserialized class has a field with a `JsonTypeInfo` annotation that enables polymorphic typing.
*/
predicate hasArgumentWithUnsafeJacksonAnnotation(MethodAccess call) {
call.getMethod() instanceof ObjectMapperReadMethod and
exists(RefType argType, int i | i > 0 and argType = call.getArgument(i).getType() |
hasJsonTypeInfoAnnotation(argType.(ParameterizedType).getATypeArgument())
)
}