rubocop-github 0.13.0 → 0.16.2

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.
@@ -0,0 +1,7 @@
1
+ inherit_from: _rails_shared.yml
2
+
3
+ Rails:
4
+ Enabled: true
5
+
6
+ Rails/FindEach:
7
+ Enabled: false
@@ -0,0 +1,4 @@
1
+ inherit_from: _rails_shared.yml
2
+
3
+ require:
4
+ - rubocop-rails
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rubocop/cop/github/insecure_hash_algorithm"
3
4
  require "rubocop/cop/github/rails_application_record"
4
5
  require "rubocop/cop/github/rails_controller_render_action_symbol"
5
6
  require "rubocop/cop/github/rails_controller_render_literal"
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubocop"
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module GitHub
8
+ class InsecureHashAlgorithm < Cop
9
+ MSG = "This hash function is not allowed"
10
+ UUID_V3_MSG = "uuid_v3 uses MD5, which is not allowed"
11
+ UUID_V5_MSG = "uuid_v5 uses SHA1, which is not allowed"
12
+
13
+ # Matches constants like these:
14
+ # Digest::MD5
15
+ # OpenSSL::Digest::MD5
16
+ def_node_matcher :insecure_const?, <<-PATTERN
17
+ (const (const _ :Digest) #insecure_algorithm?)
18
+ PATTERN
19
+
20
+ # Matches calls like these:
21
+ # Digest.new('md5')
22
+ # Digest.hexdigest('md5', 'str')
23
+ # OpenSSL::Digest.new('md5')
24
+ # OpenSSL::Digest.hexdigest('md5', 'str')
25
+ # OpenSSL::Digest::Digest.new('md5')
26
+ # OpenSSL::Digest::Digest.hexdigest('md5', 'str')
27
+ # OpenSSL::Digest::Digest.new(:MD5)
28
+ # OpenSSL::Digest::Digest.hexdigest(:MD5, 'str')
29
+ def_node_matcher :insecure_digest?, <<-PATTERN
30
+ (send
31
+ (const _ {:Digest :HMAC})
32
+ #not_just_encoding?
33
+ #insecure_algorithm?
34
+ ...)
35
+ PATTERN
36
+
37
+ # Matches calls like "Digest(:MD5)".
38
+ def_node_matcher :insecure_hash_lookup?, <<-PATTERN
39
+ (send _ :Digest #insecure_algorithm?)
40
+ PATTERN
41
+
42
+ # Matches calls like "OpenSSL::HMAC.new(secret, hash)"
43
+ def_node_matcher :openssl_hmac_new?, <<-PATTERN
44
+ (send (const (const _ :OpenSSL) :HMAC) :new ...)
45
+ PATTERN
46
+
47
+ # Matches calls like "OpenSSL::HMAC.new(secret, 'sha1')"
48
+ def_node_matcher :openssl_hmac_new_insecure?, <<-PATTERN
49
+ (send (const (const _ :OpenSSL) :HMAC) :new _ #insecure_algorithm?)
50
+ PATTERN
51
+
52
+ # Matches Rails's Digest::UUID.
53
+ def_node_matcher :digest_uuid?, <<-PATTERN
54
+ (const (const _ :Digest) :UUID)
55
+ PATTERN
56
+
57
+ def_node_matcher :uuid_v3?, <<-PATTERN
58
+ (send (const _ :UUID) :uuid_v3 ...)
59
+ PATTERN
60
+
61
+ def_node_matcher :uuid_v5?, <<-PATTERN
62
+ (send (const _ :UUID) :uuid_v5 ...)
63
+ PATTERN
64
+
65
+ def insecure_algorithm?(val)
66
+ return false if val == :Digest # Don't match "Digest::Digest".
67
+ case alg_name(val)
68
+ when *allowed_hash_functions
69
+ false
70
+ when Symbol
71
+ # can't figure this one out, it's nil or a var or const.
72
+ false
73
+ else
74
+ true
75
+ end
76
+ end
77
+
78
+ def not_just_encoding?(val)
79
+ !just_encoding?(val)
80
+ end
81
+
82
+ def just_encoding?(val)
83
+ val == :hexencode || val == :bubblebabble
84
+ end
85
+
86
+ # Built-in hash functions are listed in these docs:
87
+ # https://ruby-doc.org/stdlib-2.7.0/libdoc/digest/rdoc/Digest.html
88
+ # https://ruby-doc.org/stdlib-2.7.0/libdoc/openssl/rdoc/OpenSSL/Digest.html
89
+ DEFAULT_ALLOWED = %w[
90
+ SHA256
91
+ SHA384
92
+ SHA512
93
+ ].freeze
94
+
95
+ def allowed_hash_functions
96
+ @allowed_algorithms ||= cop_config.fetch("Allowed", DEFAULT_ALLOWED).map(&:downcase)
97
+ end
98
+
99
+ def alg_name(val)
100
+ return :nil if val.nil?
101
+ return val.to_s.downcase unless val.is_a?(RuboCop::AST::Node)
102
+ case val.type
103
+ when :sym, :str
104
+ val.children.first.to_s.downcase
105
+ else
106
+ val.type
107
+ end
108
+ end
109
+
110
+ def on_const(const_node)
111
+ if insecure_const?(const_node) && !digest_uuid?(const_node)
112
+ add_offense(const_node, message: MSG)
113
+ end
114
+ end
115
+
116
+ def on_send(send_node)
117
+ case
118
+ when uuid_v3?(send_node)
119
+ unless allowed_hash_functions.include?("md5")
120
+ add_offense(send_node, message: UUID_V3_MSG)
121
+ end
122
+ when uuid_v5?(send_node)
123
+ unless allowed_hash_functions.include?("sha1")
124
+ add_offense(send_node, message: UUID_V5_MSG)
125
+ end
126
+ when openssl_hmac_new?(send_node)
127
+ if openssl_hmac_new_insecure?(send_node)
128
+ add_offense(send_node, message: MSG)
129
+ end
130
+ when insecure_digest?(send_node)
131
+ add_offense(send_node, message: MSG)
132
+ when insecure_hash_lookup?(send_node)
133
+ add_offense(send_node, message: MSG)
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -9,11 +9,11 @@ module RuboCop
9
9
  MSG = "Prefer `render` with string instead of symbol"
10
10
 
11
11
  def_node_matcher :render_sym?, <<-PATTERN
12
- (send nil? :render $(sym _))
12
+ (send nil? {:render :render_to_string} $(sym _))
13
13
  PATTERN
14
14
 
15
15
  def_node_matcher :render_with_options?, <<-PATTERN
16
- (send nil? :render (hash $...))
16
+ (send nil? {:render :render_to_string} (hash $...))
17
17
  PATTERN
18
18
 
19
19
  def_node_matcher :action_key?, <<-PATTERN
@@ -1,32 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rubocop"
4
+ require "rubocop/cop/github/render_literal_helpers"
4
5
 
5
6
  module RuboCop
6
7
  module Cop
7
8
  module GitHub
8
9
  class RailsControllerRenderLiteral < Cop
9
- MSG = "render must be used with a string literal"
10
+ include RenderLiteralHelpers
10
11
 
11
- def_node_matcher :literal?, <<-PATTERN
12
- ({str sym true false nil?} ...)
13
- PATTERN
14
-
15
- def_node_matcher :render?, <<-PATTERN
16
- (send nil? :render ...)
17
- PATTERN
18
-
19
- def_node_matcher :render_literal?, <<-PATTERN
20
- (send nil? :render ({str sym} $_) $...)
21
- PATTERN
22
-
23
- def_node_matcher :render_const?, <<-PATTERN
24
- (send nil? :render (const _ _) ...)
25
- PATTERN
26
-
27
- def_node_matcher :render_with_options?, <<-PATTERN
28
- (send nil? :render (hash $...))
29
- PATTERN
12
+ MSG = "render must be used with a string literal or an instance of a Class"
30
13
 
31
14
  def_node_matcher :ignore_key?, <<-PATTERN
32
15
  (pair (sym {
@@ -64,10 +47,16 @@ module RuboCop
64
47
  }) ...)
65
48
  PATTERN
66
49
 
50
+ def_node_matcher :render_const?, <<-PATTERN
51
+ (send nil? {:render :render_to_string} (const _ _) ...)
52
+ PATTERN
53
+
67
54
  def on_send(node)
68
55
  return unless render?(node)
69
56
 
70
- if render_literal?(node) || render_const?(node)
57
+ return if render_view_component?(node) || render_const?(node)
58
+
59
+ if render_literal?(node)
71
60
  elsif option_pairs = render_with_options?(node)
72
61
  option_pairs = option_pairs.reject { |pair| options_key?(pair) }
73
62
 
@@ -78,18 +67,40 @@ module RuboCop
78
67
  if template_node = option_pairs.map { |pair| template_key?(pair) }.compact.first
79
68
  if !literal?(template_node)
80
69
  add_offense(node, location: :expression)
70
+ return
81
71
  end
82
72
  else
83
73
  add_offense(node, location: :expression)
74
+ return
84
75
  end
85
76
 
86
77
  if layout_node = option_pairs.map { |pair| layout_key?(pair) }.compact.first
87
78
  if !literal?(layout_node)
88
79
  add_offense(node, location: :expression)
80
+ return
89
81
  end
90
82
  end
91
83
  else
92
84
  add_offense(node, location: :expression)
85
+ return
86
+ end
87
+
88
+ if render_literal?(node)
89
+ option_hash = node.arguments[1]
90
+ if option_hash && !option_hash.hash_type?
91
+ add_offense(node, location: :expression)
92
+ return
93
+ end
94
+ option_pairs = option_hash && option_hash.pairs
95
+ else
96
+ option_pairs = node.arguments[0].pairs
97
+ end
98
+
99
+ if option_pairs
100
+ locals = option_pairs.map { |pair| locals_key?(pair) }.compact.first
101
+ if locals && (!locals.hash_type? || !hash_with_literal_keys?(locals))
102
+ add_offense(node, location: :expression)
103
+ end
93
104
  end
94
105
  end
95
106
  end
@@ -7,15 +7,15 @@ module RuboCop
7
7
  module GitHub
8
8
  class RailsControllerRenderPathsExist < Cop
9
9
  def_node_matcher :render?, <<-PATTERN
10
- (send nil? :render $...)
10
+ (send nil? {:render :render_to_string} $...)
11
11
  PATTERN
12
12
 
13
13
  def_node_matcher :render_str?, <<-PATTERN
14
- (send nil? :render $({str sym} $_) ...)
14
+ (send nil? {:render :render_to_string} $({str sym} $_) ...)
15
15
  PATTERN
16
16
 
17
17
  def_node_matcher :render_options?, <<-PATTERN
18
- (send nil? :render (hash $...))
18
+ (send nil? {:render :render_to_string} (hash $...))
19
19
  PATTERN
20
20
 
21
21
  def_node_matcher :render_key?, <<-PATTERN
@@ -9,7 +9,7 @@ module RuboCop
9
9
  MSG = "Prefer `render` template shorthand"
10
10
 
11
11
  def_node_matcher :render_with_options?, <<-PATTERN
12
- (send nil? :render (hash $...))
12
+ (send nil? {:render :render_to_string} (hash $...))
13
13
  PATTERN
14
14
 
15
15
  def_node_matcher :action_key?, <<-PATTERN
@@ -9,7 +9,7 @@ module RuboCop
9
9
  MSG = "Avoid `render inline:`"
10
10
 
11
11
  def_node_matcher :render_with_options?, <<-PATTERN
12
- (send nil? :render (hash $...))
12
+ (send nil? {:render :render_to_string} (hash $...))
13
13
  PATTERN
14
14
 
15
15
  def_node_matcher :inline_key?, <<-PATTERN
@@ -9,7 +9,7 @@ module RuboCop
9
9
  MSG = "Avoid `render object:`"
10
10
 
11
11
  def_node_matcher :render_with_options?, <<-PATTERN
12
- (send nil? :render (hash $...) ...)
12
+ (send nil? {:render :render_to_string} (hash $...) ...)
13
13
  PATTERN
14
14
 
15
15
  def_node_matcher :partial_key?, <<-PATTERN
@@ -1,32 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rubocop"
4
+ require "rubocop/cop/github/render_literal_helpers"
4
5
 
5
6
  module RuboCop
6
7
  module Cop
7
8
  module GitHub
8
9
  class RailsViewRenderLiteral < Cop
9
- MSG = "render must be used with a string literal"
10
+ include RenderLiteralHelpers
10
11
 
11
- def_node_matcher :literal?, <<-PATTERN
12
- ({str sym true false nil?} ...)
13
- PATTERN
14
-
15
- def_node_matcher :render?, <<-PATTERN
16
- (send nil? :render $...)
17
- PATTERN
18
-
19
- def_node_matcher :render_literal?, <<-PATTERN
20
- (send nil? :render ({str sym} $_) $...)
21
- PATTERN
22
-
23
- def_node_matcher :render_const?, <<-PATTERN
24
- (send nil? :render (const _ _) ...)
25
- PATTERN
26
-
27
- def_node_matcher :render_with_options?, <<-PATTERN
28
- (send nil? :render (hash $...) ...)
29
- PATTERN
12
+ MSG = "render must be used with a literal template and use literals for locals keys"
30
13
 
31
14
  def_node_matcher :ignore_key?, <<-PATTERN
32
15
  (pair (sym {
@@ -46,7 +29,10 @@ module RuboCop
46
29
  def on_send(node)
47
30
  return unless render?(node)
48
31
 
49
- if render_literal?(node) || render_const?(node)
32
+ # Ignore "component"-style renders
33
+ return if render_view_component?(node)
34
+
35
+ if render_literal?(node)
50
36
  elsif option_pairs = render_with_options?(node)
51
37
  if option_pairs.any? { |pair| ignore_key?(pair) }
52
38
  return
@@ -55,12 +41,31 @@ module RuboCop
55
41
  if partial_node = option_pairs.map { |pair| partial_key?(pair) }.compact.first
56
42
  if !literal?(partial_node)
57
43
  add_offense(node, location: :expression)
44
+ return
58
45
  end
59
46
  else
60
47
  add_offense(node, location: :expression)
48
+ return
61
49
  end
62
50
  else
63
51
  add_offense(node, location: :expression)
52
+ return
53
+ end
54
+
55
+ if render_literal?(node) && node.arguments.count > 1
56
+ locals = node.arguments[1]
57
+ elsif options_pairs = render_with_options?(node)
58
+ locals = option_pairs.map { |pair| locals_key?(pair) }.compact.first
59
+ end
60
+
61
+ if locals
62
+ if locals.hash_type?
63
+ if !hash_with_literal_keys?(locals)
64
+ add_offense(node, location: :expression)
65
+ end
66
+ else
67
+ add_offense(node, location: :expression)
68
+ end
64
69
  end
65
70
  end
66
71
  end
@@ -7,15 +7,15 @@ module RuboCop
7
7
  module GitHub
8
8
  class RailsViewRenderPathsExist < Cop
9
9
  def_node_matcher :render?, <<-PATTERN
10
- (send nil? :render $...)
10
+ (send nil? {:render :render_to_string} $...)
11
11
  PATTERN
12
12
 
13
13
  def_node_matcher :render_str?, <<-PATTERN
14
- (send nil? :render $(str $_) ...)
14
+ (send nil? {:render :render_to_string} $(str $_) ...)
15
15
  PATTERN
16
16
 
17
17
  def_node_matcher :render_options?, <<-PATTERN
18
- (send nil? :render (hash $...))
18
+ (send nil? {:render :render_to_string} (hash $...))
19
19
  PATTERN
20
20
 
21
21
  def_node_matcher :partial_key?, <<-PATTERN
@@ -9,7 +9,7 @@ module RuboCop
9
9
  MSG = "Prefer `render` partial shorthand"
10
10
 
11
11
  def_node_matcher :render_with_options?, <<-PATTERN
12
- (send nil? :render (hash $...))
12
+ (send nil? {:render :render_to_string} (hash $...))
13
13
  PATTERN
14
14
 
15
15
  def_node_matcher :partial_key?, <<-PATTERN
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ require "rubocop"
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module GitHub
8
+ module RenderLiteralHelpers
9
+ extend NodePattern::Macros
10
+
11
+ def_node_matcher :literal?, <<-PATTERN
12
+ ({str sym true false nil?} ...)
13
+ PATTERN
14
+
15
+ def_node_matcher :render?, <<-PATTERN
16
+ (send nil? {:render :render_to_string} ...)
17
+ PATTERN
18
+
19
+ def_node_matcher :render_literal?, <<-PATTERN
20
+ (send nil? {:render :render_to_string} ({str sym} $_) $...)
21
+ PATTERN
22
+
23
+ def_node_matcher :render_with_options?, <<-PATTERN
24
+ (send nil? {:render :render_to_string} (hash $...) ...)
25
+ PATTERN
26
+
27
+ def_node_matcher :render_view_component_instance?, <<-PATTERN
28
+ (send nil? {:render :render_to_string} (send _ :new ...) ...)
29
+ PATTERN
30
+
31
+ def_node_matcher :render_view_component_instance_with_content?, <<-PATTERN
32
+ (send nil? {:render :render_to_string} (send (send _ :new ...) `:with_content ...))
33
+ PATTERN
34
+
35
+ def_node_matcher :render_view_component_collection?, <<-PATTERN
36
+ (send nil? {:render :render_to_string} (send _ :with_collection ...) ...)
37
+ PATTERN
38
+
39
+ def_node_matcher :locals_key?, <<-PATTERN
40
+ (pair (sym :locals) $_)
41
+ PATTERN
42
+
43
+ def hash_with_literal_keys?(hash)
44
+ hash.pairs.all? { |pair| literal?(pair.key) }
45
+ end
46
+
47
+ def render_view_component?(node)
48
+ render_view_component_instance_with_content?(node) ||
49
+ render_view_component_instance?(node) ||
50
+ render_view_component_collection?(node)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end