brakeman 5.3.1 → 5.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 828d560cb256d564c8e79fc7222e7c7edea911639a6c4b45699901371d0c750b
4
- data.tar.gz: c9e8791df7f77b1e6a8d3bfe9cf0f5b753325171021fdafac3531f96edd3b7e1
3
+ metadata.gz: af17bb029ba552f60e4ec332399b3557ed24720b42fe005bd3ae057ff35c69ef
4
+ data.tar.gz: 0b1462edf2d398d503109a5bbb5fd05727bfb6d31af7b052a0eb7b463f31c8ce
5
5
  SHA512:
6
- metadata.gz: 8d29d985cdba9407830c4881372dc4c8ba4cd635c1c48c6cfb16c6fc6b0ef7993816460af301cdf5ca51b096bfdf30699286d6d9fffaae79fff042160b481a87
7
- data.tar.gz: 5d1824dba9dfd9661eccc7ed2a2bb9d2fd1c8d80e131bf0f97ab31cebc738f920682fe7204a8b84ee49c884c8730c2425ef20bd15f2b53c3ada1a2332d75779e
6
+ metadata.gz: 4ca3b2e7a76d69ef82bc451dba3383da830e89700565aaa38d0955a2566a15ee1a5e94e5d4e8c98dbb95a5083c97a000030361c2628ad6d39a02f178491eeeff
7
+ data.tar.gz: 6f0b414997abd0aed5bf511680cfc4e33916d18eca44b1505dec0b4d61c8b3ff566564b3c61f2c6952cd619f20f6d5069e268229394db3afd6c731350fdd26a2
data/CHANGES.md CHANGED
@@ -1,3 +1,12 @@
1
+ # 5.4.0 - 2022-11-17
2
+
3
+ * Use relative paths for CodeClimate report format (Mike Poage)
4
+ * Add check for weak RSA key sizes and padding modes
5
+ * Handle multiple values and splats in case/when
6
+ * Ignore more model methods in redirects
7
+ * Add check for absolute paths issue with Pathname
8
+ * Fix `load_rails_defaults` overwriting settings in the Rails application (James Gregory-Monk)
9
+
1
10
  # 5.3.1 - 2022-08-09
2
11
 
3
12
  * Fix version range for CVE-2022-32209
@@ -0,0 +1,48 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckPathname < Brakeman::BaseCheck
4
+ Brakeman::Checks.add self
5
+
6
+ @description = "Check for unexpected Pathname behavior"
7
+
8
+ def run_check
9
+ check_rails_root_join
10
+ check_pathname_join
11
+
12
+ end
13
+
14
+ def check_rails_root_join
15
+ tracker.find_call(target: :'Rails.root', method: :join, nested: true).each do |result|
16
+ check_result result
17
+ end
18
+ end
19
+
20
+ def check_pathname_join
21
+ pathname_methods = [
22
+ :'Pathname.new',
23
+ :'Pathname.getwd',
24
+ :'Pathname.glob',
25
+ :'Pathname.pwd',
26
+ ]
27
+
28
+ tracker.find_call(targets: pathname_methods, method: :join, nested: true).each do |result|
29
+ check_result result
30
+ end
31
+ end
32
+
33
+ def check_result result
34
+ return unless original? result
35
+
36
+ result[:call].each_arg do |arg|
37
+ if match = has_immediate_user_input?(arg)
38
+ warn :result => result,
39
+ :warning_type => "Path Traversal",
40
+ :warning_code => :pathname_traversal,
41
+ :message => "Absolute paths in `Pathname#join` cause the entire path to be relative to the absolute path, ignoring any prior values",
42
+ :user_input => match,
43
+ :confidence => :high,
44
+ :cwe_id => [22]
45
+ end
46
+ end
47
+ end
48
+ end
@@ -13,7 +13,7 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
13
13
  def run_check
14
14
  Brakeman.debug "Finding calls to redirect_to()"
15
15
 
