betterlint 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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