@@ -75,13 +75,41 @@ module UnsafeDeserialization {
7575 }
7676
7777 /**
78- * An argument in a call to `YAML.load` , considered a sink
78+ * An argument in a call to `YAML.unsafe_*` and `YAML.load_stream` , considered sinks
7979 * for unsafe deserialization. The `YAML` module is an alias of `Psych` in
8080 * recent versions of Ruby.
8181 */
8282 class YamlLoadArgument extends Sink {
8383 YamlLoadArgument ( ) {
84- this = API:: getTopLevelMember ( [ "YAML" , "Psych" ] ) .getAMethodCall ( "load" ) .getArgument ( 0 )
84+ this =
85+ API:: getTopLevelMember ( [ "YAML" , "Psych" ] )
86+ .getAMethodCall ( [ "unsafe_load_file" , "unsafe_load" , "load_stream" ] )
87+ .getArgument ( 0 )
88+ or
89+ this =
90+ API:: getTopLevelMember ( [ "YAML" , "Psych" ] )
91+ .getAMethodCall ( [ "unsafe_load" , "load_stream" ] )
92+ .getKeywordArgument ( "yaml" )
93+ or
94+ this =
95+ API:: getTopLevelMember ( [ "YAML" , "Psych" ] )
96+ .getAMethodCall ( "unsafe_load_file" )
97+ .getKeywordArgument ( "filename" )
98+ }
99+ }
100+
101+ /**
102+ * An argument in a call to `YAML.parse*`, considered sinks
103+ * for unsafe deserialization if there is a call to `to_ruby` on returned value of them,
104+ * so this need some additional taint steps. The `YAML` module is an alias of `Psych` in
105+ * recent versions of Ruby.
106+ */
107+ class YamlParseArgument extends Sink {
108+ YamlParseArgument ( ) {
109+ this =
110+ API:: getTopLevelMember ( [ "YAML" , "Psych" ] )
111+ .getAMethodCall ( [ "parse" , "parse_stream" , "parse_file" ] )
112+ .getAMethodCall ( "to_ruby" )
85113 }
86114 }
87115
@@ -208,4 +236,31 @@ module UnsafeDeserialization {
208236 )
209237 }
210238 }
239+
240+ /**
241+ * check whether an input argument has desired "key: value" input or not.
242+ */
243+ predicate checkkeyValue ( CfgNodes:: ExprNodes:: PairCfgNode p , string key , string value ) {
244+ p .getKey ( ) .getConstantValue ( ) .isStringlikeValue ( key ) and
245+ DataFlow:: exprNode ( p .getValue ( ) ) .getALocalSource ( ) .getConstantValue ( ) .toString ( ) = value
246+ }
247+
248+ /**
249+ * An argument in a call to `Plist.parse_xml` where the marshal is `true` (which is
250+ * the default), considered a sink for unsafe deserialization.
251+ */
252+ class UnsafePlistParsexmlArgument extends Sink {
253+ UnsafePlistParsexmlArgument ( ) {
254+ exists ( DataFlow:: CallNode plistParsexml |
255+ plistParsexml = API:: getTopLevelMember ( "Plist" ) .getAMethodCall ( "parse_xml" )
256+ |
257+ this = [ plistParsexml .getArgument ( 0 ) , plistParsexml .getKeywordArgument ( "filename_or_xml" ) ] and
258+ // Exclude calls that explicitly pass a safe mode option.
259+ checkkeyValue ( plistParsexml .getArgument ( 1 ) .asExpr ( ) , "marshal" , "true" )
260+ or
261+ this = [ plistParsexml .getArgument ( 0 ) , plistParsexml .getKeywordArgument ( "filename_or_xml" ) ] and
262+ plistParsexml .getNumberOfArguments ( ) = 1
263+ )
264+ }
265+ }
211266}
0 commit comments