@@ -122,96 +122,129 @@ private module Shared {
122122 AssignExpr getAnAssignExpr ( ) { result .getLeftOperand ( ) = this .getExpr ( ) }
123123 }
124124
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 |
128142 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 )
153173 )
154174 }
155175
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+
156199 private predicate isFlowFromControllerInstanceVariable ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
157200 // 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 |
164202 // match read to write on variable name
165- viewVarRead . getVariable ( ) . getName ( ) = controllerVarWrite . getVariable ( ) .getName ( ) and
203+ actionAssigns ( action , name , node1 . asExpr ( ) .getExpr ( ) , template ) and
166204 // 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 )
170206 )
171207 }
172208
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+
173221 private predicate isFlowIntoHelperMethod ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
174222 // flow from template into controller helper method
175223 exists (
176- ErbFile template , ActionControllerHelperMethod helperMethod ,
224+ ErbFile template , ActionControllerHelperMethod helperMethod , string name ,
177225 CfgNodes:: ExprNodes:: MethodCallCfgNode helperMethodCall , int argIdx
178226 |
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 ( )
184231 )
185232 }
186233
187- pragma [ noinline]
188- private predicate isHelperMethodNameMatch (
189- ActionControllerHelperMethod helperMethod , MethodCall call
190- ) {
191- helperMethod .getName ( ) = call .getMethodName ( )
192- }
193-
194234 private predicate isFlowFromHelperMethod ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
195235 // 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 )
208242 )
209243 }
210244
211245 /**
212246 * An additional step that is preserves dataflow in the context of XSS.
213247 */
214- pragma [ noopt]
215248 predicate isAdditionalXSSFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
216249 isFlowFromLocals ( node1 , node2 )
217250 or
0 commit comments