gitlab-styles 9.2.0 → 10.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +14 -2
  3. data/.rubocop.yml +2 -1
  4. data/.rubocop_todo.yml +12 -0
  5. data/.tests_mapping.yml +10 -0
  6. data/Gemfile +9 -4
  7. data/gitlab-styles.gemspec +7 -7
  8. data/lefthook.yml +11 -3
  9. data/lib/gitlab/styles/version.rb +1 -1
  10. data/lib/rubocop/cop/active_record_dependent.rb +0 -5
  11. data/lib/rubocop/cop/active_record_serialize.rb +0 -6
  12. data/lib/rubocop/cop/avoid_return_from_blocks.rb +4 -4
  13. data/lib/rubocop/cop/gem_fetcher.rb +1 -1
  14. data/lib/rubocop/cop/gitlab_security/deep_munge.rb +36 -0
  15. data/lib/rubocop/cop/gitlab_security/json_serialization.rb +133 -0
  16. data/lib/rubocop/cop/gitlab_security/public_send.rb +47 -0
  17. data/lib/rubocop/cop/gitlab_security/redirect_to_params_update.rb +38 -0
  18. data/lib/rubocop/cop/gitlab_security/send_file_params.rb +40 -0
  19. data/lib/rubocop/cop/gitlab_security/sql_injection.rb +41 -0
  20. data/lib/rubocop/cop/gitlab_security/system_command_injection.rb +38 -0
  21. data/lib/rubocop/cop/in_batches.rb +0 -2
  22. data/lib/rubocop/cop/line_break_after_guard_clauses.rb +3 -5
  23. data/lib/rubocop/cop/migration/update_large_table.rb +1 -0
  24. data/lib/rubocop/cop/polymorphic_associations.rb +0 -5
  25. data/lib/rubocop/cop/rails/include_url_helper.rb +0 -2
  26. data/lib/rubocop/cop/redirect_with_status.rb +44 -30
  27. data/rubocop-capybara.yml +8 -0
  28. data/rubocop-default.yml +1 -1
  29. data/rubocop-layout.yml +2 -2
  30. data/rubocop-lint.yml +130 -2
  31. data/rubocop-naming.yml +5 -0
  32. data/rubocop-rails.yml +25 -0
  33. data/rubocop-rspec.yml +0 -5
  34. data/rubocop-security.yml +19 -1
  35. data/rubocop-style.yml +18 -3
  36. metadata +38 -29
  37. data/lib/gitlab/styles/rubocop/model_helpers.rb +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e3e06ef1557da413f41116af9b5b01733a309e35bcd2717e8d8ec2473470589
4
- data.tar.gz: 704feb3fc08d96cde2a0cdadc8dbe5bbdde1bcb3b18a010f6b964462ec0f33aa
3
+ metadata.gz: be58fa1f46b81e0e8a73c3c85843048680518c23e846bcf54a1df301c53cfa18
4
+ data.tar.gz: 95f2466fb2ae6f5dcfcfce8b7e9343101588b9627f0c9c061c53beac597126e9
5
5
  SHA512:
6
- metadata.gz: 1dcbe74347c735155eed09bfd24923dff24013454269d8edc7d5e62c4b35ebf7413958bd3b4cff55192bdc51f01dbdfdf438f853dfab322e20b96f6ed283142c
7
- data.tar.gz: 74ce09358878dd2deca30b2af8698eb46f3417b9f041794c00ee5e97bda90719548fb86250ef8718a7969c23361d256e7acf5669abaaaf6d60ed23eb5608e516
6
+ metadata.gz: cb13dbe44128173fc03b4dddf53cfdfb67a6e8d37ffc6f4c32a4e30c33ead6e686b6023c2cff9fe7fbf95600d518b508fb9b9b68ffbb07dc8686765f65f3f638
7
+ data.tar.gz: 6a2774df245a7c42725e8989a8683b4953d91f70f059c0ceb41450b1c305b375f4e150ecb9418a56f60833ce166eca335ce94ba3a03fb7cf29575c7b72cbd54f
data/.gitlab-ci.yml CHANGED
@@ -25,15 +25,27 @@ styles:
25
25
  - bundle exec rubocop --debug --parallel
26
26
  parallel:
27
27
  matrix:
28
- - RUBY_VERSION: ['2.7', '3.0']
28
+ - RUBY_VERSION: ['2.7', '3.0', '3.1', '3.2']
29
29
 
30
30
  specs:
31
31
  stage: test
32
32
  script:
33
+ # Disable simplecov for all Ruby version other than 3.0
34
+ - if [[ "$RUBY_VERSION" != "3.0" ]]; then export SIMPLECOV=0; fi
33
35
  - bundle exec rspec
34
36
  parallel:
35
37
  matrix:
