gitlab-styles 9.1.0 → 10.0.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/.gitlab-ci.yml +14 -2
- data/.rubocop.yml +2 -1
- data/.rubocop_todo.yml +12 -0
- data/.tests_mapping.yml +10 -0
- data/Gemfile +9 -4
- data/README.md +9 -8
- data/gitlab-styles.gemspec +7 -7
- data/lefthook.yml +11 -3
- 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/gem_fetcher.rb +18 -20
- 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/line_break_after_guard_clauses.rb +3 -5
- data/lib/rubocop/cop/line_break_around_conditional_block.rb +5 -0
- 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 +1 -1
- data/rubocop-bundler.yml +10 -0
- data/rubocop-capybara.yml +8 -0
- data/rubocop-default.yml +1 -1
- data/rubocop-layout.yml +48 -4
- data/rubocop-lint.yml +131 -3
- data/rubocop-naming.yml +5 -0
- data/rubocop-performance.yml +32 -0
- data/rubocop-rails.yml +25 -0
- data/rubocop-rspec.yml +1 -5
- data/rubocop-security.yml +19 -1
- data/rubocop-style.yml +18 -3
- metadata +38 -29
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be58fa1f46b81e0e8a73c3c85843048680518c23e846bcf54a1df301c53cfa18
|
4
|
+
data.tar.gz: 95f2466fb2ae6f5dcfcfce8b7e9343101588b9627f0c9c061c53beac597126e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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'
|
data/.tests_mapping.yml
ADDED
data/Gemfile
CHANGED
@@ -6,12 +6,17 @@ source 'https://rubygems.org'
|
|
6
6
|
gemspec
|
7
7
|
|
8
8
|
group :development do
|
9
|
-
gem
|
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.
|
15
|
-
gem 'rubocop-rspec', '2.
|
16
|
-
gem 'rspec-parameterized', '0.
|
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
|
data/README.md
CHANGED
@@ -80,13 +80,6 @@ bundle exec rubocop -c .rubocop.yml
|
|
80
80
|
lefthook install
|
81
81
|
```
|
82
82
|
|
83
|
-
## Contributing
|
84
|
-
|
85
|
-
Bug reports and merge requests are welcome on GitLab at
|
86
|
-
https://gitlab.com/gitlab-org/gitlab-styles. This project is intended to be a
|
87
|
-
safe, welcoming space for collaboration, and contributors are expected to adhere
|
88
|
-
to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
89
|
-
|
90
83
|
## Release Process
|
91
84
|
|
92
85
|
We release `gitlab-styles` on an ad-hoc basis. There is no regularity to when
|
@@ -98,10 +91,18 @@ To release a new version:
|
|
98
91
|
1. Create a Merge Request.
|
99
92
|
1. Use Merge Request template [Release.md](https://gitlab.com/gitlab-org/ruby/gems/gitlab-styles/-/blob/master/.gitlab/merge_request_templates/Release.md).
|
100
93
|
1. Follow the instructions.
|
101
|
-
1.
|
94
|
+
1. (Optional, but appreciated) Create an MR on `gitlab-org/gitlab` project [with the `New Version of gitlab-styles.md` template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/merge_request_templates/New%20Version%20of%20gitlab-styles.md) to test the new version of `gitlab-styles`, and follow the MR instructions.
|
95
|
+
1. After the Merge Request has been merged, a new gem version is [published automatically](https://gitlab.com/gitlab-org/quality/pipeline-common/-/blob/master/ci/gem-release.yml)
|
102
96
|
|
103
97
|
See [!123](https://gitlab.com/gitlab-org/ruby/gems/gitlab-styles/-/merge_requests/123) as an example.
|
104
98
|
|
99
|
+
## Contributing
|
100
|
+
|
101
|
+
Bug reports and merge requests are welcome on GitLab at
|
102
|
+
https://gitlab.com/gitlab-org/gitlab-styles. This project is intended to be a
|
103
|
+
safe, welcoming space for collaboration, and contributors are expected to adhere
|
104
|
+
to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
105
|
+
|
105
106
|
## License
|
106
107
|
|
107
108
|
The gem is available as open source under the terms of the
|
data/gitlab-styles.gemspec
CHANGED
@@ -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.
|
26
|
-
spec.add_dependency 'rubocop-
|
27
|
-
spec.add_dependency 'rubocop-
|
28
|
-
spec.add_dependency 'rubocop-
|
29
|
-
spec.add_dependency 'rubocop-
|
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', '~>
|
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
|
13
|
+
# Run only relevant specs.
|
14
14
|
rspec:
|
15
|
-
|
16
|
-
|
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
|
@@ -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
|
-
|
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|
|
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
|
75
|
-
|
74
|
+
def allowed?(block_node)
|
75
|
+
ALLOWED_METHODS.include?(block_node.method_name)
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
@@ -6,31 +6,29 @@ module Rubocop
|
|
6
6
|
# `Gemfile` in order to avoid additional points of failure beyond
|
7
7
|
# rubygems.org.
|
8
8
|
class GemFetcher < RuboCop::Cop::Base
|
9
|
-
MSG = 'Do not use gems from git repositories, only use gems from RubyGems.'
|
9
|
+
MSG = 'Do not use gems from git repositories, only use gems from RubyGems or vendored gems. ' \
|
10
|
+
'See https://docs.gitlab.com/ee/development/gemfile.html#no-gems-fetched-from-git-repositories'
|
10
11
|
|
11
|
-
|
12
|
+
# See https://bundler.io/guides/git.html#custom-git-sources
|
13
|
+
GIT_SOURCES = %i[git github gist bitbucket].freeze
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
# @!method gem_option(node)
|
16
|
+
def_node_matcher :gem_option, <<~PATTERN
|
17
|
+
(send nil? :gem _ ...
|
18
|
+
(hash
|
19
|
+
<$(pair (sym {#{GIT_SOURCES.map(&:inspect).join(' ')}}) _)
|
20
|
+
...>
|
21
|
+
)
|
22
|
+
)
|
23
|
+
PATTERN
|
18
24
|
|
19
|
-
|
20
|
-
key_name = pair.children[0].children[0].to_sym
|
21
|
-
add_offense(pair.source_range) if GIT_KEYS.include?(key_name)
|
22
|
-
end
|
23
|
-
end
|
25
|
+
RESTRICT_ON_SEND = %i[gem].freeze
|
24
26
|
|
25
|
-
|
27
|
+
def on_send(node)
|
28
|
+
pair_node = gem_option(node)
|
29
|
+
return unless pair_node
|
26
30
|
|
27
|
-
|
28
|
-
node
|
29
|
-
.location
|
30
|
-
.expression
|
31
|
-
.source_buffer
|
32
|
-
.name
|
33
|
-
.end_with?("Gemfile")
|
31
|
+
add_offense(pair_node)
|
34
32
|
end
|
35
33
|
end
|
36
34
|
end
|
@@ -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
|