@@ -15,6 +15,7 @@ import codeql.ruby.AST
15
15
import codeql.ruby.Concepts
16
16
import codeql.ruby.frameworks.ActionController
17
17
import codeql.ruby.frameworks.Gemfile
18
+ import codeql.ruby.DataFlow
18
19
19
20
/**
20
21
* Holds if a call to `protect_from_forgery` is made in the controller class `definedIn`,
@@ -28,23 +29,39 @@ private predicate protectFromForgeryCall(
28
29
}
29
30
30
31
/**
31
- * Holds if the Gemfile for this application specifies a version of "rails" < 3.0.0 .
32
- * Rails versions from 3.0.0 onwards enable CSRF protection by default.
32
+ * Holds if the Gemfile for this application specifies a version of "rails" or "actionpack" < 5.2 .
33
+ * Rails versions prior to 5.2 do not enable CSRF protection by default.
33
34
*/
34
- private predicate railsPreVersion3 ( ) {
35
- exists ( Gemfile:: Gem g | g .getName ( ) = "rails" and g .getAVersionConstraint ( ) .before ( "5.2" ) )
35
+ private predicate railsPreVersion5_2 ( ) {
36
+ exists ( Gemfile:: Gem g |
37
+ g .getName ( ) = [ "rails" , "actionpack" ] and g .getAVersionConstraint ( ) .before ( "5.2" )
38
+ )
39
+ }
40
+
41
+ private float getRailsConfigDefaultVersion ( ) {
42
+ exists ( DataFlow:: CallNode config , DataFlow:: CallNode loadDefaultsCall |
43
+ DataFlow:: getConstant ( "Rails" )
44
+ .getConstant ( "Application" )
45
+ .getADescendentModule ( )
46
+ .getAnImmediateReference ( )
47
+ .flowsTo ( config .getReceiver ( ) ) and
48
+ config .getMethodName ( ) = "config" and
49
+ loadDefaultsCall .getReceiver ( ) = config and
50
+ loadDefaultsCall .getMethodName ( ) = "load_defaults" and
51
+ result = loadDefaultsCall .getArgument ( 0 ) .getConstantValue ( ) .getFloat ( )
52
+ )
36
53
}
37
54
38
55
from ActionControllerClass c
39
56
where
40
57
not protectFromForgeryCall ( _, c , _) and
41
- // Rails versions prior to 3.0.0 require CSRF protection to be explicitly enabled.
42
- // For later versions, there must exist a call to `csrf_meta_tags` in every HTML response.
43
- // We currently just check for a call to this method anywhere in the codebase.
44
58
(
45
- railsPreVersion3 ( )
59
+ // Rails versions prior to 5.2 require CSRF protection to be explicitly enabled.
60
+ railsPreVersion5_2 ( )
46
61
or
47
- not any ( MethodCall m ) .getMethodName ( ) = [ "csrf_meta_tags" , "csrf_meta_tag" ]
62
+ // For Rails >= 5.2, CSRF protection is enabled by default if there is a `load_defaults` call in the root application class
63
+ // which specifies a version >= 5.2.
64
+ not getRailsConfigDefaultVersion ( ) >= 5.2
48
65
) and
49
66
// Only generate alerts for the topmost controller in the tree.
50
67
not exists ( ActionControllerClass parent | c = parent .getAnImmediateDescendent ( ) )
0 commit comments