@@ -15,6 +15,7 @@ import codeql.ruby.AST
1515import codeql.ruby.Concepts
1616import codeql.ruby.frameworks.ActionController
1717import codeql.ruby.frameworks.Gemfile
18+ import codeql.ruby.DataFlow
1819
1920/**
2021 * Holds if a call to `protect_from_forgery` is made in the controller class `definedIn`,
@@ -28,23 +29,39 @@ private predicate protectFromForgeryCall(
2829}
2930
3031/**
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.
3334 */
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+ )
3653}
3754
3855from ActionControllerClass c
3956where
4057 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.
4458 (
45- railsPreVersion3 ( )
59+ // Rails versions prior to 5.2 require CSRF protection to be explicitly enabled.
60+ railsPreVersion5_2 ( )
4661 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
4865 ) and
4966 // Only generate alerts for the topmost controller in the tree.
5067 not exists ( ActionControllerClass parent | c = parent .getAnImmediateDescendent ( ) )
0 commit comments