@@ -123,79 +123,137 @@ private module Shared {
123123 }
124124
125125 /**
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`.
127128 */
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 |
131142 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
156146 )
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 )
162166 |
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 |
165202 // match read to write on variable name
166- viewVarRead . getVariable ( ) . getName ( ) = controllerVarWrite . getVariable ( ) .getName ( ) and
203+ actionAssigns ( action , name , node1 . asExpr ( ) .getExpr ( ) , template ) and
167204 // 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 )
171206 )
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 ) {
173222 // flow from template into controller helper method
174223 exists (
175- ErbFile template , ActionControllerHelperMethod helperMethod ,
224+ ErbFile template , ActionControllerHelperMethod helperMethod , string name ,
176225 CfgNodes:: ExprNodes:: MethodCallCfgNode helperMethodCall , int argIdx
177226 |
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 ( )
183231 )
184- or
232+ }
233+
234+ private predicate isFlowFromHelperMethod ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
185235 // 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 |
193237 // `node1` is an expr node that may be returned by the helper method
194238 exprNodeReturnedFrom ( node1 , helperMethod ) and
195239 // `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 )
197242 )
198243 }
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+ }
199257}
200258
201259/**
0 commit comments