Skip to content

Commit 4d0f6a0

Browse files
authored
Merge pull request #9788 from thiggy1342/add-activerecord-annotate
RB: Add ActiveRecord::Relation#annotate to sqlFragmentArgument()
2 parents a1d9228 + 17c8033 commit 4d0f6a0

File tree

7 files changed

+49
-0
lines changed

7 files changed

+49
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
5+
- Calls to `ActiveRecord::Relation#annotate` are now recognized as`SqlExecution`s so that it will be considered as a sink for queries like rb/sql-injection.

ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ private Expr sqlFragmentArgument(MethodCall call) {
133133
or
134134
methodName = "reload" and
135135
result = call.getKeywordArgument("lock")
136+
or
137+
// Calls to `annotate` can be used to add block comments to SQL queries. These are potentially vulnerable to
138+
// SQLi if user supplied input is passed in as an argument.
139+
methodName = "annotate" and
140+
result = call.getArgument(_)
136141
)
137142
)
138143
}

ruby/ql/test/library-tests/frameworks/ActionController.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ actionControllerControllerClasses
22
| ActiveRecord.rb:23:1:39:3 | FooController |
33
| ActiveRecord.rb:41:1:64:3 | BarController |
44
| ActiveRecord.rb:66:1:70:3 | BazController |
5+
| ActiveRecord.rb:72:1:80:3 | AnnotatedController |
56
| app/controllers/comments_controller.rb:1:1:7:3 | CommentsController |
67
| app/controllers/foo/bars_controller.rb:3:1:39:3 | BarsController |
78
| app/controllers/photos_controller.rb:1:1:4:3 | PhotosController |
@@ -12,6 +13,8 @@ actionControllerActionMethods
1213
| ActiveRecord.rb:42:3:47:5 | some_other_request_handler |
1314
| ActiveRecord.rb:49:3:63:5 | safe_paths |
1415
| ActiveRecord.rb:67:3:69:5 | yet_another_handler |
16+
| ActiveRecord.rb:73:3:75:5 | index |
17+
| ActiveRecord.rb:77:3:79:5 | unsafe_action |
1518
| app/controllers/comments_controller.rb:2:3:3:5 | index |
1619
| app/controllers/comments_controller.rb:5:3:6:5 | show |
1720
| app/controllers/foo/bars_controller.rb:5:3:7:5 | index |
@@ -38,6 +41,7 @@ paramsCalls
3841
| ActiveRecord.rb:59:12:59:17 | call to params |
3942
| ActiveRecord.rb:62:15:62:20 | call to params |
4043
| ActiveRecord.rb:68:21:68:26 | call to params |
44+
| ActiveRecord.rb:78:59:78:64 | call to params |
4145
| app/controllers/foo/bars_controller.rb:13:21:13:26 | call to params |
4246
| app/controllers/foo/bars_controller.rb:14:10:14:15 | call to params |
4347
| app/controllers/foo/bars_controller.rb:21:21:21:26 | call to params |
@@ -57,6 +61,7 @@ paramsSources
5761
| ActiveRecord.rb:59:12:59:17 | call to params |
5862
| ActiveRecord.rb:62:15:62:20 | call to params |
5963
| ActiveRecord.rb:68:21:68:26 | call to params |
64+
| ActiveRecord.rb:78:59:78:64 | call to params |
6065
| app/controllers/foo/bars_controller.rb:13:21:13:26 | call to params |
6166
| app/controllers/foo/bars_controller.rb:14:10:14:15 | call to params |
6267
| app/controllers/foo/bars_controller.rb:21:21:21:26 | call to params |

ruby/ql/test/library-tests/frameworks/ActiveRecord.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ activeRecordSqlExecutionRanges
2222
| ActiveRecord.rb:46:20:46:32 | ... + ... |
2323
| ActiveRecord.rb:52:16:52:28 | "name #{...}" |
2424
| ActiveRecord.rb:56:20:56:39 | "username = #{...}" |
25+
| ActiveRecord.rb:78:27:78:76 | "this is an unsafe annotation:..." |
2526
activeRecordModelClassMethodCalls
2627
| ActiveRecord.rb:2:3:2:17 | call to has_many |
2728
| ActiveRecord.rb:6:3:6:24 | call to belongs_to |
@@ -44,6 +45,8 @@ activeRecordModelClassMethodCalls
4445
| ActiveRecord.rb:60:5:60:33 | call to find_by |
4546
| ActiveRecord.rb:62:5:62:34 | call to find |
4647
| ActiveRecord.rb:68:5:68:45 | call to delete_by |
48+
| ActiveRecord.rb:74:13:74:54 | call to annotate |
49+
| ActiveRecord.rb:78:13:78:77 | call to annotate |
4750
potentiallyUnsafeSqlExecutingMethodCall
4851
| ActiveRecord.rb:9:5:9:68 | call to find |
4952
| ActiveRecord.rb:19:5:19:25 | call to destroy_by |
@@ -55,6 +58,7 @@ potentiallyUnsafeSqlExecutingMethodCall
5558
| ActiveRecord.rb:46:5:46:33 | call to delete_by |
5659
| ActiveRecord.rb:52:5:52:29 | call to order |
5760
| ActiveRecord.rb:56:7:56:40 | call to find_by |
61+
| ActiveRecord.rb:78:13:78:77 | call to annotate |
5862
activeRecordModelInstantiations
5963
| ActiveRecord.rb:9:5:9:68 | call to find | ActiveRecord.rb:5:1:15:3 | User |
6064
| ActiveRecord.rb:13:5:13:40 | call to find_by | ActiveRecord.rb:1:1:3:3 | UserGroup |