16
- @model_find_calls = Set[:all, :create, :create!, :find, :find_by_sql, :first, :last, :new]
16
+ @model_find_calls = Set[:all, :create, :create!, :find, :find_by_sql, :first, :first!, :last, :last!, :new, :sole]
17
17
 
18
18
  if tracker.options[:rails3]
19
19
  @model_find_calls.merge [:from, :group, :having, :joins, :lock, :order, :reorder, :select, :where]
@@ -23,6 +23,10 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
23
23
  @model_find_calls.merge [:find_by, :find_by!, :take]
24
24
  end
25
25
 
26
+ if version_between? "7.0.0", "9.9.9"
27
+ @model_find_calls << :find_sole_by
28
+ end
29
+
26
30
  @tracker.find_call(:target => false, :method => :redirect_to).each do |res|
27
31
  process_result res
28
32
  end
@@ -0,0 +1,112 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckWeakRSAKey < Brakeman::BaseCheck
4
+ Brakeman::Checks.add self
5
+
6
+ @description = "Checks for weak uses RSA keys"
7
+
8
+ def run_check
9
+ check_rsa_key_creation
10
+ check_rsa_operations
11
+ end
12
+
13
+ def check_rsa_key_creation
14
+ tracker.find_call(targets: [:'OpenSSL::PKey::RSA'], method: [:new, :generate], nested: true).each do |result|
15
+ key_size_arg = result[:call].first_arg
16
+ check_key_size(result, key_size_arg)
17
+ end
18
+
19
+ tracker.find_call(targets: [:'OpenSSL::PKey'], method: [:generate_key], nested: true).each do |result|
20
+ call = result[:call]
21
+ key_type = call.first_arg
22
+ options_arg = call.second_arg
23
+
24
+ next unless options_arg and hash? options_arg
25
+
26
+ if string? key_type and key_type.value.upcase == 'RSA'
27
+ key_size_arg = (hash_access(options_arg, :rsa_keygen_bits) || hash_access(options_arg, s(:str, 'rsa_key_gen_bits')))
28
+ check_key_size(result, key_size_arg)
29
+ end
30
+ end
31
+ end
32
+
33
+ def check_rsa_operations
34
+ tracker.find_call(targets: [:'OpenSSL::PKey::RSA.new'], methods: [:public_encrypt, :public_decrypt, :private_encrypt, :private_decrypt], nested: true).each do |result|
35
+ padding_arg = result[:call].second_arg
36
+ check_padding(result, padding_arg)
37
+ end
38
+
39
+ tracker.find_call(targets: [:'OpenSSL::PKey.generate_key'], methods: [:encrypt, :decrypt, :sign, :verify, :sign_raw, :verify_raw], nested: true).each do |result|
40
+ call = result[:call]
41
+ options_arg = call.last_arg
42
+
43
+ if options_arg and hash? options_arg
44
+ padding_arg = (hash_access(options_arg, :rsa_padding_mode) || hash_access(options_arg, s(:str, 'rsa_padding_mode')))
45
+ else
46
+ padding_arg = nil
47
+ end
48
+
49
+ check_padding(result, padding_arg)
50
+ end
51
+ end
52
+
53
+ def check_key_size result, key_size_arg
54
+ return unless number? key_size_arg
55
+ return unless original? result
56
+
57
+ key_size = key_size_arg.value
58
+
59
+ if key_size < 1024
60
+ confidence = :high
61
+ message = msg("RSA key with size ", msg_code(key_size.to_s), " is considered very weak. Use at least 2048 bit key size")
62
+ elsif key_size < 2048
63
+ confidence = :medium
64
+ message = msg("RSA key with size ", msg_code(key_size.to_s), " is considered weak. Use at least 2048 bit key size")
65
+ else
66
+ return
67
+ end
68
+
69
+ warn result: result,
70
+ warning_type: "Weak Cryptography",
71
+ warning_code: :small_rsa_key_size,
72
+ message: message,
73
+ confidence: confidence,
74
+ user_input: key_size_arg,
75
+ cwe_id: [326]
76
+ end
77
+
78
+ PKCS1_PADDING = s(:colon2, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :PKCS1_PADDING).freeze
79
+ PKCS1_PADDING_STR = s(:str, 'pkcs1').freeze
80
+ SSLV23_PADDING = s(:colon2, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :SSLV23_PADDING).freeze
81
+ SSLV23_PADDING_STR = s(:str, 'sslv23').freeze
82
+ NO_PADDING = s(:colon2, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :NO_PADDING).freeze
83
+ NO_PADDING_STR = s(:str, 'none').freeze
84
+
85
+ def check_padding result, padding_arg
86
+ return unless original? result
87
+
88
+ if string? padding_arg
89
+ padding_arg = padding_arg.deep_clone(padding_arg.line)
90
+ padding_arg.value.downcase!
91
+ end
92
+
93
+ case padding_arg
94
+ when PKCS1_PADDING, PKCS1_PADDING_STR, nil
95
+ message = "Use of padding mode PKCS1 (default if not specified), which is known to be insecure. Use OAEP instead"
96
+ when SSLV23_PADDING, SSLV23_PADDING_STR
97
+ message = "Use of padding mode SSLV23 for RSA key, which is only useful for outdated versions of SSL. Use OAEP instead"
98
+ when NO_PADDING, NO_PADDING_STR
99
+ message = "No padding mode used for RSA key. A safe padding mode (OAEP) should be specified for RSA keys"
100
+ else
101
+ return
102
+ end
103
+
104
+ warn result: result,
105
+ warning_type: "Weak Cryptography",
106
+ warning_code: :insecure_rsa_padding_mode,
107
+ message: message,
108
+ confidence: :high,
109
+ user_input: padding_arg,
110
+ cwe_id: [780]
111
+ end
112
+ end
@@ -970,11 +970,27 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
970
970
  exp.method == :==
