gitlab-styles 9.2.0 → 10.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 +4 -4
- data/.gitignore +3 -3
- data/.gitlab/merge_request_templates/Release.md +18 -5
- data/.gitlab-ci.yml +17 -2
- data/.rubocop.yml +6 -1
- data/.rubocop_todo.yml +36 -0
- data/.tests_mapping.yml +10 -0
- data/Gemfile +0 -11
- data/Gemfile.lock +227 -0
- data/README.md +0 -1
- data/gitlab-styles.gemspec +15 -8
- data/lefthook.yml +11 -3
- data/lib/gitlab/styles/rubocop/migration_helpers.rb +1 -1
- data/lib/gitlab/styles/version.rb +1 -1
- data/lib/rubocop/cop/active_record_dependent.rb +0 -5
- data/lib/rubocop/cop/active_record_serialize.rb +0 -6
- data/lib/rubocop/cop/avoid_return_from_blocks.rb +4 -4
- data/lib/rubocop/cop/custom_error_class.rb +1 -1
- data/lib/rubocop/cop/gem_fetcher.rb +1 -1
- data/lib/rubocop/cop/gitlab_security/deep_munge.rb +36 -0
- data/lib/rubocop/cop/gitlab_security/json_serialization.rb +133 -0
- data/lib/rubocop/cop/gitlab_security/public_send.rb +47 -0
- data/lib/rubocop/cop/gitlab_security/redirect_to_params_update.rb +38 -0
- data/lib/rubocop/cop/gitlab_security/send_file_params.rb +40 -0
- data/lib/rubocop/cop/gitlab_security/sql_injection.rb +41 -0
- data/lib/rubocop/cop/gitlab_security/system_command_injection.rb +38 -0
- data/lib/rubocop/cop/in_batches.rb +0 -2
- data/lib/rubocop/cop/internal_affairs/missing_cop_department.rb +80 -0
- data/lib/rubocop/cop/internal_affairs/use_restrict_on_send.rb +99 -0
- data/lib/rubocop/cop/line_break_after_guard_clauses.rb +4 -6
- data/lib/rubocop/cop/line_break_around_conditional_block.rb +1 -1
- data/lib/rubocop/cop/migration/update_large_table.rb +1 -0
- data/lib/rubocop/cop/polymorphic_associations.rb +0 -5
- data/lib/rubocop/cop/rails/include_url_helper.rb +0 -2
- data/lib/rubocop/cop/redirect_with_status.rb +44 -30
- data/lib/rubocop/cop/rspec/empty_line_after_shared_example.rb +9 -2
- data/lib/rubocop/cop/rspec/example_starting_character.rb +1 -1
- data/lib/rubocop/cop/rspec/factory_bot/excessive_create_list.rb +52 -0
- data/lib/rubocop/cop/rspec/useless_dynamic_definition.rb +67 -0
- data/lib/rubocop/cop/rspec/verbose_include_metadata.rb +1 -1
- data/rubocop-capybara.yml +8 -0
- data/rubocop-default.yml +2 -4
- data/rubocop-gemspec.yml +6 -0
- data/rubocop-internal-affairs.yml +11 -0
- data/rubocop-layout.yml +2 -2
- data/rubocop-lint.yml +134 -5
- data/rubocop-naming.yml +5 -0
- data/rubocop-rails.yml +33 -1
- data/rubocop-rspec.yml +5 -5
- data/rubocop-security.yml +19 -1
- data/rubocop-style.yml +18 -3
- metadata +142 -29
- data/lib/gitlab/styles/rubocop/model_helpers.rb +0 -19
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module GitlabSecurity
|
6
|
+
# Checks for `to_json` / `as_json` without allowing via `only`.
|
7
|
+
#
|
8
|
+
# Either method called on an instance of a `Serializer` class will be
|
9
|
+
# ignored. Associations included via `include` are subject to the same
|
10
|
+
# rules.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
#
|
14
|
+
# # bad
|
15
|
+
# render json: @user.to_json
|
16
|
+
# render json: @user.to_json(except: %i[password])
|
17
|
+
# render json: @user.to_json(
|
18
|
+
# only: %i[username],
|
19
|
+
# include: [:identities]
|
20
|
+
# )
|
21
|
+
#
|
22
|
+
# # acceptable
|
23
|
+
# render json: UserSerializer.new.to_json
|
24
|
+
#
|
25
|
+
# # good
|
26
|
+
# render json: @user.to_json(only: %i[name username])
|
27
|
+
# render json: @user.to_json(
|
28
|
+
# only: %i[username],
|
29
|
+
# include: { identities: { only: %i[provider] } }
|
30
|
+
# )
|
31
|
+
#
|
32
|
+
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/29661
|
33
|
+
class JsonSerialization < RuboCop::Cop::Cop
|
34
|
+
MSG = "Don't use `%s` without specifying `only`"
|
35
|
+
|
36
|
+
# Check for `to_json` sent to any object that's not a Hash literal or
|
37
|
+
# Serializer instance
|
38
|
+
# @!method json_serialization?(node)
|
39
|
+
def_node_matcher :json_serialization?, <<~PATTERN
|
40
|
+
(send !{nil? hash #serializer?} ${:to_json :as_json} $...)
|
41
|
+
PATTERN
|
42
|
+
|
43
|
+
# Check if node is a `only: ...` pair
|
44
|
+
# @!method only_pair?(node)
|
45
|
+
def_node_matcher :only_pair?, <<~PATTERN
|
46
|
+
(pair (sym :only) ...)
|
47
|
+
PATTERN
|
48
|
+
|
49
|
+
# Check if node is a `include: {...}` pair
|
50
|
+
# @!method include_pair?(node)
|
51
|
+
def_node_matcher :include_pair?, <<~PATTERN
|
52
|
+
(pair (sym :include) (hash $...))
|
53
|
+
PATTERN
|
54
|
+
|
55
|
+
# Check for a `only: [...]` pair anywhere in the node
|
56
|
+
# @!method contains_only?(node)
|
57
|
+
def_node_search :contains_only?, <<~PATTERN
|
58
|
+
(pair (sym :only) (array ...))
|
59
|
+
PATTERN
|
60
|
+
|
61
|
+
# Check for `SomeConstant.new`
|
62
|
+
# @!method constant_init(node)
|
63
|
+
def_node_search :constant_init, <<~PATTERN
|
64
|
+
(send (const nil? $_) :new ...)
|
65
|
+
PATTERN
|
66
|
+
|
67
|
+
def on_send(node)
|
68
|
+
matched = json_serialization?(node)
|
69
|
+
return unless matched
|
70
|
+
|
71
|
+
@_has_top_level_only = false
|
72
|
+
@method = matched.first
|
73
|
+
|
74
|
+
if matched.last.nil? || matched.last.empty?
|
75
|
+
# Empty `to_json` call
|
76
|
+
add_offense(node, location: :selector, message: format_message)
|
77
|
+
else
|
78
|
+
check_arguments(node, matched)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def format_message
|
85
|
+
format(MSG, @method)
|
86
|
+
end
|
87
|
+
|
88
|
+
def serializer?(node)
|
89
|
+
constant_init(node).any? { |name| name.to_s.end_with?('Serializer') }
|
90
|
+
end
|
91
|
+
|
92
|
+
def check_arguments(node, matched)
|
93
|
+
options = matched.last.first
|
94
|
+
|
95
|
+
# If `to_json` was given an argument that isn't a Hash, we don't
|
96
|
+
# know what to do here, so just move along
|
97
|
+
return unless options.hash_type?
|
98
|
+
|
99
|
+
options.each_child_node do |child_node|
|
100
|
+
check_pair(child_node)
|
101
|
+
end
|
102
|
+
|
103
|
+
return unless requires_only?
|
104
|
+
|
105
|
+
# Add a top-level offense for the entire argument list, but only if
|
106
|
+
# we haven't yet added any offenses to the child Hash values (such
|
107
|
+
# as `include`)
|
108
|
+
add_offense(node.children.last, message: format_message)
|
109
|
+
end
|
110
|
+
|
111
|
+
def check_pair(pair)
|
112
|
+
if only_pair?(pair)
|
113
|
+
@_has_top_level_only = true
|
114
|
+
elsif include_pair?(pair)
|
115
|
+
includes = pair.value
|
116
|
+
|
117
|
+
includes.each_child_node do |child_node|
|
118
|
+
next if contains_only?(child_node)
|
119
|
+
|
120
|
+
add_offense(child_node, message: format_message)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def requires_only?
|
126
|
+
return false if @_has_top_level_only
|
127
|
+
|
128
|
+
offenses.count.zero?
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module GitlabSecurity
|
6
|
+
# Checks for the use of `public_send`, `send`, and `__send__` methods.
|
7
|
+
#
|
8
|
+
# If passed untrusted input these methods can be used to execute arbitrary
|
9
|
+
# methods on behalf of an attacker.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
#
|
13
|
+
# # bad
|
14
|
+
# myobj.public_send("#{params[:foo]}")
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# case params[:foo].to_s
|
18
|
+
# when 'choice1'
|
19
|
+
# items.choice1
|
20
|
+
# when 'choice2'
|
21
|
+
# items.choice2
|
22
|
+
# when 'choice3'
|
23
|
+
# items.choice3
|
24
|
+
# end
|
25
|
+
class PublicSend < RuboCop::Cop::Base
|
26
|
+
MSG = 'Avoid using `%s`.'
|
27
|
+
|
28
|
+
RESTRICT_ON_SEND = %i[send public_send __send__].freeze
|
29
|
+
|
30
|
+
# @!method send?(node)
|
31
|
+
def_node_matcher :send?, <<-PATTERN
|
32
|
+
({csend | send} _ ${:send :public_send :__send__} ...)
|
33
|
+
PATTERN
|
34
|
+
|
35
|
+
def on_send(node)
|
36
|
+
send?(node) do |match|
|
37
|
+
next unless node.arguments?
|
38
|
+
|
39
|
+
add_offense(node.loc.selector, message: format(MSG, match))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
alias_method :on_csend, :on_send
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module GitlabSecurity
|
6
|
+
# Check for use of redirect_to(params.update())
|
7
|
+
#
|
8
|
+
# Passing user params to the redirect_to method provides an open redirect
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
#
|
12
|
+
# # bad
|
13
|
+
# redirect_to(params.update(action: 'main'))
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# redirect_to(allowed(params))
|
17
|
+
#
|
18
|
+
class RedirectToParamsUpdate < RuboCop::Cop::Base
|
19
|
+
MSG = 'Avoid using `redirect_to(params.%<name>s(...))`. ' \
|
20
|
+
'Only pass allowed arguments into redirect_to() (e.g. not including `host`)'
|
21
|
+
|
22
|
+
# @!method redirect_to_params_update_node(node)
|
23
|
+
def_node_matcher :redirect_to_params_update_node, <<-PATTERN
|
24
|
+
(send nil? :redirect_to $(send (send nil? :params) ${:update :merge} ...))
|
25
|
+
PATTERN
|
26
|
+
|
27
|
+
def on_send(node)
|
28
|
+
selected, name = redirect_to_params_update_node(node)
|
29
|
+
return unless name
|
30
|
+
|
31
|
+
message = format(MSG, name: name)
|
32
|
+
|
33
|
+
add_offense(selected, message: message)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module GitlabSecurity
|
6
|
+
# Check for use of send_file(..., params[], ...)
|
7
|
+
#
|
8
|
+
# Passing user params to the send_file() method allows directory traversal
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
#
|
12
|
+
# # bad
|
13
|
+
# send_file("/tmp/myproj/" + params[:filename])
|
14
|
+
#
|
15
|
+
# # good (verify directory)
|
16
|
+
|
17
|
+
# basename = File.expand_path("/tmp/myproj")
|
18
|
+
# filename = File.expand_path(File.join(basename, @file.public_filename))
|
19
|
+
# raise if basename != filename
|
20
|
+
# send_file filename, disposition: 'inline'
|
21
|
+
#
|
22
|
+
class SendFileParams < RuboCop::Cop::Base
|
23
|
+
MSG = 'Do not pass user provided params directly to send_file(), ' \
|
24
|
+
'verify the path with file.expand_path() first.'
|
25
|
+
|
26
|
+
# @!method params_node?(node)
|
27
|
+
def_node_search :params_node?, <<-PATTERN
|
28
|
+
(send (send nil? :params) ... )
|
29
|
+
PATTERN
|
30
|
+
|
31
|
+
def on_send(node)
|
32
|
+
return unless node.command?(:send_file)
|
33
|
+
return unless node.arguments.any? { |e| params_node?(e) }
|
34
|
+
|
35
|
+
add_offense(node.loc.selector)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module GitlabSecurity
|
6
|
+
# Check for use of where("name = '#{params[:name]}'")
|
7
|
+
#
|
8
|
+
# Passing user input to where() without parameterization can result in SQL Injection
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
#
|
12
|
+
# # bad
|
13
|
+
# u = User.where("name = '#{params[:name]}'")
|
14
|
+
#
|
15
|
+
# # good (parameters)
|
16
|
+
# u = User.where("name = ? AND id = ?", params[:name], params[:id])
|
17
|
+
# u = User.where(name: params[:name], id: params[:id])
|
18
|
+
#
|
19
|
+
class SqlInjection < RuboCop::Cop::Base
|
20
|
+
MSG = 'Parameterize all user-input passed to where(), do not directly embed user input in SQL queries.'
|
21
|
+
|
22
|
+
# @!method where_user_input?(node)
|
23
|
+
def_node_matcher :where_user_input?, <<-PATTERN
|
24
|
+
(send _ :where ...)
|
25
|
+
PATTERN
|
26
|
+
|
27
|
+
# @!method string_var_string?(node)
|
28
|
+
def_node_matcher :string_var_string?, <<-PATTERN
|
29
|
+
(dstr (str ...) (begin ...) (str ...) ...)
|
30
|
+
PATTERN
|
31
|
+
|
32
|
+
def on_send(node)
|
33
|
+
return unless where_user_input?(node)
|
34
|
+
return unless node.arguments.any? { |e| string_var_string?(e) }
|
35
|
+
|
36
|
+
add_offense(node.loc.selector)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module GitlabSecurity
|
6
|
+
# Check for use of system("/bin/ls #{params[:file]}")
|
7
|
+
#
|
8
|
+
# Passing user input to system() without sanitization and parameterization can result in command injection
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
#
|
12
|
+
# # bad
|
13
|
+
# system("/bin/ls #{filename}")
|
14
|
+
#
|
15
|
+
# # good (parameters)
|
16
|
+
# system("/bin/ls", filename)
|
17
|
+
# # even better
|
18
|
+
# exec("/bin/ls", shell_escape(filename))
|
19
|
+
#
|
20
|
+
class SystemCommandInjection < RuboCop::Cop::Base
|
21
|
+
MSG = 'Do not include variables in the command name for system(). ' \
|
22
|
+
'Use parameters "system(cmd, params)" or exec() instead.'
|
23
|
+
|
24
|
+
# @!method system_var?(node)
|
25
|
+
def_node_matcher :system_var?, <<-PATTERN
|
26
|
+
(dstr (str ...) (begin ...) ...)
|
27
|
+
PATTERN
|
28
|
+
|
29
|
+
def on_send(node)
|
30
|
+
return unless node.command?(:system)
|
31
|
+
return unless node.arguments.any? { |e| system_var?(e) }
|
32
|
+
|
33
|
+
add_offense(node.loc.selector)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module InternalAffairs
|
6
|
+
# Enforces the use of explicit department names for cop rules.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# module RuboCop
|
11
|
+
# module Cop
|
12
|
+
# class Implicit
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# module RuboCop
|
18
|
+
# module Cop
|
19
|
+
# module Cop
|
20
|
+
# class Explicit
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# # good
|
27
|
+
# module RuboCop
|
28
|
+
# module Cop
|
29
|
+
# module Foo
|
30
|
+
# class Implicit
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# module RuboCop
|
37
|
+
# module Cop
|
38
|
+
# module Foo
|
39
|
+
# class Explicit
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
class MissingCopDepartment < Base
|
45
|
+
MSG = 'Define a proper department. Using `Cop/` as department is discourged.'
|
46
|
+
|
47
|
+
COP_DEPARTMENT = 'Cop'
|
48
|
+
|
49
|
+
def on_class(node)
|
50
|
+
namespace = full_namespace(node)
|
51
|
+
|
52
|
+
# Skip top-level RuboCop::Cop
|
53
|
+
names = namespace.drop(2)
|
54
|
+
|
55
|
+
add_offense(node.loc.name) if names.size < 2 || names.first == COP_DEPARTMENT
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def full_namespace(node)
|
61
|
+
(node_namespace(node) + parents_namespace(node)).reverse
|
62
|
+
end
|
63
|
+
|
64
|
+
def node_namespace(node)
|
65
|
+
name_parts(node).reverse
|
66
|
+
end
|
67
|
+
|
68
|
+
def parents_namespace(node)
|
69
|
+
node
|
70
|
+
.each_ancestor(:module, :class)
|
71
|
+
.flat_map { |node| name_parts(node) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def name_parts(node)
|
75
|
+
node.identifier.source.split('::')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module InternalAffairs
|
6
|
+
# Flags if `RESTRICT_ON_SEND` constant not defined and method name is
|
7
|
+
# checked programmatically in `on_send` methods.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# def on_send(node)
|
12
|
+
# return unless method_name(node) == :foo
|
13
|
+
# return unless node.children[1] == :foo
|
14
|
+
# return unless METHOD_NAMES.include?(method_name(node))
|
15
|
+
#
|
16
|
+
# name = node.children[1]
|
17
|
+
# return unless name == :foo
|
18
|
+
# name2 = method_name(node)
|
19
|
+
# return unless name == :foo
|
20
|
+
#
|
21
|
+
# # more code
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# # good
|
25
|
+
# RESTRICT_ON_SEND = %i[foo].freeze
|
26
|
+
#
|
27
|
+
# def on_send(node)
|
28
|
+
# # more code
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# # ignored - not `on_send`
|
32
|
+
# def on_def(node)
|
33
|
+
# return unless method_name(node) == :foo
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# # ignored - `else` branch
|
37
|
+
# def on_send(node)
|
38
|
+
# if method_name(node) == :foo
|
39
|
+
# add_offense(node)
|
40
|
+
# else
|
41
|
+
# something_else
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
class UseRestrictOnSend < Base
|
45
|
+
MSG = 'Define constant `RESTRICT_ON_SEND` to speed up calls to `on_send`. ' \
|
46
|
+
'The following line is then no longer necessary:'
|
47
|
+
|
48
|
+
# @!method method_name_plain(node)
|
49
|
+
def_node_matcher :method_name_plain, <<~PATTERN
|
50
|
+
{
|
51
|
+
(send _ :method_name _ ...) # method_name(node)
|
52
|
+
(send
|
53
|
+
(send _ :children) :[] (int 1) # node.children[1]
|
54
|
+
)
|
55
|
+
}
|
56
|
+
PATTERN
|
57
|
+
|
58
|
+
# @!method method_name_call(node)
|
59
|
+
def_node_matcher :method_name_call, <<~PATTERN
|
60
|
+
{
|
61
|
+
#method_name_plain
|
62
|
+
(lvar %1)
|
63
|
+
}
|
64
|
+
PATTERN
|
65
|
+
|
66
|
+
# @!method method_name_assignment(node)
|
67
|
+
def_node_search :method_name_assignment, <<~PATTERN
|
68
|
+
(lvasgn $_name #method_name_plain)
|
69
|
+
PATTERN
|
70
|
+
|
71
|
+
# @!method method_name_check(node)
|
72
|
+
def_node_search :method_name_check, <<~PATTERN
|
73
|
+
(if
|
74
|
+
${
|
75
|
+
(send #method_name_call(%1) {:== :!=} _) # method_name(node) == foo
|
76
|
+
(send _ :include? #method_name_call(%1)) # a.include?(method_name(node))
|
77
|
+
}
|
78
|
+
{!nil? nil? | nil? !nil?} # has either `if` or `else` branch - not both
|
79
|
+
)
|
80
|
+
PATTERN
|
81
|
+
|
82
|
+
def on_def(node)
|
83
|
+
return unless node.method?(:on_send)
|
84
|
+
return if @restrict_on_send_set
|
85
|
+
|
86
|
+
local_assignments = method_name_assignment(node).to_set
|
87
|
+
|
88
|
+
method_name_check(node, local_assignments) do |call_node|
|
89
|
+
add_offense(call_node)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def on_casgn(node)
|
94
|
+
@restrict_on_send_set = true if node.name == :RESTRICT_ON_SEND
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -56,6 +56,8 @@ module Rubocop
|
|
56
56
|
#
|
57
57
|
# do_something_more
|
58
58
|
class LineBreakAfterGuardClauses < RuboCop::Cop::Base
|
59
|
+
extend RuboCop::Cop::AutoCorrector
|
60
|
+
|
59
61
|
MSG = 'Add a line break after guard clauses'
|
60
62
|
|
61
63
|
# @!method guard_clause_node?(node)
|
@@ -68,12 +70,8 @@ module Rubocop
|
|
68
70
|
return unless guard_clause?(node)
|
69
71
|
return if next_line(node).blank? || clause_last_line?(next_line(node)) || guard_clause?(next_sibling(node))
|
70
72
|
|
71
|
-
add_offense(node)
|
72
|
-
|
73
|
-
|
74
|
-
def autocorrect(node)
|
75
|
-
lambda do |corrector|
|
76
|
-
corrector.insert_after(node.loc.expression, "\n")
|
73
|
+
add_offense(node) do |corrector|
|
74
|
+
corrector.insert_after(node, "\n")
|
77
75
|
end
|
78
76
|
end
|
79
77
|
|
@@ -1,17 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../../gitlab/styles/rubocop/model_helpers'
|
4
|
-
|
5
3
|
module Rubocop
|
6
4
|
module Cop
|
7
5
|
# Cop that prevents the use of polymorphic associations
|
8
6
|
class PolymorphicAssociations < RuboCop::Cop::Base
|
9
|
-
include Gitlab::Styles::Rubocop::ModelHelpers
|
10
|
-
|
11
7
|
MSG = 'Do not use polymorphic associations, use separate tables instead'
|
12
8
|
|
13
9
|
def on_send(node)
|
14
|
-
return unless in_model?(node)
|
15
10
|
return unless node.children[1] == :belongs_to
|
16
11
|
|
17
12
|
node.children.last.each_node(:pair) do |pair|
|
@@ -2,44 +2,58 @@
|
|
2
2
|
|
3
3
|
module Rubocop
|
4
4
|
module Cop
|
5
|
-
# Prevents usage of 'redirect_to' in actions 'destroy'
|
5
|
+
# Prevents usage of 'redirect_to' in actions 'destroy' and 'destroy_all'
|
6
|
+
# without specifying 'status'.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
#
|
11
|
+
# def destroy
|
12
|
+
# redirect_to root_path
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# def destroy_all
|
16
|
+
# redirect_to root_path, alert: 'Oh no!'
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
#
|
21
|
+
# def destroy
|
22
|
+
# redirect_to root_path, status: 302
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# def destroy_all
|
26
|
+
# redirect_to root_path, alert: 'Oh no!', status: 302
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# def show
|
30
|
+
# redirect_to root_path
|
31
|
+
# end
|
32
|
+
#
|
6
33
|
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/31840
|
7
34
|
class RedirectWithStatus < RuboCop::Cop::Base
|
8
|
-
MSG = 'Do not use "redirect_to" without "status" in "
|
35
|
+
MSG = 'Do not use "redirect_to" without "status" in "%<name>s" action.'
|
9
36
|
|
10
|
-
|
11
|
-
return unless in_controller?(node)
|
12
|
-
return unless destroy?(node) || destroy_all?(node)
|
37
|
+
RESTRICT_ON_SEND = %i[redirect_to].freeze
|
13
38
|
|
14
|
-
|
15
|
-
next unless redirect_to?(def_node)
|
39
|
+
ACTIONS = %i[destroy destroy_all].to_set.freeze
|
16
40
|
|
17
|
-
|
41
|
+
# @!method redirect_to_with_status?(node)
|
42
|
+
def_node_matcher :redirect_to_with_status?, <<~PATTERN
|
43
|
+
(send nil? :redirect_to ...
|
44
|
+
(hash <(pair (sym :status) _) ...>)
|
45
|
+
)
|
46
|
+
PATTERN
|
18
47
|
|
19
|
-
|
20
|
-
|
21
|
-
end
|
48
|
+
def on_send(node)
|
49
|
+
return if redirect_to_with_status?(node)
|
22
50
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def in_controller?(node)
|
30
|
-
node.location.expression.source_buffer.name.end_with?('_controller.rb')
|
31
|
-
end
|
51
|
+
node.each_ancestor(:def) do |def_node|
|
52
|
+
next unless ACTIONS.include?(def_node.method_name)
|
32
53
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
def destroy_all?(node)
|
38
|
-
node.children.first == :destroy_all
|
39
|
-
end
|
40
|
-
|
41
|
-
def redirect_to?(node)
|
42
|
-
node.children[1] == :redirect_to
|
54
|
+
message = format(MSG, name: def_node.method_name)
|
55
|
+
add_offense(node.loc.selector, message: message)
|
56
|
+
end
|
43
57
|
end
|
44
58
|
end
|
45
59
|
end
|