@@ -53,6 +53,14 @@ class ActiveRecordModelClassMethodCall extends MethodCall {
5353 }
5454}
5555
56+ private predicate methodCanTakeSqlFragmentAsFirstArg ( string methodName ) {
57+ methodName =
58+ [
59+ "delete_all" , "destroy_all" , "exists?" , "find_by" , "find_by_sql" , "from" , "group" , "having" ,
60+ "joins" , "lock" , "not" , "order" , "pluck" , "where"
61+ ]
62+ }
63+
5664class PotentiallyUnsafeSqlExecutingMethodCall extends ActiveRecordModelClassMethodCall {
5765 // The name of the method invoked
5866 private string methodName ;
@@ -61,10 +69,6 @@ class PotentiallyUnsafeSqlExecutingMethodCall extends ActiveRecordModelClassMeth
6169 // The SQL fragment argument itself
6270 private Expr sqlFragmentExpr ;
6371
64- // TODO: This is slightly too restricted, we only look for StringlikeLiterals
65- // as arguments, but we could instead have a variable read, a string
66- // concatenation, certain arrays, etc. and still have a potentially
67- // vulnerable call
6872 // TODO: `find` with `lock:` option also takes an SQL fragment
6973 PotentiallyUnsafeSqlExecutingMethodCall ( ) {
7074 methodName = this .getMethodName ( ) and
@@ -73,37 +77,20 @@ class PotentiallyUnsafeSqlExecutingMethodCall extends ActiveRecordModelClassMeth
7377 methodName = "calculate" and sqlFragmentArgumentIndex = 1
7478 or
7579 sqlFragmentArgumentIndex = 0 and
76- (
77- methodName = "delete_all"
78- or
79- methodName = "destroy_all"
80- or
81- methodName = "exists?"
82- or
83- methodName = "find_by"
84- or
85- methodName = "find_by_sql"
86- or
87- methodName = "from"
88- or
89- methodName = "group"
90- or
91- methodName = "having"
92- or
93- methodName = "joins"
94- or
95- methodName = "lock"
96- or
97- methodName = "not"
98- or
99- methodName = "order"
100- or
101- methodName = "pluck"
102- or
103- methodName = "where"
104- )
80+ methodCanTakeSqlFragmentAsFirstArg ( methodName )
10581 ) and
106- sqlFragmentExpr instanceof StringlikeLiteral
82+ (
83+ // select only literals containing an interpolated value...
84+ exists ( StringInterpolationComponent interpolated |
85+ interpolated = sqlFragmentExpr .( StringlikeLiteral ) .getComponent ( _)
86+ )
87+ or
88+ // ...or string concatenations...
89+ sqlFragmentExpr instanceof AddExpr
90+ or
91+ // ...or variable reads
92+ sqlFragmentExpr instanceof VariableReadAccess
93+ )
10794 }
10895
10996 Expr getSqlFragmentSinkArgument ( ) { result = sqlFragmentExpr }
0 commit comments