971
971
  end
972
972
 
973
+ # Not a list of values
974
+ # when :example
973
975
  def simple_when? exp
974
976
  node_type? exp[1], :array and
975
- not node_type? exp[1][1], :splat, :array and
976
- (exp[1].length == 2 or
977
- exp[1].all? { |e| e.is_a? Symbol or node_type? e, :lit, :str })
977
+ exp[1].length == 2 and # only one element in the array
978
+ not node_type? exp[1][1], :splat, :array
979
+ end
980
+
981
+ # A list of literal values
982
+ #
983
+ # when 1,2,3
984
+ #
985
+ # or
986
+ #
987
+ # when *[:a, :b]
988
+ def all_literals_when? exp
989
+ if array? exp[1] # pretty sure this is always true
990
+ all_literals? exp[1] or # simple list, not actually array
991
+ (splat_array? exp[1][1] and
992
+ all_literals? exp[1][1][1])
993
+ end
978
994
  end
979
995
 
980
996
  def process_case exp
@@ -1002,9 +1018,16 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
1002
1018
  scope do
1003
1019
  @branch_env = env.current
1004
1020
 
1021
+ # Process the when value for matching
1022
+ process_default e[1]
1023
+
1005
1024
  # set value of case var if possible
1006
- if case_value and simple_when? e
1007
- @branch_env[case_value] = e[1][1]
1025
+ if case_value
1026
+ if simple_when? e
1027
+ @branch_env[case_value] = e[1][1]
1028
+ elsif all_literals_when? e
1029
+ @branch_env[case_value] = safe_literal(e.line + 1)
1030
+ end
1008
1031
  end
1009
1032
 
1010
1033
  # when blocks aren't blocks, they are lists of expressions
@@ -86,7 +86,7 @@ class Brakeman::Rails3ConfigProcessor < Brakeman::BasicProcessor
86
86
  end
87
87
  elsif include_rails_config? exp
88
88
  options_path = get_rails_config exp
89
- @tracker.config.set_rails_config(exp.first_arg, *options_path)
89
+ @tracker.config.set_rails_config(value: exp.first_arg, path: options_path, overwrite: true)
90
90
  end
91
91
 
92
92
  exp
