gitlab-styles 9.2.0 → 10.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|