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