@@ -73,7 +73,7 @@ class Brakeman::Report::CodeClimate < Brakeman::Report::Base
73
73
  if tracker.options[:path_prefix]
74
74
  (Pathname.new(tracker.options[:path_prefix]) + Pathname.new(warning.file.relative)).to_s
75
75
  else
76
- warning.file
76
+ warning.relative_path
77
77
  end
78
78
  end
79
79
  end
@@ -166,7 +166,7 @@ module Brakeman
166
166
  # then this will set
167
167
  #
168
168
  # rails[:action_controller][:perform_caching] = value
169
- def set_rails_config value, *path
169
+ def set_rails_config value:, path:, overwrite: false
170
170
  config = self.rails
171
171
 
172
172
  path[0..-2].each do |o|
@@ -182,7 +182,9 @@ module Brakeman
182
182
  config = option
183
183
  end
184
184
 
185
- config[path.last] = value
185
+ if overwrite || config[path.last].nil?
186
+ config[path.last] = value
187
+ end
186
188
  end
187
189
 
188
190
  # Load defaults based on config.load_defaults value
@@ -195,38 +197,38 @@ module Brakeman
195
197
  false_value = Sexp.new(:false)
196
198
 
197
199
  if version >= 5.0
198
- set_rails_config(true_value, :action_controller, :per_form_csrf_tokens)
199
- set_rails_config(true_value, :action_controller, :forgery_protection_origin_check)
200
- set_rails_config(true_value, :active_record, :belongs_to_required_by_default)
200
+ set_rails_config(value: true_value, path: [:action_controller, :per_form_csrf_tokens])
201
+ set_rails_config(value: true_value, path: [:action_controller, :forgery_protection_origin_check])
202
+ set_rails_config(value: true_value, path: [:active_record, :belongs_to_required_by_default])
201
203
  # Note: this may need to be changed, because ssl_options is a Hash
202
- set_rails_config(true_value, :ssl_options, :hsts, :subdomains)
204
+ set_rails_config(value: true_value, path: [:ssl_options, :hsts, :subdomains])
203
205
  end
204
206
 
205
207
  if version >= 5.1
206
- set_rails_config(false_value, :assets, :unknown_asset_fallback)
207
- set_rails_config(true_value, :action_view, :form_with_generates_remote_forms)
208
+ set_rails_config(value: false_value, path: [:assets, :unknown_asset_fallback])
209
+ set_rails_config(value: true_value, path: [:action_view, :form_with_generates_remote_forms])
208
210
  end
209
211
 
210
212
  if version >= 5.2
211
- set_rails_config(true_value, :active_record, :cache_versioning)
212
- set_rails_config(true_value, :action_dispatch, :use_authenticated_cookie_encryption)
213
- set_rails_config(true_value, :active_support, :use_authenticated_message_encryption)
214
- set_rails_config(true_value, :active_support, :use_sha1_digests)
215
- set_rails_config(true_value, :action_controller, :default_protect_from_forgery)
216
- set_rails_config(true_value, :action_view, :form_with_generates_ids)
213
+ set_rails_config(value: true_value, path: [:active_record, :cache_versioning])
214
+ set_rails_config(value: true_value, path: [:action_dispatch, :use_authenticated_cookie_encryption])
215
+ set_rails_config(value: true_value, path: [:active_support, :use_authenticated_message_encryption])
216
+ set_rails_config(value: true_value, path: [:active_support, :use_sha1_digests])
217
+ set_rails_config(value: true_value, path: [:action_controller, :default_protect_from_forgery])
218
+ set_rails_config(value: true_value, path: [:action_view, :form_with_generates_ids])
217
219
  end
218
220
 
219
221
  if version >= 6.0
