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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e45880e05081ae8b62a4b1e1fb9778a5580b9b17fe839973f1046242222881e1
4
- data.tar.gz: 4212f8e0f60a70d61db53a2f9af447248efc686d593af0145c715a5a831db683
3
+ metadata.gz: 96a0df612891f48475152a6781a39050feef3610798324133f10c4b24a0824aa
4
+ data.tar.gz: 44f3e440dfd7630c9c32fb46d081389e6176f282bda8734e5507647e4d452ae9
5
5
  SHA512:
6
- metadata.gz: be0a7163ee430bcc93333c227cdbe56180864dbac0f128e0e70fc526a2a82b202d36f8e54fad30d8bdf33c66fc668c9ad207ddc29d1f6eaeea97b35f4a676070
7
- data.tar.gz: a4aefcc523eb8c4c63ce31d851e4a47461288a6ee152e9a53572258135778098e55e7b470d1058f2dbd283d4d9c45abcd714717327db5ee1d6114d4f0bfd8d91
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 mass assignment style vulnerabilities. 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:
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 a mass assignment vulnerability. 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).
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/AlignParameters:
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/IndentArray:
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
- NamePrefixBlacklist:
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
- @wrapper_methods = {}
40
- @wrapper_names = []
39
+ @param_wrappers = []
41
40
  end
42
41
 
43
42
  def on_class(node)
44
- track_methods(node)
45
- track_assignments(node)
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
- arg_nodes.each do |argument|
54
- if argument.send_type?
55
- tag_unsafe_param_hash(argument)
56
- tag_unsafe_param_permit_wrapper(argument)
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
- tag_unsafe_param_hash(value)
65
- tag_unsafe_param_permit_wrapper(value)
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
- def tag_unsafe_param_hash(node)
74
- unsafe_param = extract_parameter(node)
75
- add_offense(unsafe_param, message: MSG_UNSAFE_CREATE) if unsafe_param
76
- end
77
-
78
- def tag_unsafe_param_permit_wrapper(node)
79
- return if !node.send_type? && !node.variable?
80
- return if node.send_type? && (node.method_name == :[])
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
- add_offense(node, message: MSG_UNSAFE_CREATE) if @wrapper_names.include?(name)
84
- end
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
- # keep track of all assignments that hold parameters
103
- def track_assignments(node)
104
- get_all_assignments(node).each do |assignment|
105
- variable_name, value = *assignment
106
-
107
- # if rhs calls params.permit, eg
108
- # - @var = params.permit(...)
109
- rhs_wrapper = get_param_wrappers([value])
110
- unless rhs_wrapper.empty?
111
- @wrapper_methods[variable_name] = rhs_wrapper[0]
112
- @wrapper_names << variable_name
113
- end
114
-
115
- # if rhs is a call to a parameter wrapper, eg
116
- # @var = parameter_wrapper
117
- root_token = Utils::Parser.get_root_token(value)
118
- if root_token && @wrapper_names.include?(root_token)
119
- @wrapper_methods[variable_name] = assignment
120
- @wrapper_names << variable_name
121
- end
122
-
123
- # if rhs extracts a parameter, eg
124
- # @var = params[:user_id]
125
- if extract_parameter(value)
126
- @wrapper_methods[variable_name] = assignment
127
- @wrapper_names << variable_name
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
- child.ancestors.each do |ancestor|
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 sym_or_str?(arg)
182
- %i(sym str).include?(arg.type)
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
- def array_or_hash?(arg)
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 contains_id_param?(arg_nodes)
190
- arg_nodes.any? do |arg|
191
- sym_or_str?(arg) && suspicious_id?(arg.value) ||
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 Utils::Parser.get_root_token(value) == :params
62
+ return arg if uses_params?(value)
59
63
  end
60
64
  end
61
- Utils::Parser.get_root_token(arg) == :params
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, Metrics/CyclomaticComplexity
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: 1.19.0
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: 2020-12-18 00:00:00.000000000 Z
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.61.1
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.61.1
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: 1.28.0
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: 1.28.0
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: '0'
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: '0'
167
+ version: 1.3.1
124
168
  requirements: []
125
- rubygems_version: 3.1.4
169
+ rubygems_version: 3.2.3
126
170
  signing_key:
127
171
  specification_version: 4
128
172
  summary: Betterment rubocop configuration