betterlint 1.0.0 → 1.1.0

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: 540ac996157d2947a10918bc50b385456ef3f861ec04159968da0733e0a35e02
4
- data.tar.gz: 5c2d73e4041a2224abfdbd39a6777ae0ac7ccb99c1e03ba8c9bfd5d0d3ba4cad
3
+ metadata.gz: 530a4f9e133d2b476275f42d7f58050eab86379908d1d7b1ebc0ac56c7c680f5
4
+ data.tar.gz: 497ef95c882dae58db33bf6b7d02c28a63909968f6a751a0ed9a29c97d12aa83
5
5
  SHA512:
6
- metadata.gz: 80a04cb42b4ce134acc6481e79e885ff97e1ce93498599458dea26fc810c64798dedab977ef7dfc0da8aef456a4ac194384db1191fbd818f491b0b10db9336b5
7
- data.tar.gz: 26c35bc52635ed689783177f15020091e577df1abb779a9c95547ae428388f41af84420b471034ca283746e9dd2022ef99e98e885553c977964006de9efafde2
6
+ metadata.gz: 150b06a5b965394c1c7f00b95c7f3d1bb78e58855d300fb3041f90ad828fc023ca6e0cca02faad01274d58b3d5263bbf05acf337691ce5b89e98482596f30241
7
+ data.tar.gz: 2524c76c7a7ea7101f3987f79e31270fe0e51091aabf1f8bc229b9854285757e01b4c335e09be348a5f850f7b2f5b1fe16f18d4ff0d72eda94496cc1a54a0b46
data/README.md CHANGED
@@ -156,3 +156,44 @@ Betterment/UnsafeJob:
156
156
  ```
157
157
 
158
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.
159
+
160
+ ### Betterment/NonStandardActions
161
+
162
+ This cop looks at Rails route files and flags routes that go to non-standard controller actions.
163
+ The 7 standard controller actions (index, show, new, edit, create, update, destroy) are well defined,
164
+ which allow for policies and middleware that can be applied to any controller.
165
+ For example, if we want a user role to only be able to view but not modify,
166
+ we can blanket deny access to create, update, and destroy actions and have it work in most use cases.
167
+
168
+ Custom actions require explicit configuration to work with these sorts of middleware,
169
+ so we prefer to use new controllers instead. For example, a resourceful route with a custom action like this:
170
+
171
+ ```ruby
172
+ Rails.application.routes.draw do
173
+ resources :alerts, only: [:index, :summary]
174
+ end
175
+ ```
176
+
177
+ This can instead by written with an additional controller:
178
+
179
+ ```ruby
180
+ Rails.application.routes.draw do
181
+ resources :alerts, only: :index # AlertsController#index
182
+ namespace :alerts do
183
+ resource :summary, only: :show #Alerts::SummariesController#show
184
+ end
185
+ end
186
+ ```
187
+
188
+ By default this will look only in `config/routes.rb` and will use the standard 7 actions.
189
+ These values can be configured:
190
+
191
+ ```yaml
192
+ Betterment/NonStandardActions:
193
+ AdditionalAllowedActions:
194
+ - update_all
195
+ - destroy_all
196
+ Include:
197
+ - 'config/routes.rb'
198
+ - 'config/other_routes.rb'
199
+ ```
data/config/default.yml CHANGED
@@ -20,6 +20,9 @@ AllCops:
20
20
  DisplayStyleGuide: true
21
21
  DisplayCopNames: true
22
22
 
23
+ Betterment:
24
+ StyleGuideBaseURL: https://github.com/Betterment/betterlint
25
+
23
26
  Betterment/ServerErrorAssertion:
24
27
  Description: 'Detects assertions on 5XX HTTP statuses.'
25
28
  Include:
@@ -27,20 +30,43 @@ Betterment/ServerErrorAssertion:
27
30
 
28
31
  Betterment/AuthorizationInController:
29
32
  Description: 'Detects unsafe handling of id-like parameters in controllers.'
33
+ StyleGuide: '#bettermentauthorizationincontroller'
30
34
  Enabled: false
31
35
 
32
36
  Betterment/UnsafeJob:
33
37
  Enabled: false
38
+ StyleGuide: '#bettermentunsafejob'
34
39
  sensitive_params:
35
40
  - password
36
41
  - social_security_number
37
42
  - ssn
38
43
 
44
+ Betterment/UnscopedFind:
45
+ StyleGuide: '#bettermentunscopedfind'
46
+
47
+ Betterment/DynamicParams:
48
+ StyleGuide: '#bettermentdynamicparams'
49
+
39
50
  Betterment/SitePrismLoaded:
40
51
  Include:
41
52
  - 'spec/features/**/*_spec.rb'
42
53
  - 'spec/system/**/*_spec.rb'
43
54
 
55
+ Betterment/NonStandardActions:
56
+ Description: 'Detects non-standard controller actions.'
57
+ StyleGuide: '#bettermentnonstandardactions'
58
+ AdditionalAllowedActions: []
59
+ StandardActions:
60
+ - index
61
+ - show
62
+ - new
63
+ - edit
64
+ - create
65
+ - update
66
+ - destroy
67
+ Include:
68
+ - 'config/routes.rb'
69
+
44
70
  Layout/ParameterAlignment:
45
71
  Enabled: false
46
72
 
@@ -32,7 +32,7 @@ module RuboCop
32
32
  private
33
33
 
34
34
  def has_perform_method?(node)
35
- node.descendants.find(&method(:is_perform_method?))
35
+ node.descendants.find { |n| is_perform_method?(n) }
36
36
  end
37
37
  end
38
38
  end
@@ -27,7 +27,7 @@ module RuboCop
27
27
  return unless arg_nodes
28
28
 
29
29
  arg_nodes.find do |arg|
30
- arg.array_type? && find_dynamic_param(arg.values) || !arg.literal? && !arg.const_type?
30
+ (arg.array_type? && find_dynamic_param(arg.values)) || (!arg.literal? && !arg.const_type?)
31
31
  end
32
32
  end
33
33
  end
@@ -46,7 +46,7 @@ module RuboCop
46
46
  def on_block(node)
47
47
  return unless routes_file?
48
48
 
49
- if block_form_with_options(node) { |options| options.none?(&method(:valid_status_option?)) } || block_form_without_options?(node)
49
+ if block_form_with_options(node) { |options| options.none? { |n| valid_status_option?(n) } } || block_form_without_options?(node)
50
50
  add_offense(node)
51
51
  end
52
52
  end
@@ -54,7 +54,7 @@ module RuboCop
54
54
  def on_send(node)
55
55
  return unless routes_file?
56
56
 
57
- if arg_form_with_options(node) { |options| options.none?(&method(:valid_status_option?)) } || arg_form_without_options?(node)
57
+ if arg_form_with_options(node) { |options| options.none? { |n| valid_status_option?(n) } } || arg_form_without_options?(node)
58
58
  add_offense(node)
59
59
  end
60
60
  end
@@ -3,8 +3,8 @@ module RuboCop
3
3
  module Betterment
4
4
  class MemoizationWithArguments < Cop
5
5
  MSG = 'Memoized method `%<method>s` accepts arguments, ' \
6
- 'which may cause it to return a stale result. ' \
7
- 'Remove memoization or refactor to remove arguments.'.freeze
6
+ 'which may cause it to return a stale result. ' \
7
+ 'Remove memoization or refactor to remove arguments.'.freeze
8
8
 
9
9
  def self.node_pattern
10
10
  memo_assign = '(or_asgn $(ivasgn _) _)'
@@ -21,7 +21,7 @@ module RuboCop
21
21
 
22
22
  def on_def(node)
23
23
  (method_name, ivar_assign) = memoized?(node)
24
- return if ivar_assign.nil? || node.arguments.length.zero?
24
+ return if ivar_assign.nil? || node.arguments.length.zero? || method_name == :initialize
25
25
 
26
26
  msg = format(MSG, method: method_name)
27
27
  add_offense(node, location: ivar_assign.source_range, message: msg)
@@ -0,0 +1,74 @@
1
+ module RuboCop
2
+ module Cop
3
+ module Betterment
4
+ class NonStandardActions < Cop
5
+ MSG_GENERAL = 'Use a new controller instead of custom actions.'.freeze
6
+ MSG_RESOURCE_ONLY = "Resource route refers to a non-standard action in it's 'only:' param. #{MSG_GENERAL}".freeze
7
+ MSG_ROUTE_TO = "Route goes to a non-standard controller action. #{MSG_GENERAL}".freeze
8
+
9
+ # @!method routes?(node)
10
+ def_node_matcher :routes?, <<-PATTERN
11
+ (block (send (send (send (const nil? :Rails) :application) :routes) :draw) ...)
12
+ PATTERN
13
+
14
+ # @!method resource_with_only(node)
15
+ def_node_matcher :resource_with_only, <<-PATTERN
16
+ (send nil? {:resource :resources} _ (hash <(pair (sym :only) {(array (sym $_)*) (sym $_*)} ) ...> ))
17
+ PATTERN
18
+
19
+ def not_to_or_action?(sym)
20
+ !%i(to action).include?(sym)
21
+ end
22
+
23
+ # @!method route_to(node)
24
+ def_node_matcher :route_to, <<~PATTERN
25
+ (send nil? {:match :get :post :put :patch :delete} ({str sym} $_) (hash {
26
+ <(pair (sym ${:to :action}) ({str sym} $_)) ...>
27
+ (pair (sym $#not_to_or_action?) $_)*
28
+ })?)
29
+ PATTERN
30
+
31
+ def on_block(node)
32
+ if routes?(node)
33
+ node.each_descendant(:send) do |descendant_node|
34
+ check_resource_with_only(descendant_node) || check_raw_route(descendant_node)
35
+ end
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def check_resource_with_only(node)
42
+ resource_only = resource_with_only(node)
43
+ if resource_only && resource_only.any? { |action| !allowed_action?(action) }
44
+ add_offense(node, message: MSG_RESOURCE_ONLY)
45
+ true
46
+ end
47
+ end
48
+
49
+ def check_raw_route(node)
50
+ route = route_to(node)
51
+ if route
52
+ (path, param, value) = route
53
+ action = case param
54
+ when :to then value.first.split('#').last
55
+ when :action then value.first
56
+ else path
57
+ end
58
+ add_offense(node, message: MSG_ROUTE_TO) unless allowed_action?(action)
59
+ true
60
+ end
61
+ end
62
+
63
+ # NOTE: The InternalAffairs/UndefinedConfig rule seems to have a bug where it can't fine these configs in config/default.yml
64
+ def allowed_actions
65
+ @allowed_actions ||= cop_config['StandardActions'] + cop_config['AdditionalAllowedActions'] # rubocop:disable InternalAffairs/UndefinedConfig
66
+ end
67
+
68
+ def allowed_action?(action)
69
+ allowed_actions.include?(action.to_s)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -7,6 +7,7 @@ require 'rubocop/cop/betterment/unscoped_find'
7
7
  require 'rubocop/cop/betterment/unsafe_job'
8
8
  require 'rubocop/cop/betterment/timeout'
9
9
  require 'rubocop/cop/betterment/memoization_with_arguments'
10
+ require 'rubocop/cop/betterment/non_standard_actions'
10
11
  require 'rubocop/cop/betterment/site_prism_loaded'
11
12
  require 'rubocop/cop/betterment/spec_helper_required_outside_spec_dir'
12
13
  require 'rubocop/cop/betterment/implicit_redirect_type'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: betterlint
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Development
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-28 00:00:00.000000000 Z
11
+ date: 2022-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -98,16 +98,16 @@ dependencies:
98
98
  name: rake
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - "~>"
101
+ - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: '10.0'
103
+ version: 12.3.3
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - "~>"
108
+ - - ">="
109
109
  - !ruby/object:Gem::Version
110
- version: '10.0'
110
+ version: 12.3.3
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: rspec-rails
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -139,6 +139,7 @@ files:
139
139
  - lib/rubocop/cop/betterment/dynamic_params.rb
140
140
  - lib/rubocop/cop/betterment/implicit_redirect_type.rb
141
141
  - lib/rubocop/cop/betterment/memoization_with_arguments.rb
142
+ - lib/rubocop/cop/betterment/non_standard_actions.rb
142
143
  - lib/rubocop/cop/betterment/server_error_assertion.rb
143
144
  - lib/rubocop/cop/betterment/site_prism_loaded.rb
144
145
  - lib/rubocop/cop/betterment/spec_helper_required_outside_spec_dir.rb
@@ -150,7 +151,8 @@ files:
150
151
  homepage:
151
152
  licenses:
152
153
  - MIT
153
- metadata: {}
154
+ metadata:
155
+ rubygems_mfa_required: 'true'
154
156
  post_install_message:
155
157
  rdoc_options: []
156
158
  require_paths:
@@ -159,14 +161,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
159
161
  requirements:
160
162
  - - ">="
161
163
  - !ruby/object:Gem::Version
162
- version: '2.4'
164
+ version: '2.6'
163
165
  required_rubygems_version: !ruby/object:Gem::Requirement
164
166
  requirements:
165
167
  - - ">="
166
168
  - !ruby/object:Gem::Version
167
169
  version: '0'
168
170
  requirements: []
169
- rubygems_version: 3.2.3
171
+ rubygems_version: 3.3.7
170
172
  signing_key:
171
173
  specification_version: 4
172
174
  summary: Betterment rubocop configuration