rubocop-betterment 1.15.0 → 2.0.0.pre2
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/STYLEGUIDE.md +22 -1
- data/config/default.yml +21 -6
- data/lib/rubocop/cop/betterment.rb +5 -0
- data/lib/rubocop/cop/betterment/allowlist_blocklist.rb +38 -0
- data/lib/rubocop/cop/betterment/authorization_in_controller.rb +76 -208
- data/lib/rubocop/cop/betterment/dynamic_params.rb +3 -29
- data/lib/rubocop/cop/betterment/implicit_redirect_type.rb +2 -2
- data/lib/rubocop/cop/betterment/server_error_assertion.rb +63 -0
- data/lib/rubocop/cop/betterment/spec_helper_required_outside_spec_dir.rb +1 -1
- data/lib/rubocop/cop/betterment/unsafe_job.rb +33 -0
- data/lib/rubocop/cop/betterment/unscoped_find.rb +19 -33
- data/lib/rubocop/cop/betterment/utils/method_return_table.rb +48 -0
- data/lib/rubocop/cop/betterment/utils/parser.rb +115 -0
- metadata +65 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 43b05fbd08387b00251df695ed3c9de3bbd38592195ef2dbea370f637ca2acdd
|
4
|
+
data.tar.gz: 55dfe59749ca808f49b442d5a0634b959c0492db4386eccb64f3a0d8fdfbcc82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cfb9ebd2aa93032848cbf4e27ab8bb3e69c82f2047d788833c76fce9b2f811d184e2d72a6d1186b711de9ae0e3546e6dc0f40870890ccc908ba151ede6c50e9f
|
7
|
+
data.tar.gz: '0294e0f6655280cd462aae9da70e79d830be6879a9b2b5a529d352fba13b9c0d1221ead2c44045bd4a33dfbdc1cea108c3ee5b85d96b314349617249660d8018'
|
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/STYLEGUIDE.md
CHANGED
@@ -87,4 +87,25 @@ user1 = User.first
|
|
87
87
|
user2 = User.second
|
88
88
|
```
|
89
89
|
|
90
|
-
The snake case style is more readable.
|
90
|
+
The snake case style is more readable.
|
91
|
+
|
92
|
+
## Betterment/ServerErrorAssertion
|
93
|
+
|
94
|
+
In RSpec tests, we prevent HTTP response status assertions against server error codes (e.g., 500). While it’s acceptable to
|
95
|
+
“under-build” APIs under assumption of controlled and well-behaving clients, these exceptions should be treated as undefined behavior and
|
96
|
+
thus do not need request spec coverage. In cases where the server must communicate an expected failure to the client, an appropriate
|
97
|
+
semantic status code must be used (e.g., 403, 422, etc.).
|
98
|
+
|
99
|
+
### GOOD:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
expect(response).to have_http_status :forbidden
|
103
|
+
expect(response).to have_http_status 422
|
104
|
+
```
|
105
|
+
|
106
|
+
### BAD:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
expect(response).to have_http_status :internal_server_error
|
110
|
+
expect(response).to have_http_status 500
|
111
|
+
```
|
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:
|
@@ -17,10 +20,22 @@ AllCops:
|
|
17
20
|
DisplayStyleGuide: true
|
18
21
|
DisplayCopNames: true
|
19
22
|
|
23
|
+
Betterment/ServerErrorAssertion:
|
24
|
+
Description: 'Detects assertions on 5XX HTTP statuses.'
|
25
|
+
Include:
|
26
|
+
- 'spec/requests/**/*_spec.rb'
|
27
|
+
|
20
28
|
Betterment/AuthorizationInController:
|
21
29
|
Description: 'Detects unsafe handling of id-like parameters in controllers.'
|
22
30
|
Enabled: false
|
23
31
|
|
32
|
+
Betterment/UnsafeJob:
|
33
|
+
Enabled: false
|
34
|
+
sensitive_params:
|
35
|
+
- password
|
36
|
+
- social_security_number
|
37
|
+
- ssn
|
38
|
+
|
24
39
|
Betterment/SitePrismLoaded:
|
25
40
|
Include:
|
26
41
|
- 'spec/features/**/*_spec.rb'
|
@@ -29,7 +44,7 @@ Betterment/SitePrismLoaded:
|
|
29
44
|
Capybara/FeatureMethods:
|
30
45
|
Enabled: false
|
31
46
|
|
32
|
-
Layout/
|
47
|
+
Layout/ParameterAlignment:
|
33
48
|
Enabled: false
|
34
49
|
|
35
50
|
Layout/CaseIndentation:
|
@@ -38,7 +53,7 @@ Layout/CaseIndentation:
|
|
38
53
|
Layout/ClosingParenthesisIndentation:
|
39
54
|
Enabled: false
|
40
55
|
|
41
|
-
Layout/
|
56
|
+
Layout/FirstArrayElementIndentation:
|
42
57
|
EnforcedStyle: consistent
|
43
58
|
|
44
59
|
Layout/MultilineMethodCallIndentation:
|
@@ -112,7 +127,7 @@ Naming/HeredocDelimiterNaming:
|
|
112
127
|
Naming/PredicateName:
|
113
128
|
NamePrefix:
|
114
129
|
- is_
|
115
|
-
|
130
|
+
ForbiddenPrefixes:
|
116
131
|
- is_
|
117
132
|
|
118
133
|
Naming/VariableNumber:
|
@@ -145,9 +160,6 @@ RSpec/ContextWording:
|
|
145
160
|
RSpec/DescribeClass:
|
146
161
|
Enabled: false
|
147
162
|
|
148
|
-
RSpec/DescribedClass:
|
149
|
-
Enabled: false
|
150
|
-
|
151
163
|
RSpec/DescribedClass:
|
152
164
|
EnforcedStyle: 'described_class'
|
153
165
|
|
@@ -196,6 +208,9 @@ RSpec/MessageSpies:
|
|
196
208
|
RSpec/MultipleExpectations:
|
197
209
|
Enabled: false
|
198
210
|
|
211
|
+
RSpec/MultipleMemoizedHelpers:
|
212
|
+
Enabled: false
|
213
|
+
|
199
214
|
RSpec/NamedSubject:
|
200
215
|
Enabled: false
|
201
216
|
|
@@ -1,10 +1,15 @@
|
|
1
1
|
require 'rubocop'
|
2
|
+
require 'rubocop/cop/betterment/utils/parser'
|
3
|
+
require 'rubocop/cop/betterment/utils/method_return_table'
|
2
4
|
require 'rubocop/cop/betterment/authorization_in_controller'
|
3
5
|
require 'rubocop/cop/betterment/dynamic_params'
|
4
6
|
require 'rubocop/cop/betterment/unscoped_find'
|
7
|
+
require 'rubocop/cop/betterment/unsafe_job'
|
5
8
|
require 'rubocop/cop/betterment/timeout'
|
6
9
|
require 'rubocop/cop/betterment/memoization_with_arguments'
|
7
10
|
require 'rubocop/cop/betterment/site_prism_loaded'
|
8
11
|
require 'rubocop/cop/betterment/spec_helper_required_outside_spec_dir'
|
9
12
|
require 'rubocop/cop/betterment/implicit_redirect_type'
|
10
13
|
require 'rubocop/cop/betterment/active_job_performable'
|
14
|
+
require 'rubocop/cop/betterment/allowlist_blocklist'
|
15
|
+
require 'rubocop/cop/betterment/server_error_assertion'
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# rubocop:disable Betterment/AllowlistBlocklist
|
2
|
+
module RuboCop
|
3
|
+
module Cop
|
4
|
+
module Betterment
|
5
|
+
class AllowlistBlocklist < Cop
|
6
|
+
MSG = <<-DOC.freeze
|
7
|
+
Betterment has moved away from usages of whitelist & blacklist in favor of more inclusive terms.
|
8
|
+
Replace any instances of these terms with more inclusive terms like allowlist, blocklist, denylist,
|
9
|
+
ignorelist, warnlist, safelist, etc. We generally use allowlist and blocklist and prefer consistency where
|
10
|
+
possible, but other terms may be more appropriate depending on your use case.
|
11
|
+
|
12
|
+
This is part of a larger initiative to replace exclusionary / harmful language and anti-bias tools and products.
|
13
|
+
DOC
|
14
|
+
|
15
|
+
def on_class(node)
|
16
|
+
evaluate_node(node)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def evaluate_node(node)
|
22
|
+
return unless should_use_allowlist?(node) || should_use_blocklist?(node)
|
23
|
+
|
24
|
+
add_offense(node)
|
25
|
+
end
|
26
|
+
|
27
|
+
def should_use_allowlist?(node)
|
28
|
+
node.to_s.downcase.include?('whitelist')
|
29
|
+
end
|
30
|
+
|
31
|
+
def should_use_blocklist?(node)
|
32
|
+
node.to_s.downcase.include?('blacklist')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
# rubocop:enable Betterment/AllowlistBlocklist
|
@@ -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.
|
55
|
-
|
56
|
-
|
57
|
-
elsif argument.
|
58
|
-
|
59
|
-
elsif argument.type == :hash
|
60
|
-
argument.children.each do |pair|
|
61
|
-
next if pair.type != :pair
|
62
|
-
|
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
|
+
elsif argument.hash_type?
|
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,190 +68,73 @@ module RuboCop
|
|
70
68
|
|
71
69
|
private
|
72
70
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
end
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
#
|
108
|
-
#
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
#
|
116
|
-
#
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def explicit_returns(node)
|
133
|
-
node.descendants.select(&:return_type?).map { |x|
|
134
|
-
x&.children&.first
|
135
|
-
}.compact
|
136
|
-
end
|
137
|
-
|
138
|
-
def get_return_values(node) # rubocop:disable Metrics/AbcSize
|
139
|
-
return [] unless node
|
140
|
-
return explicit_returns(node) + get_return_values(node.body) if node.def_type?
|
141
|
-
return [node] if node.literal? || node.variable?
|
142
|
-
|
143
|
-
case node.type
|
144
|
-
when :begin
|
145
|
-
get_return_values(node.children.last)
|
146
|
-
when :block
|
147
|
-
get_return_values(node.body)
|
148
|
-
when :if
|
149
|
-
if_rets = get_return_values(node.if_branch)
|
150
|
-
else_rets = get_return_values(node.else_branch)
|
151
|
-
if_rets + else_rets
|
152
|
-
when :case
|
153
|
-
cases = []
|
154
|
-
node.each_when do |block|
|
155
|
-
cases += get_return_values(block.body)
|
156
|
-
end
|
157
|
-
|
158
|
-
cases + get_return_values(node.else_branch)
|
159
|
-
when :send
|
160
|
-
[node]
|
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
|
+
name = Utils::Parser.get_root_token(node)
|
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)
|
85
|
+
end
|
86
|
+
|
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, Metrics/CyclomaticComplexity
|
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.send_type? && ret.method?(:[])
|
119
|
+
internal_params = ret.arguments.select { |x| x.sym_type? || x.str_type? }.map(&:value)
|
161
120
|
else
|
162
|
-
[]
|
163
|
-
|
164
|
-
end
|
165
|
-
|
166
|
-
def get_all_assignments(node)
|
167
|
-
return [] unless node.children && node.type == :class
|
168
|
-
|
169
|
-
node.descendants.select do |descendant|
|
170
|
-
_lhs, rhs = *descendant
|
171
|
-
descendant.equals_asgn? && (descendant.type != :casgn) && rhs&.send_type?
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
def param_symbol?(name)
|
176
|
-
name == :params
|
177
|
-
end
|
178
|
-
|
179
|
-
def get_all_methods(node)
|
180
|
-
return [] unless node.children && node.type == :class
|
181
|
-
|
182
|
-
node.descendants.select do |descendant|
|
183
|
-
descendant.type == :def
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
# fetches the name of the leftmost ("root") token
|
188
|
-
# @vars.merge(this: that).merge(etc: etc) => @vars
|
189
|
-
# params.permit(:this) => params
|
190
|
-
# params[:field] => params
|
191
|
-
def get_root_token(node) # rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize
|
192
|
-
return nil unless node
|
193
|
-
|
194
|
-
return get_root_token(node.receiver) if node.receiver
|
195
|
-
|
196
|
-
if node.send_type?
|
197
|
-
name = node.method_name
|
198
|
-
elsif node.variable?
|
199
|
-
name, = *node
|
200
|
-
elsif node.literal?
|
201
|
-
_, name = *node
|
202
|
-
elsif node.const_type?
|
203
|
-
name = node.const_name
|
204
|
-
elsif node.sym_type?
|
205
|
-
name = node.value
|
206
|
-
elsif node.variable?
|
207
|
-
name = node.children[0]
|
208
|
-
elsif node.parenthesized_call?
|
209
|
-
name = nil
|
210
|
-
else
|
211
|
-
raise "Unknown node type: #{node.type.inspect}"
|
212
|
-
end
|
213
|
-
|
214
|
-
name
|
215
|
-
end
|
216
|
-
|
217
|
-
# this finds all calls to any method on a params like object
|
218
|
-
# then walks up to find calls to permit
|
219
|
-
# if the arguments to permit are "suspicious", then we add
|
220
|
-
# the whole method to a list of methods that wrap params.permit
|
221
|
-
def get_param_wrappers(methods) # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity
|
222
|
-
wrappers = []
|
223
|
-
|
224
|
-
methods.each do |method|
|
225
|
-
return [] unless method.type == :def || method.type == :send
|
226
|
-
|
227
|
-
method.descendants.each do |child|
|
228
|
-
next unless child.type == :send
|
229
|
-
next unless param_symbol?(child.method_name)
|
230
|
-
|
231
|
-
child.ancestors.each do |ancestor|
|
232
|
-
_receiver_node, method_name, *arg_nodes = *ancestor
|
233
|
-
next unless ancestor.send_type?
|
234
|
-
next unless method_name == :permit
|
235
|
-
|
236
|
-
# we're only interested in calls to params.....permit(...)
|
237
|
-
wrappers << method if contains_id_param?(arg_nodes)
|
238
|
-
end
|
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) }
|
239
123
|
end
|
240
|
-
end
|
241
124
|
|
242
|
-
|
125
|
+
add_offense(node, message: MSG_UNSAFE_CREATE) if flag_indirect_param_use?(extracted_params, internal_params, propagated_params)
|
126
|
+
end
|
243
127
|
end
|
244
128
|
|
245
|
-
def
|
246
|
-
|
247
|
-
end
|
129
|
+
def flag_indirect_param_use?(extracted_params, internal_params, propagated_params)
|
130
|
+
return contains_id_parameter?(extracted_params) if extracted_params.any?
|
248
131
|
|
249
|
-
|
250
|
-
%i(array hash).include?(arg.type)
|
132
|
+
contains_id_parameter?(extracted_params) || contains_id_parameter?(internal_params) || contains_id_parameter?(propagated_params)
|
251
133
|
end
|
252
134
|
|
253
|
-
def
|
254
|
-
|
255
|
-
|
256
|
-
array_or_hash?(arg) && contains_id_param?(arg.values)
|
135
|
+
def contains_id_parameter?(params)
|
136
|
+
params.any? do |arg|
|
137
|
+
suspicious_id?(arg)
|
257
138
|
end
|
258
139
|
end
|
259
140
|
|
@@ -261,19 +142,6 @@ module RuboCop
|
|
261
142
|
def suspicious_id?(symbol_name)
|
262
143
|
@unsafe_parameters.include?(symbol_name.to_sym) || @unsafe_regex.match(symbol_name) # symbol_name.to_s.end_with?("_id")
|
263
144
|
end
|
264
|
-
|
265
|
-
def extract_parameter(argument)
|
266
|
-
_receiver_node, method_name, *arg_nodes = *argument
|
267
|
-
return unless argument.send_type? && method_name == :[]
|
268
|
-
|
269
|
-
argument_name = get_root_token(argument)
|
270
|
-
|
271
|
-
if param_symbol?(argument_name) || @wrapper_names.include?(argument_name)
|
272
|
-
arg_nodes.find do |arg|
|
273
|
-
sym_or_str?(arg) && suspicious_id?(arg.value)
|
274
|
-
end
|
275
|
-
end
|
276
|
-
end
|
277
145
|
end
|
278
146
|
end
|
279
147
|
end
|
@@ -14,8 +14,8 @@ module RuboCop
|
|
14
14
|
PATTERN
|
15
15
|
|
16
16
|
def on_send(node)
|
17
|
-
_, _, *arg_nodes = *node
|
18
|
-
return unless permit_or_hash?(node) && get_root_token(node) == :params
|
17
|
+
_, _, *arg_nodes = *node # rubocop:disable InternalAffairs/NodeDestructuring
|
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)
|
21
21
|
add_offense(dynamic_param, message: MSG_DYNAMIC_PARAMS) if dynamic_param
|
@@ -27,35 +27,9 @@ module RuboCop
|
|
27
27
|
return unless arg_nodes
|
28
28
|
|
29
29
|
arg_nodes.find do |arg|
|
30
|
-
arg.
|
30
|
+
arg.array_type? && find_dynamic_param(arg.values) || !arg.literal? && !arg.const_type?
|
31
31
|
end
|
32
32
|
end
|
33
|
-
|
34
|
-
def get_root_token(node) # rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize
|
35
|
-
return nil unless node
|
36
|
-
|
37
|
-
return get_root_token(node.receiver) if node.receiver
|
38
|
-
|
39
|
-
if node.send_type?
|
40
|
-
name = node.method_name
|
41
|
-
elsif node.variable?
|
42
|
-
name, = *node
|
43
|
-
elsif node.literal?
|
44
|
-
_, name = *node
|
45
|
-
elsif node.const_type?
|
46
|
-
name = node.const_name
|
47
|
-
elsif node.sym_type?
|
48
|
-
name = node.value
|
49
|
-
elsif node.variable?
|
50
|
-
name = node.children[0]
|
51
|
-
elsif node.parenthesized_call?
|
52
|
-
name = nil
|
53
|
-
else
|
54
|
-
raise "Unknown node type: #{node.type.inspect}"
|
55
|
-
end
|
56
|
-
|
57
|
-
name
|
58
|
-
end
|
59
33
|
end
|
60
34
|
end
|
61
35
|
end
|
@@ -47,7 +47,7 @@ module RuboCop
|
|
47
47
|
return unless routes_file?
|
48
48
|
|
49
49
|
if block_form_with_options(node) { |options| options.none?(&method(:valid_status_option?)) } || block_form_without_options?(node)
|
50
|
-
add_offense(node
|
50
|
+
add_offense(node)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
@@ -55,7 +55,7 @@ module RuboCop
|
|
55
55
|
return unless routes_file?
|
56
56
|
|
57
57
|
if arg_form_with_options(node) { |options| options.none?(&method(:valid_status_option?)) } || arg_form_without_options?(node)
|
58
|
-
add_offense(node
|
58
|
+
add_offense(node)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Betterment
|
6
|
+
# Checks the status passed to have_http_status
|
7
|
+
#
|
8
|
+
# If a number, enforces that it doesn't start with 5. If a symbol or a string, enforces that it's not one of:
|
9
|
+
#
|
10
|
+
# * internal_server_error
|
11
|
+
# * not_implemented
|
12
|
+
# * bad_gateway
|
13
|
+
# * service_unavailable
|
14
|
+
# * gateway_timeout
|
15
|
+
# * http_version_not_supported
|
16
|
+
# * insufficient_storage
|
17
|
+
# * not_extended
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
#
|
21
|
+
# # bad
|
22
|
+
# expect(response).to have_http_status :internal_server_error
|
23
|
+
#
|
24
|
+
# # bad
|
25
|
+
# expect(response).to have_http_status 500
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# expect(response).to have_http_status :forbidden
|
29
|
+
#
|
30
|
+
# # good
|
31
|
+
# expect(response).to have_http_status 422
|
32
|
+
class ServerErrorAssertion < Cop
|
33
|
+
MSG = 'Do not assert on 5XX statuses. Use a semantic status (e.g., 403, 422, etc.) or treat them as bugs (omit tests).'
|
34
|
+
BAD_STATUSES = %i(
|
35
|
+
internal_server_error
|
36
|
+
not_implemented
|
37
|
+
bad_gateway
|
38
|
+
service_unavailable
|
39
|
+
gateway_timeout
|
40
|
+
http_version_not_supported
|
41
|
+
insufficient_storage
|
42
|
+
not_extended
|
43
|
+
).freeze
|
44
|
+
|
45
|
+
def_node_matcher :offensive_node?, <<-PATTERN
|
46
|
+
(send nil? :have_http_status
|
47
|
+
{
|
48
|
+
(int {#{(500..599).map(&:to_s).join(' ')}})
|
49
|
+
(str {#{BAD_STATUSES.map(&:to_s).map(&:inspect).join(' ')}})
|
50
|
+
(sym {#{BAD_STATUSES.map(&:inspect).join(' ')}})
|
51
|
+
}
|
52
|
+
)
|
53
|
+
PATTERN
|
54
|
+
|
55
|
+
def on_send(node)
|
56
|
+
return unless offensive_node?(node)
|
57
|
+
|
58
|
+
add_offense(node)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -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
|
@@ -31,19 +31,23 @@ module RuboCop
|
|
31
31
|
def initialize(config = nil, options = nil)
|
32
32
|
super(config, options)
|
33
33
|
config = @config.for_cop(self)
|
34
|
-
@unauthenticated_models = config.fetch("unauthenticated_models", [])
|
34
|
+
@unauthenticated_models = config.fetch("unauthenticated_models", []).map(&:to_sym)
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_class(node)
|
38
|
+
Utils::MethodReturnTable.populate_index(node)
|
35
39
|
end
|
36
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) ||
|
42
46
|
custom_scope_find?(node) ||
|
43
47
|
static_method_name(node.method_name)
|
44
|
-
) && !@unauthenticated_models.include?(get_root_token(node))
|
48
|
+
) && !@unauthenticated_models.include?(Utils::Parser.get_root_token(node))
|
45
49
|
|
46
|
-
add_offense(node
|
50
|
+
add_offense(node) if find_param_arg(arg_nodes)
|
47
51
|
end
|
48
52
|
|
49
53
|
private
|
@@ -52,13 +56,21 @@ module RuboCop
|
|
52
56
|
return unless arg_nodes
|
53
57
|
|
54
58
|
arg_nodes.find do |arg|
|
55
|
-
if arg.
|
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
|
|
@@ -69,32 +81,6 @@ module RuboCop
|
|
69
81
|
|
70
82
|
match[2] ? 'find_by!' : 'find_by'
|
71
83
|
end
|
72
|
-
|
73
|
-
def get_root_token(node) # rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize
|
74
|
-
return nil unless node
|
75
|
-
|
76
|
-
return get_root_token(node.receiver) if node.receiver
|
77
|
-
|
78
|
-
if node.send_type?
|
79
|
-
name = node.method_name
|
80
|
-
elsif node.variable?
|
81
|
-
name, = *node
|
82
|
-
elsif node.literal?
|
83
|
-
_, name = *node
|
84
|
-
elsif node.const_type?
|
85
|
-
name = node.const_name
|
86
|
-
elsif node.sym_type?
|
87
|
-
name = node.value
|
88
|
-
elsif node.variable?
|
89
|
-
name = node.children[0]
|
90
|
-
elsif node.parenthesized_call?
|
91
|
-
name = nil
|
92
|
-
else
|
93
|
-
raise "Unknown node type: #{node.type.inspect}"
|
94
|
-
end
|
95
|
-
|
96
|
-
name
|
97
|
-
end
|
98
84
|
end
|
99
85
|
end
|
100
86
|
end
|
@@ -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
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Cop
|
3
|
+
module Utils
|
4
|
+
module Parser
|
5
|
+
def self.get_root_token(node) # rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize
|
6
|
+
return nil unless node
|
7
|
+
|
8
|
+
return get_root_token(node.receiver) if node.receiver
|
9
|
+
|
10
|
+
# rubocop:disable InternalAffairs/NodeDestructuring
|
11
|
+
if node.send_type?
|
12
|
+
name = node.method_name
|
13
|
+
elsif node.variable?
|
14
|
+
name, = *node
|
15
|
+
elsif node.literal?
|
16
|
+
_, name = *node
|
17
|
+
elsif node.const_type?
|
18
|
+
name = node.const_name.to_sym
|
19
|
+
elsif node.sym_type?
|
20
|
+
name = node.value
|
21
|
+
elsif node.self_type?
|
22
|
+
name = :self
|
23
|
+
elsif node.block_pass_type?
|
24
|
+
name, = *node.children
|
25
|
+
else
|
26
|
+
name = nil
|
27
|
+
end
|
28
|
+
# rubocop:enable InternalAffairs/NodeDestructuring
|
29
|
+
|
30
|
+
name
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.get_return_values(node) # rubocop:disable Metrics/AbcSize
|
34
|
+
return [] unless node
|
35
|
+
return explicit_returns(node) + get_return_values(node.body) if node.def_type?
|
36
|
+
return [node] if node.literal? || node.variable?
|
37
|
+
|
38
|
+
case node.type
|
39
|
+
when :begin
|
40
|
+
get_return_values(node.children.last)
|
41
|
+
when :block
|
42
|
+
get_return_values(node.body)
|
43
|
+
when :if
|
44
|
+
if_rets = get_return_values(node.if_branch)
|
45
|
+
else_rets = get_return_values(node.else_branch)
|
46
|
+
if_rets + else_rets
|
47
|
+
when :case
|
48
|
+
cases = []
|
49
|
+
node.each_when do |block|
|
50
|
+
cases += get_return_values(block.body)
|
51
|
+
end
|
52
|
+
|
53
|
+
cases + get_return_values(node.else_branch)
|
54
|
+
when :send
|
55
|
+
[node]
|
56
|
+
else
|
57
|
+
[]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.explicit_returns(node)
|
62
|
+
node.descendants.select(&:return_type?).map { |x|
|
63
|
+
x&.children&.first
|
64
|
+
}.compact
|
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
|
+
if node.method?(:[]) && param_aliases.include?(get_root_token(node))
|
91
|
+
return node.arguments.select { |x|
|
92
|
+
x.sym_type? || x.str_type?
|
93
|
+
}.map(&:value)
|
94
|
+
end
|
95
|
+
|
96
|
+
children = node.descendants.select do |child|
|
97
|
+
child.send_type? && param_aliases.include?(child.method_name)
|
98
|
+
end
|
99
|
+
|
100
|
+
children.each do |child|
|
101
|
+
ancestors = child.ancestors.select do |ancestor|
|
102
|
+
ancestor.send_type? && ancestor.method?(:permit)
|
103
|
+
end
|
104
|
+
|
105
|
+
ancestors.each do |ancestor|
|
106
|
+
parameter_names += params_from_arguments(ancestor.arguments)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
parameter_names.map(&:to_sym)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
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.pre2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Development
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-29 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
|
@@ -92,19 +134,24 @@ files:
|
|
92
134
|
- config/default.yml
|
93
135
|
- lib/rubocop/cop/betterment.rb
|
94
136
|
- lib/rubocop/cop/betterment/active_job_performable.rb
|
137
|
+
- lib/rubocop/cop/betterment/allowlist_blocklist.rb
|
95
138
|
- lib/rubocop/cop/betterment/authorization_in_controller.rb
|
96
139
|
- lib/rubocop/cop/betterment/dynamic_params.rb
|
97
140
|
- lib/rubocop/cop/betterment/implicit_redirect_type.rb
|
98
141
|
- lib/rubocop/cop/betterment/memoization_with_arguments.rb
|
142
|
+
- lib/rubocop/cop/betterment/server_error_assertion.rb
|
99
143
|
- lib/rubocop/cop/betterment/site_prism_loaded.rb
|
100
144
|
- lib/rubocop/cop/betterment/spec_helper_required_outside_spec_dir.rb
|
101
145
|
- lib/rubocop/cop/betterment/timeout.rb
|
146
|
+
- lib/rubocop/cop/betterment/unsafe_job.rb
|
102
147
|
- lib/rubocop/cop/betterment/unscoped_find.rb
|
103
|
-
|
148
|
+
- lib/rubocop/cop/betterment/utils/method_return_table.rb
|
149
|
+
- lib/rubocop/cop/betterment/utils/parser.rb
|
150
|
+
homepage:
|
104
151
|
licenses:
|
105
152
|
- MIT
|
106
153
|
metadata: {}
|
107
|
-
post_install_message:
|
154
|
+
post_install_message:
|
108
155
|
rdoc_options: []
|
109
156
|
require_paths:
|
110
157
|
- lib
|
@@ -112,15 +159,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
112
159
|
requirements:
|
113
160
|
- - ">="
|
114
161
|
- !ruby/object:Gem::Version
|
115
|
-
version: '
|
162
|
+
version: '2.4'
|
116
163
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
164
|
requirements:
|
118
|
-
- - "
|
165
|
+
- - ">"
|
119
166
|
- !ruby/object:Gem::Version
|
120
|
-
version:
|
167
|
+
version: 1.3.1
|
121
168
|
requirements: []
|
122
|
-
rubygems_version: 3.
|
123
|
-
signing_key:
|
169
|
+
rubygems_version: 3.2.3
|
170
|
+
signing_key:
|
124
171
|
specification_version: 4
|
125
172
|
summary: Betterment rubocop configuration
|
126
173
|
test_files: []
|