@@ -15,6 +15,7 @@ private import semmle.code.java.frameworks.HessianBurlap
1515private import semmle.code.java.frameworks.Castor
1616private import semmle.code.java.frameworks.Jackson
1717private import semmle.code.java.frameworks.Jabsorb
18+ private import semmle.code.java.frameworks.JoddJson
1819private import semmle.code.java.frameworks.apache.Lang
1920private import semmle.code.java.Reflection
2021
@@ -192,6 +193,16 @@ predicate unsafeDeserialization(MethodAccess ma, Expr sink) {
192193 or
193194 m instanceof JabsorbFromJsonMethod and
194195 sink = ma .getArgument ( 0 )
196+ or
197+ m instanceof JoddJsonParseMethod and
198+ sink = ma .getArgument ( 0 ) and
199+ (
200+ // User controls the target type for deserialization
201+ any ( UnsafeTypeConfig c ) .hasFlowToExpr ( ma .getArgument ( 1 ) )
202+ or
203+ // jodd.json.JsonParser may be configured for unrestricted deserialization to user-specified types
204+ joddJsonParserConfiguredUnsafely ( ma .getQualifier ( ) )
205+ )
195206 )
196207}
197208
@@ -248,6 +259,17 @@ class UnsafeDeserializationConfig extends TaintTracking::Configuration {
248259 ma .getArgument ( 0 ) = node .asExpr ( ) and
249260 exists ( SafeJsonIoConfig sji | sji .hasFlowToExpr ( ma .getArgument ( 1 ) ) )
250261 )
262+ or
263+ exists ( MethodAccess ma |
264+ // Sanitize the input to jodd.json.JsonParser.parse et al whenever it appears
265+ // to be called with an explicit class argument limiting those types that can
266+ // be instantiated during deserialization.
267+ ma .getMethod ( ) instanceof JoddJsonParseMethod and
268+ ma .getArgument ( 1 ) .getType ( ) instanceof TypeClass and
269+ not ma .getArgument ( 1 ) instanceof NullLiteral and
270+ not ma .getArgument ( 1 ) .getType ( ) .getName ( ) = [ "Class<Object>" , "Class<?>" ] and
271+ node .asExpr ( ) = ma .getAnArgument ( )
272+ )
251273 }
252274}
253275
@@ -295,6 +317,8 @@ class UnsafeTypeConfig extends TaintTracking2::Configuration {
295317 ma .getMethod ( ) instanceof ObjectMapperReadMethod
296318 or
297319 ma .getMethod ( ) instanceof JabsorbUnmarshallMethod
320+ or
321+ ma .getMethod ( ) instanceof JoddJsonParseMethod
298322 ) and
299323 // Note `JacksonTypeDescriptorType` includes plain old `java.lang.Class`
300324 arg .getType ( ) instanceof JacksonTypeDescriptorType and
@@ -356,3 +380,85 @@ class SafeObjectMapperConfig extends DataFlow2::Configuration {
356380 )
357381 }
358382}
383+
384+ /**
385+ * A method that configures Jodd's JsonParser, either enabling dangerous deserialization to
386+ * arbitrary Java types or restricting the types that can be instantiated.
387+ */
388+ private class JoddJsonParserConfigurationMethodQualifier extends DataFlow:: ExprNode {
389+ JoddJsonParserConfigurationMethodQualifier ( ) {
390+ exists ( MethodAccess ma , Method m | ma .getQualifier ( ) = this .asExpr ( ) and m = ma .getMethod ( ) |
391+ m instanceof WithClassMetadataMethod
392+ or
393+ m instanceof SetClassMetadataNameMethod
394+ or
395+ m instanceof AllowClassMethod
396+ )
397+ }
398+ }
399+
400+ /**
401+ * Configuration tracking flow from methods that configure `jodd.json.JsonParser`'s class
402+ * instantiation feature to a `.parse` call on the same parser.
403+ */
404+ private class JoddJsonParserConfigurationMethodConfig extends DataFlow2:: Configuration {
405+ JoddJsonParserConfigurationMethodConfig ( ) {
406+ this = "UnsafeDeserialization::JoddJsonParserConfigurationMethodConfig"
407+ }
408+
409+ override predicate isSource ( DataFlow:: Node src ) {
410+ src instanceof JoddJsonParserConfigurationMethodQualifier
411+ }
412+
413+ override predicate isSink ( DataFlow:: Node sink ) {
414+ exists ( MethodAccess ma |
415+ ma .getMethod ( ) instanceof JoddJsonParseMethod and
416+ sink .asExpr ( ) = ma .getQualifier ( ) // The class type argument
417+ )
418+ }
419+ }
420+
421+ /**
422+ * Gets the qualifier to a method call that configures a `jodd.json.JsonParser` instance unsafely.
423+ *
424+ * Such a parser may instantiate an arbtirary type when deserializing untrusted data.
425+ */
426+ private DataFlow:: Node getAnUnsafelyConfiguredParser ( ) {
427+ exists ( MethodAccess ma | result .asExpr ( ) = ma .getQualifier ( ) |
428+ ma .getMethod ( ) instanceof WithClassMetadataMethod and
429+ ma .getArgument ( 0 ) .( CompileTimeConstantExpr ) .getBooleanValue ( ) = true
430+ or
431+ ma .getMethod ( ) instanceof SetClassMetadataNameMethod and
432+ not ma .getArgument ( 0 ) instanceof NullLiteral
433+ )
434+ }
435+
436+ /**
437+ * Gets the qualifier to a method call that configures a `jodd.json.JsonParser` instance safely.
438+ *
439+ * Such a parser will not instantiate an arbtirary type when deserializing untrusted data.
440+ */
441+ private DataFlow:: Node getASafelyConfiguredParser ( ) {
442+ exists ( MethodAccess ma | result .asExpr ( ) = ma .getQualifier ( ) |
443+ ma .getMethod ( ) instanceof WithClassMetadataMethod and
444+ ma .getArgument ( 0 ) .( CompileTimeConstantExpr ) .getBooleanValue ( ) = false
445+ or
446+ ma .getMethod ( ) instanceof SetClassMetadataNameMethod and
447+ ma .getArgument ( 0 ) instanceof NullLiteral
448+ or
449+ ma .getMethod ( ) instanceof AllowClassMethod
450+ )
451+ }
452+
453+ /**
454+ * Holds if `parseMethodQualifierExpr` is a `jodd.json.JsonParser` instance that is configured unsafely
455+ * and which never appears to be configured safely.
456+ */
457+ private predicate joddJsonParserConfiguredUnsafely ( Expr parserExpr ) {
458+ exists ( DataFlow:: Node parser , JoddJsonParserConfigurationMethodConfig config |
459+ parser .asExpr ( ) = parserExpr
460+ |
461+ config .hasFlow ( getAnUnsafelyConfiguredParser ( ) , parser ) and
462+ not config .hasFlow ( getASafelyConfiguredParser ( ) , parser )
463+ )
464+ }
0 commit comments