rubocop-github 0.13.0 → 0.16.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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