@@ -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/**
@@ -48,18 +77,10 @@ deprecated class CookiesCall = Rails::CookiesCall;
4877 */
4978class ActionControllerClass extends DataFlow:: ClassNode {
5079 ActionControllerClass ( ) {
51- this =
52- [
53- DataFlow:: getConstant ( "ActionController" ) .getConstant ( "Base" ) ,
54- // In Rails applications `ApplicationController` typically extends `ActionController::Base`, but we
55- // treat it separately in case the `ApplicationController` definition is not in the database.
56- DataFlow:: getConstant ( "ApplicationController" ) ,
57- // ActionController::Metal technically doesn't contain all of the
58- // methods available in Base, such as those for rendering views.
59- // However we prefer to be over-sensitive in this case in order to find
60- // more results.
61- DataFlow:: getConstant ( "ActionController" ) .getConstant ( "Metal" )
62- ] .getADescendentModule ( )
80+ this = DataFlow:: getConstant ( "ApplicationController" ) .getADescendentModule ( )
81+ or
82+ this = actionControllerBaseClass ( ) .getADescendentModule ( ) and
83+ not exists ( DataFlow:: ModuleNode m | m = actionControllerBaseClass ( ) .asModule ( ) | this = m )
6384 }
6485
6586 /**
@@ -83,6 +104,20 @@ class ActionControllerClass extends DataFlow::ClassNode {
83104 }
84105}
85106
107+ private DataFlow:: ConstRef actionControllerBaseClass ( ) {
108+ result =
109+ [
110+ // In Rails applications `ApplicationController` typically extends `ActionController::Base`, but we
111+ // treat it separately in case the `ApplicationController` definition is not in the database.
112+ DataFlow:: getConstant ( "ActionController" ) .getConstant ( "Base" ) ,
113+ // ActionController::Metal technically doesn't contain all of the
114+ // methods available in Base, such as those for rendering views.
115+ // However we prefer to be over-sensitive in this case in order to find
116+ // more results.
117+ DataFlow:: getConstant ( "ActionController" ) .getConstant ( "Metal" )
118+ ]
119+ }
120+
86121private API:: Node actionControllerInstance ( ) {
87122 result = any ( ActionControllerClass cls ) .getSelf ( ) .track ( )
88123}
@@ -431,27 +466,6 @@ class ActionControllerSkipForgeryProtectionCall extends CsrfProtectionSetting::R
431466 override boolean getVerificationSetting ( ) { result = false }
432467}
433468
434- /**
435- * A call to `protect_from_forgery`.
436- */
437- private class ActionControllerProtectFromForgeryCall extends CsrfProtectionSetting:: Range ,
438- DataFlow:: CallNode
439- {
440- ActionControllerProtectFromForgeryCall ( ) {
441- this = actionControllerInstance ( ) .getAMethodCall ( "protect_from_forgery" )
442- }
443-
444- private string getWithValueText ( ) {
445- result = this .getKeywordArgument ( "with" ) .getConstantValue ( ) .getSymbol ( )
446- }
447-
448- // Calls without `with: :exception` can allow for bypassing CSRF protection
449- // in some scenarios.
450- override boolean getVerificationSetting ( ) {
451- if this .getWithValueText ( ) = "exception" then result = true else result = false
452- }
453- }
454-
455469/**
456470 * A call to `send_file`, which sends the file at the given path to the client.
457471 */
0 commit comments