Skip to content

Commit 0a3d73d

Browse files
Add flow steps and sanitizers for permit calls
1 parent 976ca48 commit 0a3d73d

File tree

3 files changed

+78
-22
lines changed

3 files changed

+78
-22
lines changed

ruby/ql/lib/codeql/ruby/security/MassAssignmentCustomizations.qll

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ module MassAssignment {
4141

4242
private class RemoteSource extends Source instanceof RemoteFlowSource { }
4343

44+
/** A call to `permit!`, which permits each key of its receiver. */
4445
private class PermitBangCall extends MassPermit instanceof DataFlow::CallNode {
45-
PermitBangCall() { this.asExpr().getExpr().(MethodCall).getMethodName() = "permit!" }
46+
PermitBangCall() { this.(DataFlow::CallNode).getMethodName() = "permit!" }
4647

4748
override DataFlow::Node getParamsArgument() { result = this.(DataFlow::CallNode).getReceiver() }
4849

@@ -52,4 +53,45 @@ module MassAssignment {
5253
result.(DataFlow::PostUpdateNode).getPreUpdateNode() = this.getParamsArgument()
5354
}
5455
}
56+
57+
/** Holds if `h` is an empty hash or contains an empty hash at one if its (possibly nested) values. */
58+
private predicate hasEmptyHash(Expr e) {
59+
e instanceof HashLiteral and
60+
count(e.(HashLiteral).getAKeyValuePair()) = 0
61+
or
62+
hasEmptyHash(e.(HashLiteral).getAKeyValuePair().getValue())
63+
or
64+
hasEmptyHash(e.(Pair).getValue())
65+
or
66+
hasEmptyHash(e.(ArrayLiteral).getAnElement())
67+
}
68+
69+
/** A call to `permit` that fully specifies the permitted parameters. */
70+
private class PermitCallSanitizer extends Sanitizer, DataFlow::CallNode {
71+
PermitCallSanitizer() {
72+
this.getMethodName() = "permit" and
73+
not hasEmptyHash(this.getArgument(_).asExpr().getExpr())
74+
}
75+
}
76+
77+
/** A call to `permit` that uses an empty hash, which allows arbitrary keys to be specified. */
78+
private class PermitCallMassPermit extends MassPermit instanceof DataFlow::CallNode {
79+
PermitCallMassPermit() {
80+
this.(DataFlow::CallNode).getMethodName() = "permit" and
81+
hasEmptyHash(this.(DataFlow::CallNode).getArgument(_).asExpr().getExpr())
82+
}
83+
84+
override DataFlow::Node getParamsArgument() { result = this.(DataFlow::CallNode).getReceiver() }
85+
86+
override DataFlow::Node getPermittedParamsResult() { result = this }
87+
}
88+
89+
/** A call to `to_unsafe_h`, which allows arbitrary parameter. */
90+
private class ToUnsafeHashCall extends MassPermit instanceof DataFlow::CallNode {
91+
ToUnsafeHashCall() { this.(DataFlow::CallNode).getMethodName() = "to_unsafe_h" }
92+
93+
override DataFlow::Node getParamsArgument() { result = this.(DataFlow::CallNode).getReceiver() }
94+
95+
override DataFlow::Node getPermittedParamsResult() { result = this }
96+
}
5597
}
Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
11
edges
2-
| test.rb:23:25:23:37 | call to [] [element 0] | test.rb:23:25:23:37 | call to [] | provenance | |
3-
| test.rb:23:26:23:36 | call to user_params | test.rb:23:25:23:37 | call to [] [element 0] | provenance | |
4-
| test.rb:24:26:24:38 | call to [] [element 0] | test.rb:24:26:24:38 | call to [] | provenance | |
5-
| test.rb:24:27:24:37 | call to user_params | test.rb:24:26:24:38 | call to [] [element 0] | provenance | |
6-
| test.rb:30:21:30:33 | call to [] [element 0] | test.rb:30:21:30:33 | call to [] | provenance | |
7-
| test.rb:30:22:30:32 | call to user_params | test.rb:30:21:30:33 | call to [] [element 0] | provenance | |
82
| test.rb:43:9:43:14 | call to params | test.rb:43:9:43:29 | call to require | provenance | |
93
| test.rb:43:9:43:29 | call to require | test.rb:43:9:43:37 | call to permit! | provenance | |
104
| test.rb:43:9:43:37 | call to permit! | test.rb:8:18:8:28 | call to user_params | provenance | |
@@ -13,41 +7,37 @@ edges
137
| test.rb:43:9:43:37 | call to permit! | test.rb:20:22:20:32 | call to user_params | provenance | |
148
| test.rb:43:9:43:37 | call to permit! | test.rb:21:21:21:31 | call to user_params | provenance | |
159
| test.rb:43:9:43:37 | call to permit! | test.rb:22:22:22:32 | call to user_params | provenance | |
16-
| test.rb:43:9:43:37 | call to permit! | test.rb:23:26:23:36 | call to user_params | provenance | |
17-
| test.rb:43:9:43:37 | call to permit! | test.rb:24:27:24:37 | call to user_params | provenance | |
1810
| test.rb:43:9:43:37 | call to permit! | test.rb:25:21:25:31 | call to user_params | provenance | |
1911
| test.rb:43:9:43:37 | call to permit! | test.rb:26:24:26:34 | call to user_params | provenance | |
2012
| test.rb:43:9:43:37 | call to permit! | test.rb:27:22:27:32 | call to user_params | provenance | |
2113
| test.rb:43:9:43:37 | call to permit! | test.rb:28:25:28:35 | call to user_params | provenance | |
2214
| test.rb:43:9:43:37 | call to permit! | test.rb:29:21:29:31 | call to user_params | provenance | |
23-
| test.rb:43:9:43:37 | call to permit! | test.rb:30:22:30:32 | call to user_params | provenance | |
2415
| test.rb:43:9:43:37 | call to permit! | test.rb:31:32:31:42 | call to user_params | provenance | |
2516
| test.rb:43:9:43:37 | call to permit! | test.rb:32:33:32:43 | call to user_params | provenance | |
2617
| test.rb:43:9:43:37 | call to permit! | test.rb:33:36:33:46 | call to user_params | provenance | |
2718
| test.rb:43:9:43:37 | call to permit! | test.rb:34:32:34:42 | call to user_params | provenance | |
2819
| test.rb:43:9:43:37 | call to permit! | test.rb:35:33:35:43 | call to user_params | provenance | |
2920
| test.rb:43:9:43:37 | call to permit! | test.rb:36:26:36:36 | call to user_params | provenance | |
21+
| test.rb:47:9:47:9 | x | test.rb:48:9:48:9 | x | provenance | |
22+
| test.rb:47:13:47:18 | call to params | test.rb:47:13:47:25 | ...[...] | provenance | |
23+
| test.rb:47:13:47:25 | ...[...] | test.rb:47:9:47:9 | x | provenance | |
24+
| test.rb:48:9:48:9 | [post] x | test.rb:49:18:49:18 | x | provenance | |
25+
| test.rb:48:9:48:9 | x | test.rb:48:9:48:9 | [post] x | provenance | |
26+
| test.rb:51:18:51:23 | call to params | test.rb:51:18:51:40 | call to permit | provenance | |
27+
| test.rb:52:18:52:23 | call to params | test.rb:52:18:52:69 | call to permit | provenance | |
28+
| test.rb:53:18:53:23 | call to params | test.rb:53:18:53:35 | call to to_unsafe_h | provenance | |
3029
nodes
3130
| test.rb:8:18:8:28 | call to user_params | semmle.label | call to user_params |
3231
| test.rb:18:20:18:30 | call to user_params | semmle.label | call to user_params |
3332
| test.rb:19:21:19:31 | call to user_params | semmle.label | call to user_params |
3433
| test.rb:20:22:20:32 | call to user_params | semmle.label | call to user_params |
3534
| test.rb:21:21:21:31 | call to user_params | semmle.label | call to user_params |
3635
| test.rb:22:22:22:32 | call to user_params | semmle.label | call to user_params |
37-
| test.rb:23:25:23:37 | call to [] | semmle.label | call to [] |
38-
| test.rb:23:25:23:37 | call to [] [element 0] | semmle.label | call to [] [element 0] |
39-
| test.rb:23:26:23:36 | call to user_params | semmle.label | call to user_params |
40-
| test.rb:24:26:24:38 | call to [] | semmle.label | call to [] |
41-
| test.rb:24:26:24:38 | call to [] [element 0] | semmle.label | call to [] [element 0] |
42-
| test.rb:24:27:24:37 | call to user_params | semmle.label | call to user_params |
4336
| test.rb:25:21:25:31 | call to user_params | semmle.label | call to user_params |
4437
| test.rb:26:24:26:34 | call to user_params | semmle.label | call to user_params |
4538
| test.rb:27:22:27:32 | call to user_params | semmle.label | call to user_params |
4639
| test.rb:28:25:28:35 | call to user_params | semmle.label | call to user_params |
4740
| test.rb:29:21:29:31 | call to user_params | semmle.label | call to user_params |
48-
| test.rb:30:21:30:33 | call to [] | semmle.label | call to [] |
49-
| test.rb:30:21:30:33 | call to [] [element 0] | semmle.label | call to [] [element 0] |
50-
| test.rb:30:22:30:32 | call to user_params | semmle.label | call to user_params |
5141
| test.rb:31:32:31:42 | call to user_params | semmle.label | call to user_params |
5242
| test.rb:32:33:32:43 | call to user_params | semmle.label | call to user_params |
5343
| test.rb:33:36:33:46 | call to user_params | semmle.label | call to user_params |
@@ -57,6 +47,18 @@ nodes
5747
| test.rb:43:9:43:14 | call to params | semmle.label | call to params |
5848
| test.rb:43:9:43:29 | call to require | semmle.label | call to require |
5949
| test.rb:43:9:43:37 | call to permit! | semmle.label | call to permit! |
50+
| test.rb:47:9:47:9 | x | semmle.label | x |
51+
| test.rb:47:13:47:18 | call to params | semmle.label | call to params |
52+
| test.rb:47:13:47:25 | ...[...] | semmle.label | ...[...] |
53+
| test.rb:48:9:48:9 | [post] x | semmle.label | [post] x |
54+
| test.rb:48:9:48:9 | x | semmle.label | x |
55+
| test.rb:49:18:49:18 | x | semmle.label | x |
56+
| test.rb:51:18:51:23 | call to params | semmle.label | call to params |
57+
| test.rb:51:18:51:40 | call to permit | semmle.label | call to permit |
58+
| test.rb:52:18:52:23 | call to params | semmle.label | call to params |
59+
| test.rb:52:18:52:69 | call to permit | semmle.label | call to permit |
60+
| test.rb:53:18:53:23 | call to params | semmle.label | call to params |
61+
| test.rb:53:18:53:35 | call to to_unsafe_h | semmle.label | call to to_unsafe_h |
6062
subpaths
6163
#select
6264
| test.rb:8:18:8:28 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:8:18:8:28 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
@@ -65,17 +67,18 @@ subpaths
6567
| test.rb:20:22:20:32 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:20:22:20:32 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
6668
| test.rb:21:21:21:31 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:21:21:21:31 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
6769
| test.rb:22:22:22:32 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:22:22:22:32 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
68-
| test.rb:23:25:23:37 | call to [] | test.rb:43:9:43:14 | call to params | test.rb:23:25:23:37 | call to [] | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
69-
| test.rb:24:26:24:38 | call to [] | test.rb:43:9:43:14 | call to params | test.rb:24:26:24:38 | call to [] | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
7070
| test.rb:25:21:25:31 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:25:21:25:31 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
7171
| test.rb:26:24:26:34 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:26:24:26:34 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
7272
| test.rb:27:22:27:32 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:27:22:27:32 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
7373
| test.rb:28:25:28:35 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:28:25:28:35 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
7474
| test.rb:29:21:29:31 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:29:21:29:31 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
75-
| test.rb:30:21:30:33 | call to [] | test.rb:43:9:43:14 | call to params | test.rb:30:21:30:33 | call to [] | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
7675
| test.rb:31:32:31:42 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:31:32:31:42 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
7776
| test.rb:32:33:32:43 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:32:33:32:43 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
7877
| test.rb:33:36:33:46 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:33:36:33:46 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
7978
| test.rb:34:32:34:42 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:34:32:34:42 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
8079
| test.rb:35:33:35:43 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:35:33:35:43 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
8180
| test.rb:36:26:36:36 | call to user_params | test.rb:43:9:43:14 | call to params | test.rb:36:26:36:36 | call to user_params | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:43:9:43:14 | call to params | this remote flow source |
81+
| test.rb:49:18:49:18 | x | test.rb:47:13:47:18 | call to params | test.rb:49:18:49:18 | x | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:47:13:47:18 | call to params | this remote flow source |
82+
| test.rb:51:18:51:40 | call to permit | test.rb:51:18:51:23 | call to params | test.rb:51:18:51:40 | call to permit | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:51:18:51:23 | call to params | this remote flow source |
83+
| test.rb:52:18:52:69 | call to permit | test.rb:52:18:52:23 | call to params | test.rb:52:18:52:69 | call to permit | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:52:18:52:23 | call to params | this remote flow source |
84+
| test.rb:53:18:53:35 | call to to_unsafe_h | test.rb:53:18:53:23 | call to params | test.rb:53:18:53:35 | call to to_unsafe_h | This mass assignment operation can assign user-controlled attributes from $@. | test.rb:53:18:53:23 | call to params | this remote flow source |

ruby/ql/test/query-tests/security/cwe-915/test.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,15 @@ def create3
4242
def user_params
4343
params.require(:user).permit!
4444
end
45+
46+
def create4
47+
x = params[:user]
48+
x.permit!
49+
User.new(x) # BAD
50+
User.new(x.permit(:name,:address)) # GOOD
51+
User.new(params.permit(user: {})) # BAD
52+
User.new(params.permit(user: [:name, :address, {friends:{}}])) # BAD
53+
User.new(params.to_unsafe_h) # BAD
54+
User.new(params.permit(user: [:name, :address]).to_unsafe_h) # GOOD
55+
end
4556
end

0 commit comments

Comments
 (0)