36
- - RUBY_VERSION: ['2.7', '3.0']
38
+ - RUBY_VERSION: ['2.7', '3.0', '3.1', '3.2']
39
+ artifacts:
40
+ name: coverage
41
+ expire_in: 31d
42
+ paths:
43
+ - coverage/index.html
44
+ - coverage/assets/
45
+ reports:
46
+ coverage_report:
47
+ coverage_format: cobertura
48
+ path: coverage/coverage.xml
37
49
 
38
50
  include:
39
51
  - project: 'gitlab-org/quality/pipeline-common'
data/.rubocop.yml CHANGED
@@ -1,12 +1,13 @@
1
1
  inherit_from:
2
+ - .rubocop_todo.yml
2
3
  - rubocop-default.yml
3
4
 
4
5
  require:
5
6
  - rubocop/cop/internal_affairs
7
+ - rubocop-rake
6
8
 
7
9
  AllCops:
8
10
  NewCops: disable # https://gitlab.com/gitlab-org/ruby/gems/gitlab-styles/-/issues/40
9
- SuggestExtensions: false # https://gitlab.com/gitlab-org/ruby/gems/gitlab-styles/-/issues/39
10
11
 
11
12
  InternalAffairs/DeprecateCopHelper:
12
13
  Enabled: true
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,12 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2023-01-11 12:59:00 UTC using RuboCop version 1.43.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ InternalAffairs/InheritDeprecatedCopClass:
11
+ Exclude:
12
+ - 'lib/rubocop/cop/gitlab_security/json_serialization.rb'
@@ -0,0 +1,10 @@
1
+ ---
2
+ mapping:
3
+ - source: 'lib/(.+)\.rb'
4
+ test: 'spec/%s_spec.rb'
5
+
6
+ - source: 'rubocop-.*\.yml'
7
+ test: 'spec/yml_spec.rb'
8
+
9
+ - source: '(spec/.*_spec\.rb)'
10
+ test: '%s'
data/Gemfile CHANGED
@@ -6,12 +6,17 @@ source 'https://rubygems.org'
6
6
  gemspec
7
7
 
8
8
  group :development do
9
- gem "lefthook", require: false
9
+ gem 'lefthook', require: false
10
+ gem 'test_file_finder', '~> 0.1.4'
10
11
  end
11
12
 
12
13
  group :test do
13
14
  # Pin these dependencies, otherwise a new rule could break the CI pipelines
14
- gem 'rubocop', '1.38.0'
15
- gem 'rubocop-rspec', '2.15.0'
16
- gem 'rspec-parameterized', '0.5.2', require: false
15
+ gem 'rubocop', '1.43.0'
16
+ gem 'rubocop-rspec', '2.18.1'
17
+ gem 'rspec-parameterized-table_syntax', '1.0.0', require: false
18
+
19
+ gem 'simplecov', '~> 0.22.0', require: false
20
+ gem 'simplecov-html', '~> 0.12.3', require: false
21
+ gem 'simplecov-cobertura', '~> 2.1.0', require: false
17
22
  end
@@ -22,15 +22,15 @@ Gem::Specification.new do |spec|
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ['lib']
24
24
 
25
- spec.add_dependency 'rubocop', '~> 1.38.0'
26
- spec.add_dependency 'rubocop-gitlab-security', '~> 0.1.1'
27
- spec.add_dependency 'rubocop-graphql', '~> 0.14'
28
- spec.add_dependency 'rubocop-performance', '~> 1.14'
29
- spec.add_dependency 'rubocop-rails', '~> 2.15'
30
- spec.add_dependency 'rubocop-rspec', '~> 2.15'
25
+ spec.add_dependency 'rubocop', '~> 1.43.0'
26
+ spec.add_dependency 'rubocop-graphql', '~> 0.18'
27
+ spec.add_dependency 'rubocop-performance', '~> 1.15'
28
+ spec.add_dependency 'rubocop-rails', '~> 2.17'
29
+ spec.add_dependency 'rubocop-rspec', '~> 2.18'
31
30
 
32
31
  spec.add_development_dependency 'bundler', '~> 2.1'
33
32
  spec.add_development_dependency 'gitlab-dangerfiles', '~> 2.11.0'
34
- spec.add_development_dependency 'rake', '~> 10.0'
33
+ spec.add_development_dependency 'rake', '~> 13.0'
35
34
  spec.add_development_dependency 'rspec', '~> 3.0'
35
+ spec.add_development_dependency 'rubocop-rake', '~> 0.6'
36
36
  end
data/lefthook.yml CHANGED
@@ -10,7 +10,15 @@ pre-push:
10
10
  glob: '*.{rb,rake}'
11
11
  run: bundle exec rubocop --parallel --force-exclusion {files}
12
12
 
13
- # Run all tests (warn if there are any missing tools required for tests).
13
+ # Run only relevant specs.
14
14
  rspec:
15
- run: bundle exec rspec -f progress
16
- glob: '*.rb'
15
+ files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
16
+ run: |
17
+ tests=$(tff --mapping-file .tests_mapping.yml {files})
18
+ if [ "$tests" != "" ]; then
19
+ echo "bundle exec rspec --format progress $tests"
20
+ bundle exec rspec --format progress $tests
21
+ else
22
+ echo "No specs to run."
23
+ exit 0
24
+ fi
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Gitlab
4
4
  module Styles
