@@ -181,6 +181,9 @@ class SafeKryo extends DataFlow2::Configuration {
181181 }
182182}
183183
184+ /**
185+ * Tracks flow from `enableDefaultTyping` calls to a subsequent Jackson deserialization method call.
186+ */
184187private class EnableJacksonDefaultTypingConfig extends DataFlow2:: Configuration {
185188 EnableJacksonDefaultTypingConfig ( ) { this = "EnableJacksonDefaultTypingConfig" }
186189
@@ -191,6 +194,12 @@ private class EnableJacksonDefaultTypingConfig extends DataFlow2::Configuration
191194 override predicate isSink ( DataFlow:: Node sink ) { sink instanceof ObjectMapperReadSink }
192195}
193196
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+ */
194203private class SafeObjectMapperConfig extends DataFlow2:: Configuration {
195204 SafeObjectMapperConfig ( ) { this = "SafeObjectMapperConfig" }
196205
@@ -217,6 +226,12 @@ private class SafeObjectMapperConfig extends DataFlow2::Configuration {
217226 }
218227}
219228
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+ */
220235private class UnsafeTypeConfig extends TaintTracking2:: Configuration {
221236 UnsafeTypeConfig ( ) { this = "UnsafeTypeConfig" }
222237
@@ -232,6 +247,10 @@ private class UnsafeTypeConfig extends TaintTracking2::Configuration {
232247
233248 /**
234249 * 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.
235254 */
236255 override predicate isAdditionalTaintStep ( DataFlow:: Node fromNode , DataFlow:: Node toNode ) {
237256 exists ( MethodAccess ma , RefType returnType | returnType = ma .getMethod ( ) .getReturnType ( ) |
@@ -244,6 +263,9 @@ private class UnsafeTypeConfig extends TaintTracking2::Configuration {
244263
245264/**
246265 * 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.
247269 */
248270predicate createJacksonJsonParserStep ( DataFlow:: Node fromNode , DataFlow:: Node toNode ) {
249271 exists ( MethodAccess ma , Method m | m = ma .getMethod ( ) |
@@ -256,6 +278,9 @@ predicate createJacksonJsonParserStep(DataFlow::Node fromNode, DataFlow::Node to
256278
257279/**
258280 * 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.
259284 */
260285predicate createJacksonTreeNodeStep ( DataFlow:: Node fromNode , DataFlow:: Node toNode ) {
261286 exists ( MethodAccess ma , Method m | m = ma .getMethod ( ) |
@@ -294,6 +319,17 @@ private predicate hasFieldWithJsonTypeAnnotation(RefType type) {
294319 )
295320}
296321
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+
297333predicate unsafeDeserialization ( MethodAccess ma , Expr sink ) {
298334 exists ( Method m | m = ma .getMethod ( ) |
299335 m instanceof ObjectInputStreamReadObjectMethod and
@@ -350,9 +386,7 @@ predicate unsafeDeserialization(MethodAccess ma, Expr sink) {
350386 or
351387 exists ( EnableJacksonDefaultTypingConfig config | config .hasFlowToExpr ( ma .getQualifier ( ) ) )
352388 or
353- exists ( RefType argType , int i | i > 0 and argType = ma .getArgument ( i ) .getType ( ) |
354- hasJsonTypeInfoAnnotation ( argType .( ParameterizedType ) .getATypeArgument ( ) )
355- )
389+ hasArgumentWithUnsafeJacksonAnnotation ( ma )
356390 ) and
357391 not exists ( SafeObjectMapperConfig config | config .hasFlowToExpr ( ma .getQualifier ( ) ) )
358392 )
0 commit comments