@@ -181,6 +181,9 @@ class SafeKryo extends DataFlow2::Configuration {
181
181
}
182
182
}
183
183
184
+ /**
185
+ * Tracks flow from `enableDefaultTyping` calls to a subsequent Jackson deserialization method call.
186
+ */
184
187
private class EnableJacksonDefaultTypingConfig extends DataFlow2:: Configuration {
185
188
EnableJacksonDefaultTypingConfig ( ) { this = "EnableJacksonDefaultTypingConfig" }
186
189
@@ -191,6 +194,12 @@ private class EnableJacksonDefaultTypingConfig extends DataFlow2::Configuration
191
194
override predicate isSink ( DataFlow:: Node sink ) { sink instanceof ObjectMapperReadSink }
192
195
}
193
196
197
+ /**
198
+ * Tracks flow from calls, which set a type validator, to a subsequent Jackson deserialization method call,
199
+ * including across builder method calls.
200
+ *
201
+ * Such a Jackson deserialization method call is safe because validation will likely prevent instantiating unexpected types.
202
+ */
194
203
private class SafeObjectMapperConfig extends DataFlow2:: Configuration {
195
204
SafeObjectMapperConfig ( ) { this = "SafeObjectMapperConfig" }
196
205
@@ -217,6 +226,12 @@ private class SafeObjectMapperConfig extends DataFlow2::Configuration {
217
226
}
218
227
}
219
228
229
+ /**
230
+ * Tracks flow from a remote source to a type descriptor (e.g. a `java.lang.Class` instance)
231
+ * passed to a Jackson deserialization method.
232
+ *
233
+ * If this is user-controlled, arbitrary code could be executed while instantiating the user-specified type.
234
+ */
220
235
private class UnsafeTypeConfig extends TaintTracking2:: Configuration {
221
236
UnsafeTypeConfig ( ) { this = "UnsafeTypeConfig" }
222
237
@@ -232,6 +247,10 @@ private class UnsafeTypeConfig extends TaintTracking2::Configuration {
232
247
233
248
/**
234
249
* Holds if `fromNode` to `toNode` is a dataflow step that looks like resolving a class.
250
+ *
251
+ * Note any method that returns a `Class` or similar is assumed to propagate taint from all
252
+ * of its arguments, so methods that accept user-controlled data but sanitize it or use it for some
253
+ * completely different purpose before returning a `Class` could result in false positives.
235
254
*/
236
255
override predicate isAdditionalTaintStep ( DataFlow:: Node fromNode , DataFlow:: Node toNode ) {
237
256
exists ( MethodAccess ma , RefType returnType | returnType = ma .getMethod ( ) .getReturnType ( ) |
@@ -244,6 +263,9 @@ private class UnsafeTypeConfig extends TaintTracking2::Configuration {
244
263
245
264
/**
246
265
* Holds if `fromNode` to `toNode` is a dataflow step that creates a Jackson parser.
266
+ *
267
+ * For example, a `createParser(userString)` call yields a `JsonParser` which becomes dangerous
268
+ * if passed to an unsafely-configured `ObjectMapper`'s `readValue` method.
247
269
*/
248
270
predicate createJacksonJsonParserStep ( DataFlow:: Node fromNode , DataFlow:: Node toNode ) {
249
271
exists ( MethodAccess ma , Method m | m = ma .getMethod ( ) |
@@ -256,6 +278,9 @@ predicate createJacksonJsonParserStep(DataFlow::Node fromNode, DataFlow::Node to
256
278
257
279
/**
258
280
* Holds if `fromNode` to `toNode` is a dataflow step that creates a Jackson `TreeNode`.
281
+ *
282
+ * These are parse trees of user-supplied JSON, which may lead to arbitrary code execution
283
+ * if passed to an unsafely-configured `ObjectMapper`'s `treeToValue` method.
259
284
*/
260
285
predicate createJacksonTreeNodeStep ( DataFlow:: Node fromNode , DataFlow:: Node toNode ) {
261
286
exists ( MethodAccess ma , Method m | m = ma .getMethod ( ) |
@@ -294,6 +319,17 @@ private predicate hasFieldWithJsonTypeAnnotation(RefType type) {
294
319
)
295
320
}
296
321
322
+ /**
323
+ * Holds if `call` is a method call to a Jackson deserialization method such as `ObjectMapper.readValue(String, Class)`,
324
+ * and the target deserialized class has a field with a `JsonTypeInfo` annotation that enables polymorphic typing.
325
+ */
326
+ private predicate hasArgumentWithUnsafeJacksonAnnotation ( MethodAccess call ) {
327
+ call .getMethod ( ) instanceof ObjectMapperReadMethod and
328
+ exists ( RefType argType , int i | i > 0 and argType = call .getArgument ( i ) .getType ( ) |
329
+ hasJsonTypeInfoAnnotation ( argType .( ParameterizedType ) .getATypeArgument ( ) )
330
+ )
331
+ }
332
+
297
333
predicate unsafeDeserialization ( MethodAccess ma , Expr sink ) {
298
334
exists ( Method m | m = ma .getMethod ( ) |
299
335
m instanceof ObjectInputStreamReadObjectMethod and
@@ -350,9 +386,7 @@ predicate unsafeDeserialization(MethodAccess ma, Expr sink) {
350
386
or
351
387
exists ( EnableJacksonDefaultTypingConfig config | config .hasFlowToExpr ( ma .getQualifier ( ) ) )
352
388
or
353
- exists ( RefType argType , int i | i > 0 and argType = ma .getArgument ( i ) .getType ( ) |
354
- hasJsonTypeInfoAnnotation ( argType .( ParameterizedType ) .getATypeArgument ( ) )
355
- )
389
+ hasArgumentWithUnsafeJacksonAnnotation ( ma )
356
390
) and
357
391
not exists ( SafeObjectMapperConfig config | config .hasFlowToExpr ( ma .getQualifier ( ) ) )
358
392
)
0 commit comments