@@ -123,79 +123,137 @@ private module Shared {
123
123
}
124
124
125
125
/**
126
- * An additional step that is preserves dataflow in the context of XSS.
126
+ * Holds if `call` is a method call in ERB file `erb`, targeting a method
127
+ * named `name`.
127
128
*/
128
- predicate isAdditionalXSSFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
129
- // node1 is a `locals` argument to a render call...
130
- exists ( RenderCall call , Pair kvPair , string hashKey |
129
+ pragma [ noinline]
130
+ private predicate isMethodCall ( MethodCall call , string name , ErbFile erb ) {
131
+ name = call .getMethodName ( ) and
132
+ erb = call .getLocation ( ) .getFile ( )
133
+ }
134
+
135
+ /**
136
+ * Holds if some render call passes `value` for `hashKey` in the `locals`
137
+ * argument, in ERB file `erb`.
138
+ */
139
+ pragma [ noinline]
140
+ private predicate renderCallLocals ( string hashKey , Expr value , ErbFile erb ) {
141
+ exists ( RenderCall call , Pair kvPair |
131
142
call .getLocals ( ) .getAKeyValuePair ( ) = kvPair and
132
- kvPair .getValue ( ) = node1 .asExpr ( ) .getExpr ( ) and
133
- kvPair .getKey ( ) .( StringlikeLiteral ) .getValueText ( ) = hashKey and
134
- // `node2` appears in the rendered template file
135
- call .getTemplateFile ( ) = node2 .getLocation ( ) .getFile ( ) and
136
- (
137
- // node2 is an element reference against `local_assigns`
138
- exists (
139
- CfgNodes:: ExprNodes:: ElementReferenceCfgNode refNode , DataFlow:: Node argNode ,
140
- CfgNodes:: ExprNodes:: StringlikeLiteralCfgNode strNode
141
- |
142
- refNode = node2 .asExpr ( ) and
143
- argNode .asExpr ( ) = refNode .getArgument ( 0 ) and
144
- refNode .getReceiver ( ) .getExpr ( ) .( MethodCall ) .getMethodName ( ) = "local_assigns" and
145
- argNode .getALocalSource ( ) = DataFlow:: exprNode ( strNode ) and
146
- strNode .getExpr ( ) .getValueText ( ) = hashKey
147
- )
148
- or
149
- // ...node2 is a "method call" to a "method" with `hashKey` as its name
150
- // TODO: This may be a variable read in reality that we interpret as a method call
151
- exists ( MethodCall varAcc |
152
- varAcc = node2 .asExpr ( ) .( CfgNodes:: ExprNodes:: MethodCallCfgNode ) .getExpr ( ) and
153
- varAcc .getMethodName ( ) = hashKey
154
- )
155
- )
143
+ kvPair .getValue ( ) = value and
144
+ kvPair .getKey ( ) .getValueText ( ) = hashKey and
145
+ call .getTemplateFile ( ) = erb
156
146
)
157
- or
158
- // instance variables in the controller
159
- exists (
160
- ActionControllerActionMethod action , VariableReadAccess viewVarRead , AssignExpr ae ,
161
- FinalInstanceVarWrite controllerVarWrite
147
+ }
148
+
149
+ pragma [ noinline]
150
+ private predicate isFlowFromLocals0 (
151
+ CfgNodes:: ExprNodes:: ElementReferenceCfgNode refNode , string hashKey , ErbFile erb
152
+ ) {
153
+ exists ( DataFlow:: Node argNode , CfgNodes:: ExprNodes:: StringlikeLiteralCfgNode strNode |
154
+ argNode .asExpr ( ) = refNode .getArgument ( 0 ) and
155
+ refNode .getReceiver ( ) .getExpr ( ) .( MethodCall ) .getMethodName ( ) = "local_assigns" and
156
+ argNode .getALocalSource ( ) = DataFlow:: exprNode ( strNode ) and
157
+ strNode .getExpr ( ) .getValueText ( ) = hashKey and
158
+ erb = refNode .getFile ( )
159
+ )
160
+ }
161
+
162
+ private predicate isFlowFromLocals ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
163
+ exists ( string hashKey , ErbFile erb |
164
+ // node1 is a `locals` argument to a render call...
165
+ renderCallLocals ( hashKey , node1 .asExpr ( ) .getExpr ( ) , erb )
162
166
|
163
- viewVarRead = node2 .asExpr ( ) .( CfgNodes:: ExprNodes:: VariableReadAccessCfgNode ) .getExpr ( ) and
164
- action .getDefaultTemplateFile ( ) = viewVarRead .getLocation ( ) .getFile ( ) and
167
+ // node2 is an element reference against `local_assigns`
168
+ isFlowFromLocals0 ( node2 .asExpr ( ) , hashKey , erb )
169
+ or
170
+ // ...node2 is a "method call" to a "method" with `hashKey` as its name
171
+ // TODO: This may be a variable read in reality that we interpret as a method call
172
+ isMethodCall ( node2 .asExpr ( ) .getExpr ( ) , hashKey , erb )
173
+ )
174
+ }
175
+
176
+ /**
177
+ * Holds if `action` contains an assignment of `value` to an instance
178
+ * variable named `name`, in ERB file `erb`.
179
+ */
180
+ pragma [ noinline]
181
+ private predicate actionAssigns (
182
+ ActionControllerActionMethod action , string name , Expr value , ErbFile erb
183
+ ) {
184
+ exists ( AssignExpr ae , FinalInstanceVarWrite controllerVarWrite |
185
+ action .getDefaultTemplateFile ( ) = erb and
186
+ ae .getParent + ( ) = action and
187
+ ae = controllerVarWrite .getAnAssignExpr ( ) and
188
+ name = controllerVarWrite .getVariable ( ) .getName ( ) and
189
+ value = ae .getRightOperand ( )
190
+ )
191
+ }
192
+
193
+ pragma [ noinline]
194
+ private predicate isVariableReadAccess ( VariableReadAccess viewVarRead , string name , ErbFile erb ) {
195
+ erb = viewVarRead .getLocation ( ) .getFile ( ) and
196
+ viewVarRead .getVariable ( ) .getName ( ) = name
197
+ }
198
+
199
+ private predicate isFlowFromControllerInstanceVariable ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
200
+ // instance variables in the controller
201
+ exists ( ActionControllerActionMethod action , string name , ErbFile template |
165
202
// match read to write on variable name
166
- viewVarRead . getVariable ( ) . getName ( ) = controllerVarWrite . getVariable ( ) .getName ( ) and
203
+ actionAssigns ( action , name , node1 . asExpr ( ) .getExpr ( ) , template ) and
167
204
// propagate taint from assignment RHS expr to variable read access in view
168
- ae = controllerVarWrite .getAnAssignExpr ( ) and
169
- node1 .asExpr ( ) .getExpr ( ) = ae .getRightOperand ( ) and
170
- ae .getParent + ( ) = action
205
+ isVariableReadAccess ( node2 .asExpr ( ) .getExpr ( ) , name , template )
171
206
)
172
- or
207
+ }
208
+
209
+ /**
210
+ * Holds if `helperMethod` is a helper method named `name` that is associated
211
+ * with ERB file `erb`.
212
+ */
213
+ pragma [ noinline]
214
+ private predicate isHelperMethod (
215
+ ActionControllerHelperMethod helperMethod , string name , ErbFile erb
216
+ ) {
217
+ helperMethod .getName ( ) = name and
218
+ helperMethod .getControllerClass ( ) = getAssociatedControllerClass ( erb )
219
+ }
220
+
221
+ private predicate isFlowIntoHelperMethod ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
173
222
// flow from template into controller helper method
174
223
exists (
175
- ErbFile template , ActionControllerHelperMethod helperMethod ,
224
+ ErbFile template , ActionControllerHelperMethod helperMethod , string name ,
176
225
CfgNodes:: ExprNodes:: MethodCallCfgNode helperMethodCall , int argIdx
177
226
|
178
- template = node1 .getLocation ( ) .getFile ( ) and
179
- helperMethod .getName ( ) = helperMethodCall .getExpr ( ) .getMethodName ( ) and
180
- helperMethod .getControllerClass ( ) = getAssociatedControllerClass ( template ) and
181
- helperMethodCall .getArgument ( argIdx ) = node1 .asExpr ( ) and
182
- helperMethod .getParameter ( argIdx ) = node2 .asExpr ( ) .getExpr ( )
227
+ isHelperMethod ( helperMethod , name , template ) and
228
+ isMethodCall ( helperMethodCall .getExpr ( ) , name , template ) and
229
+ helperMethodCall .getArgument ( pragma [ only_bind_into ] ( argIdx ) ) = node1 .asExpr ( ) and
230
+ helperMethod .getParameter ( pragma [ only_bind_into ] ( argIdx ) ) = node2 .asExpr ( ) .getExpr ( )
183
231
)
184
- or
232
+ }
233
+
234
+ private predicate isFlowFromHelperMethod ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
185
235
// flow out of controller helper method into template
186
- exists (
187
- ErbFile template , ActionControllerHelperMethod helperMethod ,
188
- CfgNodes:: ExprNodes:: MethodCallCfgNode helperMethodCall
189
- |
190
- template = node2 .getLocation ( ) .getFile ( ) and
191
- helperMethod .getName ( ) = helperMethodCall .getExpr ( ) .getMethodName ( ) and
192
- helperMethod .getControllerClass ( ) = getAssociatedControllerClass ( template ) and
236
+ exists ( ErbFile template , ActionControllerHelperMethod helperMethod , string name |
193
237
// `node1` is an expr node that may be returned by the helper method
194
238
exprNodeReturnedFrom ( node1 , helperMethod ) and
195
239
// `node2` is a call to the helper method
196
- node2 .asExpr ( ) = helperMethodCall
240
+ isHelperMethod ( helperMethod , name , template ) and
241
+ isMethodCall ( node2 .asExpr ( ) .getExpr ( ) , name , template )
197
242
)
198
243
}
244
+
245
+ /**
246
+ * An additional step that is preserves dataflow in the context of XSS.
247
+ */
248
+ predicate isAdditionalXSSFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
249
+ isFlowFromLocals ( node1 , node2 )
250
+ or
251
+ isFlowFromControllerInstanceVariable ( node1 , node2 )
252
+ or
253
+ isFlowIntoHelperMethod ( node1 , node2 )
254
+ or
255
+ isFlowFromHelperMethod ( node1 , node2 )
256
+ }
199
257
}
200
258
201
259
/**
0 commit comments