@@ -21,6 +21,35 @@ private import codeql.ruby.dataflow.internal.DataFlowDispatch
21
21
module ActionController {
22
22
// TODO: move the rest of this file inside this module.
23
23
import codeql.ruby.frameworks.actioncontroller.Filters
24
+
25
+ /**
26
+ * An ActionController class which sits at the top of the class hierarchy.
27
+ * In other words, it does not subclass any other class in source code.
28
+ */
29
+ class RootController extends ActionControllerClass {
30
+ RootController ( ) {
31
+ not exists ( ActionControllerClass parent | this != parent and this = parent .getADescendent ( ) )
32
+ }
33
+ }
34
+
35
+ /**
36
+ * A call to `protect_from_forgery`.
37
+ */
38
+ class ProtectFromForgeryCall extends CsrfProtectionSetting:: Range , DataFlow:: CallNode {
39
+ ProtectFromForgeryCall ( ) {
40
+ this = actionControllerInstance ( ) .getAMethodCall ( "protect_from_forgery" )
41
+ }
42
+
43
+ private string getWithValueText ( ) {
44
+ result = this .getKeywordArgument ( "with" ) .getConstantValue ( ) .getSymbol ( )
45
+ }
46
+
47
+ // Calls without `with: :exception` can allow for bypassing CSRF protection
48
+ // in some scenarios.
49
+ override boolean getVerificationSetting ( ) {
50
+ if this .getWithValueText ( ) = "exception" then result = true else result = false
51
+ }
52
+ }
24
53
}
25
54
26
55
/**
@@ -38,18 +67,10 @@ module ActionController {
38
67
*/
39
68
class ActionControllerClass extends DataFlow:: ClassNode {
40
69
ActionControllerClass ( ) {
41
- this =
42
- [
43
- DataFlow:: getConstant ( "ActionController" ) .getConstant ( "Base" ) ,
44
- // In Rails applications `ApplicationController` typically extends `ActionController::Base`, but we
45
- // treat it separately in case the `ApplicationController` definition is not in the database.
46
- DataFlow:: getConstant ( "ApplicationController" ) ,
47
- // ActionController::Metal technically doesn't contain all of the
48
- // methods available in Base, such as those for rendering views.
49
- // However we prefer to be over-sensitive in this case in order to find
50
- // more results.
51
- DataFlow:: getConstant ( "ActionController" ) .getConstant ( "Metal" )
52
- ] .getADescendentModule ( )
70
+ this = DataFlow:: getConstant ( "ApplicationController" ) .getADescendentModule ( )
71
+ or
72
+ this = actionControllerBaseClass ( ) .getADescendentModule ( ) and
73
+ not exists ( DataFlow:: ModuleNode m | m = actionControllerBaseClass ( ) .asModule ( ) | this = m )
53
74
}
54
75
55
76
/**
@@ -73,6 +94,20 @@ class ActionControllerClass extends DataFlow::ClassNode {
73
94
}
74
95
}
75
96
97
+ private DataFlow:: ConstRef actionControllerBaseClass ( ) {
98
+ result =
99
+ [
100
+ // In Rails applications `ApplicationController` typically extends `ActionController::Base`, but we
101
+ // treat it separately in case the `ApplicationController` definition is not in the database.
102
+ DataFlow:: getConstant ( "ActionController" ) .getConstant ( "Base" ) ,
103
+ // ActionController::Metal technically doesn't contain all of the
104
+ // methods available in Base, such as those for rendering views.
105
+ // However we prefer to be over-sensitive in this case in order to find
106
+ // more results.
107
+ DataFlow:: getConstant ( "ActionController" ) .getConstant ( "Metal" )
108
+ ]
109
+ }
110
+
76
111
private API:: Node actionControllerInstance ( ) {
77
112
result = any ( ActionControllerClass cls ) .getSelf ( ) .track ( )
78
113
}
@@ -406,27 +441,6 @@ class ActionControllerSkipForgeryProtectionCall extends CsrfProtectionSetting::R
406
441
override boolean getVerificationSetting ( ) { result = false }
407
442
}
408
443
409
- /**
410
- * A call to `protect_from_forgery`.
411
- */
412
- private class ActionControllerProtectFromForgeryCall extends CsrfProtectionSetting:: Range ,
413
- DataFlow:: CallNode
414
- {
415
- ActionControllerProtectFromForgeryCall ( ) {
416
- this = actionControllerInstance ( ) .getAMethodCall ( "protect_from_forgery" )
417
- }
418
-
419
- private string getWithValueText ( ) {
420
- result = this .getKeywordArgument ( "with" ) .getConstantValue ( ) .getSymbol ( )
421
- }
422
-
423
- // Calls without `with: :exception` can allow for bypassing CSRF protection
424
- // in some scenarios.
425
- override boolean getVerificationSetting ( ) {
426
- if this .getWithValueText ( ) = "exception" then result = true else result = false
427
- }
428
- }
429
-
430
444
/**
431
445
* A call to `send_file`, which sends the file at the given path to the client.
432
446
*/
0 commit comments