5
- VERSION = '9.2.0'
5
+ VERSION = '10.0.0'
6
6
  end
7
7
  end
@@ -1,13 +1,9 @@
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 `dependent: ...` in ActiveRecord models.
8
6
  class ActiveRecordDependent < RuboCop::Cop::Base
9
- include Gitlab::Styles::Rubocop::ModelHelpers
10
-
11
7
  MSG = 'Do not use `dependent:` to remove associated data, ' \
12
8
  'use foreign keys with cascading deletes instead.'
13
9
 
@@ -15,7 +11,6 @@ module Rubocop
15
11
  ALLOWED_OPTIONS = [:restrict_with_error].freeze
16
12
 
17
13
  def on_send(node)
18
- return unless in_model?(node)
19
14
  return unless METHOD_NAMES.include?(node.children[1])
20
15
 
21
16
  node.children.last.each_node(:pair) do |pair|
@@ -1,18 +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 `serialize` in ActiveRecord models.
8
6
  class ActiveRecordSerialize < RuboCop::Cop::Base
9
- include Gitlab::Styles::Rubocop::ModelHelpers
10
-
11
7
  MSG = 'Do not store serialized data in the database, use separate columns and/or tables instead'
12
8
 
13
9
  def on_send(node)
14
- return unless in_model?(node)
15
-
16
10
  add_offense(node.loc.selector) if node.children[1] == :serialize
17
11
  end
18
12
  end
@@ -23,7 +23,7 @@ module Rubocop
23
23
  class AvoidReturnFromBlocks < RuboCop::Cop::Base
24
24
  MSG = 'Do not return from a block, use next or break instead.'
25
25
  DEF_METHODS = %i[define_method lambda].freeze
26
- WHITELISTED_METHODS = %i[each each_filename times loop].freeze
26
+ ALLOWED_METHODS = %i[each each_filename times loop].freeze
27
27
 
28
28
  def on_block(node)
29
29
  block_body = node.body
@@ -32,7 +32,7 @@ module Rubocop
32
32
  return unless top_block?(node)
33
33
 
34
34
  block_body.each_node(:return) do |return_node|
35
- next if parent_blocks(node, return_node).all? { |block| whitelisted?(block) }
35
+ next if parent_blocks(node, return_node).all? { |block| allowed?(block) }
36
36
 
37
37
  add_offense(return_node)
38
38
  end
@@ -71,8 +71,8 @@ module Rubocop
71
71
  (node.block_type? && DEF_METHODS.include?(node.method_name))
72
72
  end
73
73
 
74
- def whitelisted?(block_node)
75
- WHITELISTED_METHODS.include?(block_node.method_name)
74
+ def allowed?(block_node)
75
+ ALLOWED_METHODS.include?(block_node.method_name)
76
76
  end
77
77
  end
78
78
  end
@@ -14,7 +14,7 @@ module Rubocop
14
14
 
15
15
  # @!method gem_option(node)
16
16
  def_node_matcher :gem_option, <<~PATTERN
17
- (send nil? :gem _
17
+ (send nil? :gem _ ...
18
18
  (hash
19
19
  <$(pair (sym {#{GIT_SOURCES.map(&:inspect).join(' ')}}) _)
20
20
  ...>
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module GitlabSecurity
6
+ # Checks for disabling the deep munge security control.
7
+ #
8
+ # Disabling this security setting can leave the application open to unsafe
9
+ # query generation
10
+ #
11
+ # @example
12
+ #
13
+ # # bad
14
+ # config.action_dispatch.perform_deep_munge = false
15
+ #
16
+ # See CVE-2012-2660, CVE-2012-2694, and CVE-2013-0155.
17
+ class DeepMunge < RuboCop::Cop::Base
18
+ MSG = 'Never disable the deep munge security option.'
19
+
20
+ # @!method disable_deep_munge?(node)
21
+ def_node_matcher :disable_deep_munge?, <<-PATTERN
22
+ (send
23
+ (send (send nil? :config) :action_dispatch) :perform_deep_munge=
24
+ { (false) (send true :!) }
25
+ )
26
+ PATTERN
27
+
28
+ def on_send(node)
29
+ return unless disable_deep_munge?(node)
30
+
31
+ add_offense(node.loc.selector)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -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
@@ -1,7 +1,5 @@
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 `in_batches`
@@ -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,11 +70,7 @@ 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
- end
73
-
74
- def autocorrect(node)
75
- lambda do |corrector|
73
+ add_offense(node) do |corrector|
76
74
  corrector.insert_after(node.loc.expression, "\n")
77
75
  end
78
76
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative '../../../gitlab/styles/rubocop/migration_helpers'
3
4
 
4
5
  module Rubocop
@@ -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|