@@ -9,6 +9,7 @@ private import codeql.ruby.controlflow.CfgNodes::ExprNodes
99private import codeql.ruby.DataFlow
1010private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
1111private import codeql.ruby.ast.internal.Constant
12+ private import codeql.ruby.ast.internal.Module
1213
1314/**
1415 * Provides modeling for ActionController filters.
@@ -34,6 +35,17 @@ module Filters {
3435 }
3536 }
3637
38+ bindingset [ call]
39+ pragma [ inline_late]
40+ private ActionControllerActionMethod getADescendentAction ( MethodCallCfgNode call ) {
41+ result = call .getExpr ( ) .getEnclosingModule ( ) .getAMethod ( )
42+ or
43+ exists ( ModuleBase m |
44+ m .getModule ( ) = call .getExpr ( ) .getEnclosingModule ( ) .getModule ( ) .getAnImmediateDescendent + ( ) and
45+ result = m .getAMethod ( )
46+ )
47+ }
48+
3749 /**
3850 * A call to a class method that adds or removes a filter from the callback chain.
3951 * This class exists to encapsulate common behavior between calls that
@@ -64,14 +76,7 @@ module Filters {
6476 not exists ( this .getOnlyArgument ( ) ) and
6577 forall ( string except | except = this .getExceptArgument ( ) | result .getName ( ) != except )
6678 ) and
67- (
68- result = this .getExpr ( ) .getEnclosingModule ( ) .getAMethod ( )
69- or
70- exists ( ModuleBase m |
71- m .getModule ( ) = this .getExpr ( ) .getEnclosingModule ( ) .getModule ( ) .getADescendent ( ) and
72- result = m .getAMethod ( )
73- )
74- )
79+ result = getADescendentAction ( this )
7580 }
7681
7782 private string getOnlyArgument ( ) {
@@ -104,8 +109,12 @@ module Filters {
104109
105110 StringlikeLiteralCfgNode getFilterArgument ( ) { result = this .getPositionalArgument ( _) }
106111
112+ string getFilterArgumentName ( ) {
113+ result = this .getFilterArgument ( ) .getConstantValue ( ) .getStringlikeValue ( )
114+ }
115+
107116 /**
108- * Gets the callable that implements the filter with name `name` .
117+ * Gets the callable that implements a filter registered by this call .
109118 * This currently only finds methods in the local class or superclass.
110119 * It doesn't handle:
111120 * - lambdas
@@ -122,10 +131,9 @@ module Filters {
122131 * end
123132 * ```
124133 */
125- Callable getFilterCallable ( string name ) {
126- result .( MethodBase ) .getName ( ) = name and
127- result .getEnclosingModule ( ) .getModule ( ) =
128- this .getExpr ( ) .getEnclosingModule ( ) .getModule ( ) .getAnAncestor ( )
134+ Callable getAFilterCallable ( ) {
135+ result =
136+ lookupMethod ( this .getExpr ( ) .getEnclosingModule ( ) .getModule ( ) , this .getFilterArgumentName ( ) )
129137 }
130138 }
131139
@@ -321,7 +329,9 @@ module Filters {
321329
322330 string getFilterName ( ) { result = this .getConstantValue ( ) .getStringlikeValue ( ) }
323331
324- Callable getFilterCallable ( ) { result = call .getFilterCallable ( this .getFilterName ( ) ) }
332+ Callable getFilterCallable ( ) {
333+ result = call .getAFilterCallable ( ) and result .( MethodBase ) .getName ( ) = this .getFilterName ( )
334+ }
325335
326336 ActionControllerActionMethod getAnAction ( ) { result = call .getAnAction ( ) }
327337 }
@@ -387,4 +397,62 @@ module Filters {
387397 * `pred` and `succ` may be methods bound to callbacks or controller actions.
388398 */
389399 predicate next ( Method pred , Method succ ) { next ( _, pred , succ ) }
400+
401+ /**
402+ * Holds if `n` is a post-update node for `self` in method `m`.
403+ */
404+ private predicate selfPostUpdate ( DataFlow:: PostUpdateNode n , Method m ) {
405+ n .getPreUpdateNode ( ) .asExpr ( ) .getExpr ( ) =
406+ any ( SelfVariableAccess self |
407+ pragma [ only_bind_into ] ( m ) = self .getEnclosingCallable ( ) and
408+ self .getVariable ( ) .getDeclaringScope ( ) = m
409+ )
410+ }
411+
412+ /**
413+ * Holds if `n` is the self parameter of method `m`.
414+ */
415+ private predicate selfParameter ( DataFlowPrivate:: SelfParameterNode n , Method m ) {
416+ m = n .getMethod ( )
417+ }
418+
419+ /**
420+ * A class defining additional jump steps arising from filters.
421+ */
422+ class FilterJumpStep extends DataFlowPrivate:: AdditionalJumpStep {
423+ /**
424+ * Holds if data can flow from `pred` to `succ` via a callback chain.
425+ * `pred` is the post-update node of the self parameter in a method, and
426+ * `succ` is the self parameter of a subsequent method that is executed as
427+ * part of the callback chain.
428+ */
429+ override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
430+ exists ( Method predMethod , Method succMethod | next ( predMethod , succMethod ) |
431+ // Flow from a post-update node of self in `pred` to the self parameter of `succ`
432+ //
433+ // def a
434+ // foo() ---------+
435+ // @x = 1 ---+ |
436+ // end | |
437+ // | |
438+ // def b <----+----+
439+ // ...
440+ //
441+ selfPostUpdate ( pred , predMethod ) and
442+ selfParameter ( succ , succMethod )
443+ or
444+ // Flow from the self parameter of `pred` to the self parameter of `succ`
445+ //
446+ // def a ---+
447+ // ... |
448+ // end |
449+ // |
450+ // def b <-+
451+ // ...
452+ //
453+ selfParameter ( pred , predMethod ) and
454+ selfParameter ( succ , succMethod )
455+ )
456+ }
457+ }
390458}
0 commit comments