ruby/ql/test/library-tests/frameworks/ActiveRecord.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,13 @@ def yet_another_handler
6868
Admin.delete_by(params[:admin_condition])
6969
end
7070
end
71+
72+
class AnnotatedController < ActionController::Base
73+
def index
74+
users = User.annotate("this is a safe annotation")
75+
end
76+
77+
def unsafe_action
78+
users = User.annotate("this is an unsafe annotation:#{params[:comment]}")
79+
end
80+
end

ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,17 @@ def yet_another_handler
137137
Admin.delete_by(params[:admin_condition])
138138
end
139139
end
140+
141+
class AnnotatedController < ActionController::Base
142+
def index
143+
name = params[:user_name]
144+
# GOOD: string literal arguments not controlled by user are safe for annotations
145+
users = User.annotate("this is a safe annotation").find_by(user_name: name)
146+
end
147+
148+
def unsafe_action
149+
name = params[:user_name]
150+
# BAD: user input passed into annotations are vulnerable to SQLi
151+
users = User.annotate("this is an unsafe annotation:#{params[:comment]}").find_by(user_name: name)
152+
end
153+
end

ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ edges
3131
| ActiveRecordInjection.rb:99:11:99:17 | ...[...] : | ActiveRecordInjection.rb:104:20:104:32 | ... + ... |
3232
| ActiveRecordInjection.rb:137:21:137:26 | call to params : | ActiveRecordInjection.rb:137:21:137:44 | ...[...] : |
3333
| ActiveRecordInjection.rb:137:21:137:44 | ...[...] : | ActiveRecordInjection.rb:20:22:20:30 | condition : |
34+
| ActiveRecordInjection.rb:151:59:151:64 | call to params : | ActiveRecordInjection.rb:151:59:151:74 | ...[...] : |
35+
| ActiveRecordInjection.rb:151:59:151:74 | ...[...] : | ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." |
3436
nodes
3537
| ActiveRecordInjection.rb:8:25:8:28 | name : | semmle.label | name : |
3638
| ActiveRecordInjection.rb:8:31:8:34 | pass : | semmle.label | pass : |
@@ -80,6 +82,9 @@ nodes
8082
| ActiveRecordInjection.rb:104:20:104:32 | ... + ... | semmle.label | ... + ... |
8183
| ActiveRecordInjection.rb:137:21:137:26 | call to params : | semmle.label | call to params : |
8284
| ActiveRecordInjection.rb:137:21:137:44 | ...[...] : | semmle.label | ...[...] : |
85+
| ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | semmle.label | "this is an unsafe annotation:..." |
86+
| ActiveRecordInjection.rb:151:59:151:64 | call to params : | semmle.label | call to params : |
87+
| ActiveRecordInjection.rb:151:59:151:74 | ...[...] : | semmle.label | ...[...] : |
8388
subpaths
8489
#select
8590
| ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:70:23:70:28 | call to params : | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | This SQL query depends on $@. | ActiveRecordInjection.rb:70:23:70:28 | call to params | a user-provided value |
@@ -99,3 +104,4 @@ subpaths
99104
| ActiveRecordInjection.rb:88:18:88:35 | ...[...] | ActiveRecordInjection.rb:88:18:88:23 | call to params : | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | This SQL query depends on $@. | ActiveRecordInjection.rb:88:18:88:23 | call to params | a user-provided value |
100105
| ActiveRecordInjection.rb:92:21:92:35 | ...[...] | ActiveRecordInjection.rb:92:21:92:26 | call to params : | ActiveRecordInjection.rb:92:21:92:35 | ...[...] | This SQL query depends on $@. | ActiveRecordInjection.rb:92:21:92:26 | call to params | a user-provided value |
101106
| ActiveRecordInjection.rb:104:20:104:32 | ... + ... | ActiveRecordInjection.rb:98:10:98:15 | call to params : | ActiveRecordInjection.rb:104:20:104:32 | ... + ... | This SQL query depends on $@. | ActiveRecordInjection.rb:98:10:98:15 | call to params | a user-provided value |
107+
| ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | ActiveRecordInjection.rb:151:59:151:64 | call to params : | ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | This SQL query depends on $@. | ActiveRecordInjection.rb:151:59:151:64 | call to params | a user-provided value |

0 commit comments

Comments
 (0)