@@ -14,21 +14,38 @@ import codeql.ruby.DataFlow
1414import codeql.ruby.TaintTracking
1515import DataFlow:: PathGraph
1616
17+ /**
18+ * any direct parameters reference that happens outside of a strong params method but inside
19+ * of a controller class
20+ */
1721class WeakParams extends Expr {
1822 WeakParams ( ) {
19- allParamsAccess ( this ) or
20- this instanceof ParamsReference
23+ (
24+ allParamsAccess ( this ) or
25+ this instanceof ParamsReference
26+ ) and
27+ this .getEnclosingModule ( ) instanceof ControllerClass and
28+ not this .getEnclosingMethod ( ) instanceof StrongParamsMethod
2129 }
2230}
2331
32+ /**
33+ * A controller class, which extendsd `ApplicationController`
34+ */
2435class ControllerClass extends ModuleBase {
2536 ControllerClass ( ) { this .getModule ( ) .getSuperClass + ( ) .toString ( ) = "ApplicationController" }
2637}
2738
39+ /**
40+ * A method that follows the strong params naming convention
41+ */
2842class StrongParamsMethod extends Method {
2943 StrongParamsMethod ( ) { this .getName ( ) .regexpMatch ( ".*_params" ) }
3044}
3145
46+ /**
47+ * a call to a method that exposes or accesses all parameters from an inbound HTTP request
48+ */
3249predicate allParamsAccess ( MethodCall call ) {
3350 call .getMethodName ( ) = "expose_all" or
3451 call .getMethodName ( ) = "original_hash" or
@@ -39,17 +56,28 @@ predicate allParamsAccess(MethodCall call) {
3956 call .getMethodName ( ) = "POST"
4057}
4158
59+ /**
60+ * A reference to an element in the `params` object
61+ */
4262class ParamsReference extends ElementReference {
4363 ParamsReference ( ) { this .getAChild ( ) .toString ( ) = "params" }
4464}
4565
66+ /**
67+ * returns either Model or ViewModel classes with a base class of `ViewModel` or includes `ActionModel::Model`,
68+ * which are required to support the strong parameters pattern
69+ */
4670class ModelClass extends ModuleBase {
4771 ModelClass ( ) {
4872 this .getModule ( ) .getSuperClass + ( ) .toString ( ) = "ViewModel" or
4973 this .getModule ( ) .getSuperClass + ( ) .getAnIncludedModule ( ) .toString ( ) = "ActionModel::Model"
5074 }
5175}
5276
77+ /**
78+ * A DataFlow::Node representation that corresponds to any argument passed into a method call
79+ * where the receiver is an instance of ModelClass
80+ */
5381class ModelClassMethodArgument extends DataFlow:: Node {
5482 private DataFlow:: CallNode call ;
5583
@@ -59,6 +87,10 @@ class ModelClassMethodArgument extends DataFlow::Node {
5987 }
6088}
6189
90+ /**
91+ * Taint tracking config where the source is a weak params access in a controller and the sink
92+ * is a method call of a model class
93+ */
6294class Configuration extends TaintTracking:: Configuration {
6395 Configuration ( ) { this = "Configuration" }
6496
@@ -70,10 +102,5 @@ class Configuration extends TaintTracking::Configuration {
70102
71103from Configuration config , DataFlow:: PathNode source , DataFlow:: PathNode sink
72104where config .hasFlowPath ( source , sink )
73- select sink .getNode ( ) .( ModelClassMethodArgument ) , source , sink , "This is bad"
74- // from WeakParams params
75- // where
76- // not params.getEnclosingMethod() instanceof StrongParamsMethod and
77- // params.getEnclosingModule() instanceof ControllerClass
78- // select params,
79- // "By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects."
105+ select sink .getNode ( ) .( ModelClassMethodArgument ) , source , sink ,
106+ "By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects. It is safer to follow the 'strong parameters' pattern in Rails, which is outlined here: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html"
0 commit comments