@@ -75,13 +75,41 @@ module UnsafeDeserialization {
75
75
}
76
76
77
77
/**
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
79
79
* for unsafe deserialization. The `YAML` module is an alias of `Psych` in
80
80
* recent versions of Ruby.
81
81
*/
82
82
class YamlLoadArgument extends Sink {
83
83
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" )
85
113
}
86
114
}
87
115
@@ -208,4 +236,31 @@ module UnsafeDeserialization {
208
236
)
209
237
}
210
238
}
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
+ }
211
266
}
0 commit comments