@@ -21,6 +21,35 @@ private import codeql.ruby.dataflow.internal.DataFlowDispatch
2121module ActionController {
2222 // TODO: move the rest of this file inside this module.
2323 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+ }
2453}
2554
2655/**
@@ -38,18 +67,10 @@ module ActionController {
3867 */
3968class ActionControllerClass extends DataFlow:: ClassNode {
4069 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 )
5374 }
5475
5576 /**
@@ -73,6 +94,20 @@ class ActionControllerClass extends DataFlow::ClassNode {
7394 }
7495}
7596
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+
76111private API:: Node actionControllerInstance ( ) {
77112 result = any ( ActionControllerClass cls ) .getSelf ( ) .track ( )
78113}
@@ -406,27 +441,6 @@ class ActionControllerSkipForgeryProtectionCall extends CsrfProtectionSetting::R
406441 override boolean getVerificationSetting ( ) { result = false }
407442}
408443
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-
430444/**
431445 * A call to `send_file`, which sends the file at the given path to the client.
432446 */
0 commit comments