220
- set_rails_config(Sexp.new(:lit, :zeitwerk), :autoloader)
221
- set_rails_config(false_value, :action_view, :default_enforce_utf8)
222
- set_rails_config(true_value, :action_dispatch, :use_cookies_with_metadata)
223
- set_rails_config(false_value, :action_dispatch, :return_only_media_type_on_content_type)
224
- set_rails_config(Sexp.new(:str, 'ActionMailer::MailDeliveryJob'), :action_mailer, :delivery_job)
225
- set_rails_config(true_value, :active_job, :return_false_on_aborted_enqueue)
226
- set_rails_config(Sexp.new(:lit, :active_storage_analysis), :active_storage, :queues, :analysis)
227
- set_rails_config(Sexp.new(:lit, :active_storage_purge), :active_storage, :queues, :purge)
228
- set_rails_config(true_value, :active_storage, :replace_on_assign_to_many)
229
- set_rails_config(true_value, :active_record, :collection_cache_versioning)
222
+ set_rails_config(value: Sexp.new(:lit, :zeitwerk), path: [:autoloader])
223
+ set_rails_config(value: false_value, path: [:action_view, :default_enforce_utf8])
224
+ set_rails_config(value: true_value, path: [:action_dispatch, :use_cookies_with_metadata])
225
+ set_rails_config(value: false_value, path: [:action_dispatch, :return_only_media_type_on_content_type])
226
+ set_rails_config(value: Sexp.new(:str, 'ActionMailer::MailDeliveryJob'), path: [:action_mailer, :delivery_job])
227
+ set_rails_config(value: true_value, path: [:active_job, :return_false_on_aborted_enqueue])
228
+ set_rails_config(value: Sexp.new(:lit, :active_storage_analysis), path: [:active_storage, :queues, :analysis])
229
+ set_rails_config(value: Sexp.new(:lit, :active_storage_purge), path: [:active_storage, :queues, :purge])
230
+ set_rails_config(value: true_value, path: [:active_storage, :replace_on_assign_to_many])
231
+ set_rails_config(value: true_value, path: [:active_record, :collection_cache_versioning])
230
232
  end
231
233
  end
232
234
  end
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "5.3.1"
2
+ Version = "5.4.0"
3
3
  end
@@ -126,6 +126,10 @@ module Brakeman::WarningCodes
126
126
  :pending_eol_rails => 122,
127
127
  :pending_eol_ruby => 123,
128
128
  :CVE_2022_32209 => 124,
129
+ :pathname_traversal => 125,
130
+ :insecure_rsa_padding_mode => 126,
131
+ :missing_rsa_padding_mode => 127,
132
+ :small_rsa_key_size => 128,
129
133
 
130
134
  :custom_check => 9090,
131
135
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brakeman
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.3.1
4
+ version: 5.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Collins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-10 00:00:00.000000000 Z
11
+ date: 2022-11-18 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Brakeman detects security vulnerabilities in Ruby on Rails applications
14
14
  via static analysis.
@@ -482,6 +482,7 @@ files:
482
482
  - lib/brakeman/checks/check_nested_attributes_bypass.rb
483
483
  - lib/brakeman/checks/check_number_to_currency.rb
484
484
  - lib/brakeman/checks/check_page_caching_cve.rb
485
+ - lib/brakeman/checks/check_pathname.rb
485
486
  - lib/brakeman/checks/check_permit_attributes.rb
486
487
  - lib/brakeman/checks/check_quote_table_name.rb
487
488
  - lib/brakeman/checks/check_redirect.rb
@@ -520,6 +521,7 @@ files:
520
521
  - lib/brakeman/checks/check_validation_regex.rb
521
522
  - lib/brakeman/checks/check_verb_confusion.rb
522
523
  - lib/brakeman/checks/check_weak_hash.rb
524
+ - lib/brakeman/checks/check_weak_rsa_key.rb
523
525
  - lib/brakeman/checks/check_without_protection.rb
524
526
  - lib/brakeman/checks/check_xml_dos.rb
525
527
  - lib/brakeman/checks/check_yaml_parsing.rb
@@ -646,7 +648,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
646
648
  - !ruby/object:Gem::Version
647
649
  version: '0'
648
650
  requirements: []
649
- rubygems_version: 3.1.2
651
+ rubygems_version: 3.1.6
650
652
  signing_key:
651
653
  specification_version: 4
652
654
  summary: Security vulnerability scanner for Ruby on Rails.