rubocop-betterment 1.19.0 → 2.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +38 -3
- data/config/default.yml +16 -6
- data/lib/rubocop/cop/betterment.rb +2 -0
- data/lib/rubocop/cop/betterment/authorization_in_controller.rb +72 -140
- data/lib/rubocop/cop/betterment/dynamic_params.rb +1 -1
- data/lib/rubocop/cop/betterment/unsafe_job.rb +33 -0
- data/lib/rubocop/cop/betterment/unscoped_find.rb +15 -3
- data/lib/rubocop/cop/betterment/utils/method_return_table.rb +48 -0
- data/lib/rubocop/cop/betterment/utils/parser.rb +45 -3
- metadata +58 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96a0df612891f48475152a6781a39050feef3610798324133f10c4b24a0824aa
|
4
|
+
data.tar.gz: 44f3e440dfd7630c9c32fb46d081389e6176f282bda8734e5507647e4d452ae9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 268ce6ed38e7a5f13b2f2963d20c468cfbb0e244e7b88c216dce45030fae9959bdde561b85eef4f65601c64957b3e4cc8ee1f6baae4a468b7da35a1f5db7f5ef
|
7
|
+
data.tar.gz: f375ef9abdb496a97a9affaf9a0b34f637d773ef13094740e7a0999de4b3923f7b967bdc0993c10ee848a479f59bb418c1ede958ace15974e3fe33094a718a15
|
data/README.md
CHANGED
@@ -33,7 +33,7 @@ All cops are located under [`lib/rubocop/cop/betterment`](lib/rubocop/cop/better
|
|
33
33
|
|
34
34
|
### Betterment/AuthorizationInController
|
35
35
|
|
36
|
-
This cop looks for unsafe handling of id-like parameters in controllers that may lead to
|
36
|
+
This cop looks for unsafe handling of id-like parameters in controllers that may lead to [insecure direct object reference vulnerabilities](https://portswigger.net/web-security/access-control/idor). It does this by tracking methods that retrieve input from the client and variables that hold onto these values. Any models initialized or updated using these values will then be flagged by the cop. Take this example controller:
|
37
37
|
|
38
38
|
```ruby
|
39
39
|
class Controller
|
@@ -50,7 +50,7 @@ class Controller
|
|
50
50
|
end
|
51
51
|
```
|
52
52
|
|
53
|
-
All three `Model.new` calls may be susceptible to
|
53
|
+
All three `Model.new` calls may be susceptible to an insecure direct object reference vulnerability. This may end up letting attackers read and write content belonging to other users. To address these vulnerabilities, some form of authorization will be needed to ensure that the user issuing this request is allowed to create a `Model` that references the specific `user_id`. To get a better understanding of what this cop flags and doesn't flag, take a look at its [spec](spec/rubocop/cop/betterment/authorization_in_controller_spec.rb).
|
54
54
|
|
55
55
|
In cases where more fine-grained control over what parameters are considered sensitive is desired, two configuration options can be used: `unsafe_parameters` and `unsafe_regex`. By default this cop will flag unsafe uses of any parameters whose names end in `_id`, but additional parameters can be specified by configuring `unsafe_parameters`. In cases where the default pattern of `.*_id` is insufficient or incorrect, this regex can be swapped out by specifying the `unsafe_regex` configuration option. In total, this cop will flag any parameters whose names are on the `unsafe_parameters` list or matches the `unsafe_regex` pattern.
|
56
56
|
|
@@ -69,7 +69,7 @@ Betterment/AuthorizationInController:
|
|
69
69
|
|
70
70
|
### Betterment/UnscopedFind
|
71
71
|
|
72
|
-
This cop flags code that passes user input directly into a `find`-like call that may lead to authorization issues. For example, a controller that uses user input to find a document will need to ensure that the user is authorized to access that document. Take the following sample:
|
72
|
+
This cop flags code that passes user input directly into a `find`-like call that may lead to authorization issues (such as [indirect object reference vulnerabilities](https://portswigger.net/web-security/access-control/idor)). For example, a controller that uses user input to find a document will need to ensure that the user is authorized to access that document. Take the following sample:
|
73
73
|
|
74
74
|
```ruby
|
75
75
|
class Controller
|
@@ -121,3 +121,38 @@ end
|
|
121
121
|
```
|
122
122
|
|
123
123
|
All three `params.permit` calls will be flagged.
|
124
|
+
|
125
|
+
### Betterment/UnsafeJob
|
126
|
+
|
127
|
+
This cop flags delayed jobs (e.g. ActiveJob, delayed_job) whose classes accept sensitive data via a `perform` or `initialize` method. Jobs are serialized in plaintext, so any sensitive data they accept will be accessible in plaintext to everyone with database access. Instead, consider passing ActiveRecord instances that appropriately handle sensitive data (e.g. encrypted at rest and decrypted when the data is needed) or avoid passing in this data entirely.
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
class RegistrationJob < ApplicationJob
|
131
|
+
def perform(user:, password:, authorization_token:)
|
132
|
+
# do something to the user with the password and authorization_token
|
133
|
+
end
|
134
|
+
end
|
135
|
+
```
|
136
|
+
|
137
|
+
When a `RegistrationJob` gets queued, this job will get serialized, leaving both `password` and `authorization_token` accessible in plaintext. `Betterment/UnsafeJob` can be configured to flag parameters like these to discourage their use. Some ways to remediate this might be to stop passing in `password`, and to encrypt `authorization_token` and storing it alongside the user object. For example:
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
class RegistrationJob < ApplicationJob
|
141
|
+
def perform(user:)
|
142
|
+
authorization_token = user.authorization_token.decrypt
|
143
|
+
# do something with the authorization_token
|
144
|
+
end
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
148
|
+
By default, this job will look at classes whose name ends with `Job` but this can be replaced with any regex. This cop can also be configured to take an arbitrary list of parameter names so that any Job found accepting these parameters will be flagged.
|
149
|
+
|
150
|
+
```yaml
|
151
|
+
Betterment/UnsafeJob:
|
152
|
+
class_regex: .*Job$
|
153
|
+
sensitive_params:
|
154
|
+
- password
|
155
|
+
- authorization_token
|
156
|
+
```
|
157
|
+
|
158
|
+
It may make sense to consult your application's values for `Rails.application.config.filter_parameters`; if the application is filtering specific parameters from being logged, it might be a good idea to prevent these values from being stored in plaintext in a database as well.
|
data/config/default.yml
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
require:
|
4
4
|
- rubocop/cop/betterment.rb
|
5
|
+
- rubocop-performance
|
6
|
+
- rubocop-rails
|
7
|
+
- rubocop-rake
|
5
8
|
- rubocop-rspec
|
6
9
|
|
7
10
|
AllCops:
|
@@ -26,6 +29,13 @@ Betterment/AuthorizationInController:
|
|
26
29
|
Description: 'Detects unsafe handling of id-like parameters in controllers.'
|
27
30
|
Enabled: false
|
28
31
|
|
32
|
+
Betterment/UnsafeJob:
|
33
|
+
Enabled: false
|
34
|
+
sensitive_params:
|
35
|
+
- password
|
36
|
+
- social_security_number
|
37
|
+
- ssn
|
38
|
+
|
29
39
|
Betterment/SitePrismLoaded:
|
30
40
|
Include:
|
31
41
|
- 'spec/features/**/*_spec.rb'
|
@@ -34,7 +44,7 @@ Betterment/SitePrismLoaded:
|
|
34
44
|
Capybara/FeatureMethods:
|
35
45
|
Enabled: false
|
36
46
|
|
37
|
-
Layout/
|
47
|
+
Layout/ParameterAlignment:
|
38
48
|
Enabled: false
|
39
49
|
|
40
50
|
Layout/CaseIndentation:
|
@@ -43,7 +53,7 @@ Layout/CaseIndentation:
|
|
43
53
|
Layout/ClosingParenthesisIndentation:
|
44
54
|
Enabled: false
|
45
55
|
|
46
|
-
Layout/
|
56
|
+
Layout/FirstArrayElementIndentation:
|
47
57
|
EnforcedStyle: consistent
|
48
58
|
|
49
59
|
Layout/MultilineMethodCallIndentation:
|
@@ -117,7 +127,7 @@ Naming/HeredocDelimiterNaming:
|
|
117
127
|
Naming/PredicateName:
|
118
128
|
NamePrefix:
|
119
129
|
- is_
|
120
|
-
|
130
|
+
ForbiddenPrefixes:
|
121
131
|
- is_
|
122
132
|
|
123
133
|
Naming/VariableNumber:
|
@@ -150,9 +160,6 @@ RSpec/ContextWording:
|
|
150
160
|
RSpec/DescribeClass:
|
151
161
|
Enabled: false
|
152
162
|
|
153
|
-
RSpec/DescribedClass:
|
154
|
-
Enabled: false
|
155
|
-
|
156
163
|
RSpec/DescribedClass:
|
157
164
|
EnforcedStyle: 'described_class'
|
158
165
|
|
@@ -201,6 +208,9 @@ RSpec/MessageSpies:
|
|
201
208
|
RSpec/MultipleExpectations:
|
202
209
|
Enabled: false
|
203
210
|
|
211
|
+
RSpec/MultipleMemoizedHelpers:
|
212
|
+
Enabled: false
|
213
|
+
|
204
214
|
RSpec/NamedSubject:
|
205
215
|
Enabled: false
|
206
216
|
|
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'rubocop'
|
2
2
|
require 'rubocop/cop/betterment/utils/parser'
|
3
|
+
require 'rubocop/cop/betterment/utils/method_return_table'
|
3
4
|
require 'rubocop/cop/betterment/authorization_in_controller'
|
4
5
|
require 'rubocop/cop/betterment/dynamic_params'
|
5
6
|
require 'rubocop/cop/betterment/unscoped_find'
|
7
|
+
require 'rubocop/cop/betterment/unsafe_job'
|
6
8
|
require 'rubocop/cop/betterment/timeout'
|
7
9
|
require 'rubocop/cop/betterment/memoization_with_arguments'
|
8
10
|
require 'rubocop/cop/betterment/site_prism_loaded'
|
@@ -36,33 +36,31 @@ module RuboCop
|
|
36
36
|
config = @config.for_cop(self)
|
37
37
|
@unsafe_parameters = config.fetch("unsafe_parameters", []).map(&:to_sym)
|
38
38
|
@unsafe_regex = Regexp.new config.fetch("unsafe_regex", ".*_id$")
|
39
|
-
@
|
40
|
-
@wrapper_names = []
|
39
|
+
@param_wrappers = []
|
41
40
|
end
|
42
41
|
|
43
42
|
def on_class(node)
|
44
|
-
|
45
|
-
|
43
|
+
Utils::MethodReturnTable.populate_index node
|
44
|
+
Utils::MethodReturnTable.indexed_methods.each do |method_name, method_returns|
|
45
|
+
method_returns.each do |x|
|
46
|
+
name = Utils::Parser.get_root_token(x)
|
47
|
+
@param_wrappers << method_name if name == :params || @param_wrappers.include?(name)
|
48
|
+
end
|
49
|
+
end
|
46
50
|
end
|
47
51
|
|
48
|
-
def on_send(node) # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity
|
49
|
-
_receiver_node, _method_name, *arg_nodes = *node
|
50
|
-
|
52
|
+
def on_send(node) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
51
53
|
return if !model_new?(node) && !model_update?(node)
|
52
54
|
|
53
|
-
|
54
|
-
if argument.send_type?
|
55
|
-
|
56
|
-
|
57
|
-
elsif argument.variable?
|
58
|
-
tag_unsafe_param_permit_wrapper(argument)
|
55
|
+
node.arguments.each do |argument|
|
56
|
+
if argument.send_type? || argument.variable?
|
57
|
+
flag_literal_param_use(argument)
|
58
|
+
flag_indirect_param_use(argument)
|
59
59
|
elsif argument.hash_type?
|
60
|
-
argument.children.each do |pair|
|
61
|
-
next if pair.type != :pair
|
62
|
-
|
60
|
+
argument.children.select(&:pair_type?).each do |pair|
|
63
61
|
_key, value = *pair.children
|
64
|
-
|
65
|
-
|
62
|
+
flag_literal_param_use(value)
|
63
|
+
flag_indirect_param_use(value)
|
66
64
|
end
|
67
65
|
end
|
68
66
|
end
|
@@ -70,126 +68,73 @@ module RuboCop
|
|
70
68
|
|
71
69
|
private
|
72
70
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
71
|
+
# Flags objects being created/updated with unsafe
|
72
|
+
# params directly from params or through params.permit
|
73
|
+
#
|
74
|
+
# class MyController < ApplicationController
|
75
|
+
# def create
|
76
|
+
# Object.create params.permit(:user_id)
|
77
|
+
# Object.create(user_id: params[:user_id])
|
78
|
+
# end
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
def flag_literal_param_use(node)
|
82
82
|
name = Utils::Parser.get_root_token(node)
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
# if a method returns params[...] or params.permit(...) then it will
|
87
|
-
# be tracked; if the method does not explicitly return a params
|
88
|
-
# sourced argument, then it will not be tracked
|
89
|
-
def track_methods(node)
|
90
|
-
methods = get_all_methods(node)
|
91
|
-
|
92
|
-
methods.map do |method|
|
93
|
-
method_returns = Utils::Parser.get_return_values(method)
|
94
|
-
|
95
|
-
unless get_param_wrappers(method_returns).empty?
|
96
|
-
@wrapper_methods[method.method_name] = method
|
97
|
-
@wrapper_names << method.method_name
|
98
|
-
end
|
99
|
-
end
|
83
|
+
extracted_params = Utils::Parser.get_extracted_parameters(node)
|
84
|
+
add_offense(node, message: MSG_UNSAFE_CREATE) if name == :params && contains_id_parameter?(extracted_params)
|
100
85
|
end
|
101
86
|
|
102
|
-
#
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
#
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
87
|
+
# Flags objects being created/updated with unsafe
|
88
|
+
# params indirectly from params or through params.permit
|
89
|
+
def flag_indirect_param_use(node) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
90
|
+
name = Utils::Parser.get_root_token(node)
|
91
|
+
# extracted_params contains parameters used like:
|
92
|
+
# def create
|
93
|
+
# Object.new(user_id: indirect_params[:user_id])
|
94
|
+
# end
|
95
|
+
# def indirect_params
|
96
|
+
# params.permit(:user_id)
|
97
|
+
# end
|
98
|
+
extracted_params = Utils::Parser.get_extracted_parameters(node, param_aliases: @param_wrappers)
|
99
|
+
|
100
|
+
returns = Utils::MethodReturnTable.get_method(name) || []
|
101
|
+
returns.each do |ret|
|
102
|
+
# # propagated_params contains parameters used like:
|
103
|
+
# def create
|
104
|
+
# Object.new indirect_params
|
105
|
+
# end
|
106
|
+
# def indirect_params
|
107
|
+
# params.permit(:user_id)
|
108
|
+
# end
|
109
|
+
propagated_params = Utils::Parser.get_extracted_parameters(ret, param_aliases: @param_wrappers)
|
110
|
+
|
111
|
+
# # internal_params contains parameters used like:
|
112
|
+
# def create
|
113
|
+
# Object.new(user_id: indirect_params)
|
114
|
+
# end
|
115
|
+
# def indirect_params
|
116
|
+
# params[:user_id]
|
117
|
+
# end
|
118
|
+
if ret.method?(:[])
|
119
|
+
internal_params = ret.arguments.map(&:value)
|
120
|
+
else
|
121
|
+
internal_returns = Utils::MethodReturnTable.get_method(Utils::Parser.get_root_token(ret)) || []
|
122
|
+
internal_params = internal_returns.flat_map { |x| Utils::Parser.get_extracted_parameters(x, param_aliases: @param_wrappers) }
|
128
123
|
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def get_all_assignments(node)
|
133
|
-
return [] unless node.children && node.class_type?
|
134
|
-
|
135
|
-
node.descendants.select do |descendant|
|
136
|
-
_lhs, rhs = *descendant
|
137
|
-
descendant.equals_asgn? && (descendant.type != :casgn) && rhs&.send_type?
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def param_symbol?(name)
|
142
|
-
name == :params
|
143
|
-
end
|
144
|
-
|
145
|
-
def get_all_methods(node)
|
146
|
-
return [] unless node.children && node.class_type?
|
147
|
-
|
148
|
-
node.descendants.select do |descendant|
|
149
|
-
descendant.def_type?
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
# this finds all calls to any method on a params like object
|
154
|
-
# then walks up to find calls to permit
|
155
|
-
# if the arguments to permit are "suspicious", then we add
|
156
|
-
# the whole method to a list of methods that wrap params.permit
|
157
|
-
def get_param_wrappers(methods) # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity
|
158
|
-
wrappers = []
|
159
|
-
|
160
|
-
methods.each do |method|
|
161
|
-
return [] unless method.def_type? || method.send_type?
|
162
|
-
|
163
|
-
method.descendants.each do |child|
|
164
|
-
next unless child.send_type?
|
165
|
-
next unless param_symbol?(child.method_name)
|
166
124
|
|
167
|
-
|
168
|
-
_receiver_node, method_name, *arg_nodes = *ancestor
|
169
|
-
next unless ancestor.send_type?
|
170
|
-
next unless method_name == :permit
|
171
|
-
|
172
|
-
# we're only interested in calls to params.....permit(...)
|
173
|
-
wrappers << method if contains_id_param?(arg_nodes)
|
174
|
-
end
|
175
|
-
end
|
125
|
+
add_offense(node, message: MSG_UNSAFE_CREATE) if flag_indirect_param_use?(extracted_params, internal_params, propagated_params)
|
176
126
|
end
|
177
|
-
|
178
|
-
wrappers
|
179
127
|
end
|
180
128
|
|
181
|
-
def
|
182
|
-
|
183
|
-
end
|
129
|
+
def flag_indirect_param_use?(extracted_params, internal_params, propagated_params)
|
130
|
+
return contains_id_parameter?(extracted_params) if extracted_params.any?
|
184
131
|
|
185
|
-
|
186
|
-
%i(array hash).include?(arg.type)
|
132
|
+
contains_id_parameter?(extracted_params) || contains_id_parameter?(internal_params) || contains_id_parameter?(propagated_params)
|
187
133
|
end
|
188
134
|
|
189
|
-
def
|
190
|
-
|
191
|
-
|
192
|
-
array_or_hash?(arg) && contains_id_param?(arg.values)
|
135
|
+
def contains_id_parameter?(params)
|
136
|
+
params.any? do |arg|
|
137
|
+
suspicious_id?(arg)
|
193
138
|
end
|
194
139
|
end
|
195
140
|
|
@@ -197,19 +142,6 @@ module RuboCop
|
|
197
142
|
def suspicious_id?(symbol_name)
|
198
143
|
@unsafe_parameters.include?(symbol_name.to_sym) || @unsafe_regex.match(symbol_name) # symbol_name.to_s.end_with?("_id")
|
199
144
|
end
|
200
|
-
|
201
|
-
def extract_parameter(argument)
|
202
|
-
_receiver_node, method_name, *arg_nodes = *argument
|
203
|
-
return unless argument.send_type? && method_name == :[]
|
204
|
-
|
205
|
-
argument_name = Utils::Parser.get_root_token(argument)
|
206
|
-
|
207
|
-
if param_symbol?(argument_name) || @wrapper_names.include?(argument_name)
|
208
|
-
arg_nodes.find do |arg|
|
209
|
-
sym_or_str?(arg) && suspicious_id?(arg.value)
|
210
|
-
end
|
211
|
-
end
|
212
|
-
end
|
213
145
|
end
|
214
146
|
end
|
215
147
|
end
|
@@ -14,7 +14,7 @@ module RuboCop
|
|
14
14
|
PATTERN
|
15
15
|
|
16
16
|
def on_send(node)
|
17
|
-
_, _, *arg_nodes = *node
|
17
|
+
_, _, *arg_nodes = *node # rubocop:disable InternalAffairs/NodeDestructuring
|
18
18
|
return unless permit_or_hash?(node) && Utils::Parser.get_root_token(node) == :params
|
19
19
|
|
20
20
|
dynamic_param = find_dynamic_param(arg_nodes)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Cop
|
3
|
+
module Betterment
|
4
|
+
class UnsafeJob < Cop
|
5
|
+
attr_accessor :sensitive_params, :class_regex
|
6
|
+
|
7
|
+
MSG = <<~MSG.freeze
|
8
|
+
This job takes a parameter that will end up serialized in plaintext. Do not pass sensitive data as bare arguments into jobs.
|
9
|
+
|
10
|
+
See here for more information on this error:
|
11
|
+
https://github.com/Betterment/rubocop-betterment#bettermentunsafejob
|
12
|
+
MSG
|
13
|
+
|
14
|
+
def initialize(config = nil, options = nil)
|
15
|
+
super(config, options)
|
16
|
+
config = @config.for_cop(self)
|
17
|
+
@sensitive_params = config.fetch("sensitive_params", []).map(&:to_sym)
|
18
|
+
@class_regex = Regexp.new config.fetch("class_regex", ".*Job$")
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_def(node)
|
22
|
+
return unless %i(perform initialize).include?(node.method_name)
|
23
|
+
return unless @class_regex.match(node.parent_module_name)
|
24
|
+
|
25
|
+
node.arguments.any? do |argument|
|
26
|
+
name, = *argument
|
27
|
+
add_offense(argument) if @sensitive_params.include?(name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -34,8 +34,12 @@ module RuboCop
|
|
34
34
|
@unauthenticated_models = config.fetch("unauthenticated_models", []).map(&:to_sym)
|
35
35
|
end
|
36
36
|
|
37
|
+
def on_class(node)
|
38
|
+
Utils::MethodReturnTable.populate_index(node)
|
39
|
+
end
|
40
|
+
|
37
41
|
def on_send(node)
|
38
|
-
_, _, *arg_nodes = *node
|
42
|
+
_, _, *arg_nodes = *node # rubocop:disable InternalAffairs/NodeDestructuring
|
39
43
|
return unless
|
40
44
|
(
|
41
45
|
find?(node) ||
|
@@ -55,10 +59,18 @@ module RuboCop
|
|
55
59
|
if arg.hash_type?
|
56
60
|
arg.children.each do |pair|
|
57
61
|
_key, value = *pair.children
|
58
|
-
return arg if
|
62
|
+
return arg if uses_params?(value)
|
59
63
|
end
|
60
64
|
end
|
61
|
-
|
65
|
+
|
66
|
+
uses_params?(arg)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def uses_params?(node)
|
71
|
+
root = Utils::Parser.get_root_token(node)
|
72
|
+
root == :params || Array(Utils::MethodReturnTable.get_method(root)).find do |x|
|
73
|
+
Utils::Parser.get_root_token(x) == :params
|
62
74
|
end
|
63
75
|
end
|
64
76
|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Cop
|
3
|
+
module Utils
|
4
|
+
module MethodReturnTable
|
5
|
+
class << self
|
6
|
+
def populate_index(node)
|
7
|
+
raise "not a class" unless node.class_type?
|
8
|
+
|
9
|
+
get_methods_for_class(node).each do |method|
|
10
|
+
track_method(method.method_name, Utils::Parser.get_return_values(method))
|
11
|
+
end
|
12
|
+
|
13
|
+
node.descendants.each do |descendant|
|
14
|
+
lhs, rhs = *descendant
|
15
|
+
next unless descendant.equals_asgn? && (descendant.type != :casgn) && rhs&.send_type?
|
16
|
+
|
17
|
+
track_method(lhs, [rhs])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def indexed_methods
|
22
|
+
@indexed_methods ||= {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_method(method_name)
|
26
|
+
indexed_methods[method_name]
|
27
|
+
end
|
28
|
+
|
29
|
+
def has_method?(method_name)
|
30
|
+
indexed_methods.include?(method_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def track_method(method_name, returns)
|
36
|
+
indexed_methods[method_name] = returns
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_methods_for_class(node)
|
40
|
+
return [] unless node.children && node.class_type?
|
41
|
+
|
42
|
+
node.descendants.select(&:def_type?)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -2,11 +2,12 @@ module RuboCop
|
|
2
2
|
module Cop
|
3
3
|
module Utils
|
4
4
|
module Parser
|
5
|
-
def self.get_root_token(node) # rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize
|
5
|
+
def self.get_root_token(node) # rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize
|
6
6
|
return nil unless node
|
7
7
|
|
8
8
|
return get_root_token(node.receiver) if node.receiver
|
9
9
|
|
10
|
+
# rubocop:disable InternalAffairs/NodeDestructuring
|
10
11
|
if node.send_type?
|
11
12
|
name = node.method_name
|
12
13
|
elsif node.variable?
|
@@ -17,8 +18,6 @@ module RuboCop
|
|
17
18
|
name = node.const_name.to_sym
|
18
19
|
elsif node.sym_type?
|
19
20
|
name = node.value
|
20
|
-
elsif node.variable?
|
21
|
-
name = node.children[0]
|
22
21
|
elsif node.self_type?
|
23
22
|
name = :self
|
24
23
|
elsif node.block_pass_type?
|
@@ -26,6 +25,7 @@ module RuboCop
|
|
26
25
|
else
|
27
26
|
name = nil
|
28
27
|
end
|
28
|
+
# rubocop:enable InternalAffairs/NodeDestructuring
|
29
29
|
|
30
30
|
name
|
31
31
|
end
|
@@ -63,6 +63,48 @@ module RuboCop
|
|
63
63
|
x&.children&.first
|
64
64
|
}.compact
|
65
65
|
end
|
66
|
+
|
67
|
+
def self.params_from_arguments(arguments) # rubocop:disable Metrics/PerceivedComplexity
|
68
|
+
parameter_names = []
|
69
|
+
|
70
|
+
arguments.each do |arg|
|
71
|
+
if arg.hash_type?
|
72
|
+
arg.children.each do |pair|
|
73
|
+
value = pair.value
|
74
|
+
parameter_names << value.value if value.sym_type? || value.str_type?
|
75
|
+
end
|
76
|
+
elsif arg.sym_type? || arg.str_type?
|
77
|
+
parameter_names << arg.value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
parameter_names
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.get_extracted_parameters(node, param_aliases: []) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
85
|
+
return [] unless node.send_type?
|
86
|
+
|
87
|
+
parameter_names = []
|
88
|
+
param_aliases << :params
|
89
|
+
|
90
|
+
return node.arguments.map(&:value) if node.method?(:[]) && param_aliases.include?(get_root_token(node))
|
91
|
+
|
92
|
+
children = node.descendants.select do |child|
|
93
|
+
child.send_type? && param_aliases.include?(child.method_name)
|
94
|
+
end
|
95
|
+
|
96
|
+
children.each do |child|
|
97
|
+
ancestors = child.ancestors.select do |ancestor|
|
98
|
+
ancestor.send_type? && ancestor.method?(:permit)
|
99
|
+
end
|
100
|
+
|
101
|
+
ancestors.each do |ancestor|
|
102
|
+
parameter_names += params_from_arguments(ancestor.arguments)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
parameter_names.map(&:to_sym)
|
107
|
+
end
|
66
108
|
end
|
67
109
|
end
|
68
110
|
end
|
metadata
CHANGED
@@ -1,43 +1,85 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-betterment
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Development
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubocop
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0
|
19
|
+
version: '1.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubocop-performance
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
25
53
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop-rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
27
69
|
- !ruby/object:Gem::Dependency
|
28
70
|
name: rubocop-rspec
|
29
71
|
requirement: !ruby/object:Gem::Requirement
|
30
72
|
requirements:
|
31
|
-
- -
|
73
|
+
- - ">="
|
32
74
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
75
|
+
version: '0'
|
34
76
|
type: :runtime
|
35
77
|
prerelease: false
|
36
78
|
version_requirements: !ruby/object:Gem::Requirement
|
37
79
|
requirements:
|
38
|
-
- -
|
80
|
+
- - ">="
|
39
81
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
82
|
+
version: '0'
|
41
83
|
- !ruby/object:Gem::Dependency
|
42
84
|
name: bundler
|
43
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -101,7 +143,9 @@ files:
|
|
101
143
|
- lib/rubocop/cop/betterment/site_prism_loaded.rb
|
102
144
|
- lib/rubocop/cop/betterment/spec_helper_required_outside_spec_dir.rb
|
103
145
|
- lib/rubocop/cop/betterment/timeout.rb
|
146
|
+
- lib/rubocop/cop/betterment/unsafe_job.rb
|
104
147
|
- lib/rubocop/cop/betterment/unscoped_find.rb
|
148
|
+
- lib/rubocop/cop/betterment/utils/method_return_table.rb
|
105
149
|
- lib/rubocop/cop/betterment/utils/parser.rb
|
106
150
|
homepage:
|
107
151
|
licenses:
|
@@ -115,14 +159,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
115
159
|
requirements:
|
116
160
|
- - ">="
|
117
161
|
- !ruby/object:Gem::Version
|
118
|
-
version: '
|
162
|
+
version: '2.4'
|
119
163
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
164
|
requirements:
|
121
|
-
- - "
|
165
|
+
- - ">"
|
122
166
|
- !ruby/object:Gem::Version
|
123
|
-
version:
|
167
|
+
version: 1.3.1
|
124
168
|
requirements: []
|
125
|
-
rubygems_version: 3.
|
169
|
+
rubygems_version: 3.2.3
|
126
170
|
signing_key:
|
127
171
|
specification_version: 4
|
128
172
|
summary: Betterment rubocop configuration
|