brakeman-min 4.8.1 → 5.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +41 -0
  3. data/README.md +6 -4
  4. data/lib/brakeman.rb +26 -0
  5. data/lib/brakeman/app_tree.rb +36 -3
  6. data/lib/brakeman/checks/base_check.rb +1 -1
  7. data/lib/brakeman/checks/check_basic_auth.rb +2 -0
  8. data/lib/brakeman/checks/check_csrf_token_forgery_cve.rb +28 -0
  9. data/lib/brakeman/checks/check_deserialize.rb +21 -1
  10. data/lib/brakeman/checks/check_json_entity_escape.rb +38 -0
  11. data/lib/brakeman/checks/check_mass_assignment.rb +19 -4
  12. data/lib/brakeman/checks/check_model_attr_accessible.rb +1 -1
  13. data/lib/brakeman/checks/check_model_attributes.rb +1 -1
  14. data/lib/brakeman/checks/check_page_caching_cve.rb +37 -0
  15. data/lib/brakeman/checks/check_permit_attributes.rb +1 -1
  16. data/lib/brakeman/checks/check_skip_before_filter.rb +4 -4
  17. data/lib/brakeman/checks/check_sql.rb +1 -1
  18. data/lib/brakeman/checks/check_template_injection.rb +32 -0
  19. data/lib/brakeman/checks/check_unsafe_reflection_methods.rb +68 -0
  20. data/lib/brakeman/checks/check_verb_confusion.rb +75 -0
  21. data/lib/brakeman/commandline.rb +25 -1
  22. data/lib/brakeman/file_parser.rb +19 -18
  23. data/lib/brakeman/options.rb +25 -1
  24. data/lib/brakeman/parsers/template_parser.rb +2 -3
  25. data/lib/brakeman/processors/alias_processor.rb +2 -3
  26. data/lib/brakeman/processors/haml_template_processor.rb +8 -1
  27. data/lib/brakeman/processors/lib/call_conversion_helper.rb +1 -1
  28. data/lib/brakeman/processors/lib/file_type_detector.rb +64 -0
  29. data/lib/brakeman/processors/lib/find_all_calls.rb +27 -12
  30. data/lib/brakeman/report.rb +15 -0
  31. data/lib/brakeman/report/ignore/config.rb +4 -0
  32. data/lib/brakeman/report/report_sarif.rb +114 -0
  33. data/lib/brakeman/report/report_sonar.rb +38 -0
  34. data/lib/brakeman/report/report_text.rb +37 -16
  35. data/lib/brakeman/rescanner.rb +7 -5
  36. data/lib/brakeman/scanner.rb +46 -19
  37. data/lib/brakeman/tracker.rb +9 -1
  38. data/lib/brakeman/tracker/config.rb +6 -4
  39. data/lib/brakeman/tracker/constants.rb +8 -7
  40. data/lib/brakeman/util.rb +23 -2
  41. data/lib/brakeman/version.rb +1 -1
  42. data/lib/brakeman/warning_codes.rb +8 -0
  43. metadata +29 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 92ecc405f5d8aa44662d99820f962df8e9b2fe6391837b06639511c7cb7a24bc
4
- data.tar.gz: e991cb7e2732104d3859973aedad74ddce99157f5143c0799c90c2c489289e44
3
+ metadata.gz: ef344d29a0b41b77d931c49ce79ad88e4f17c4e69c51de28a850016029691d35
4
+ data.tar.gz: b2c7ddb17560b73213f8923e99c8dc96a37be8cd7697bf417d06f66368bfdf98
5
5
  SHA512:
6
- metadata.gz: 6fc22f32bfead785a7fe0b5ac06289b323b2a047528f4d1da238b8e0d233ecfabb37a869a51c72698974d9b15e7818d47ef2fafc8dd57fc578bce46c3f3a29b3
7
- data.tar.gz: 5b96b57238d6e2813dbd83744c32ba546e116e205d0b3546bc52010621394ee7c8f012c258155aed021010e535c8f8ee706108059f7c8ef54019a66a25658d03
6
+ metadata.gz: 7eb5af1fffd3fe230ddf3ea70d826a3eaf82cfc43090cde0b452f24f3382e2c6b1bfcfc84b997b3f4c0bb8088f635965e5297c78fd17a21e87d9aa11dc115d3e
7
+ data.tar.gz: 6edc3a49f3426c0a215afa6a99ddaf5d531a514df23bd51101f13b800b28640a6b0384f8343c26748e1fe1476159e5a229f904e0728784eb4648de85d22d9be8
data/CHANGES.md CHANGED
@@ -1,3 +1,44 @@
1
+ # 5.0.0.pre1 - 2020-11-17
2
+
3
+ * Add check for (more) unsafe method reflection
4
+ * Suggest using `--force` if no Rails application is detected
5
+ * Add Sonarqube report format (Adam England)
6
+ * Add check for potential HTTP verb confusion
7
+ * Add `--[no-]skip-vendor` option
8
+ * Scan (almost) all Ruby files in project
9
+ * Add support for Haml 5.2.0
10
+
11
+ # 4.10.0 - 2020-09-28
12
+
13
+ * Add SARIF report format (Steve Winton)
14
+
15
+ # 4.9.1 - 2020-09-04
16
+
17
+ * Check `chomp`ed strings for SQL injection
18
+ * Use version from `active_record` for non-Rails apps (Ulysse Buonomo)
19
+ * Always set line number for joined arrays
20
+ * Avoid warning about missing `attr_accessible` if `protected_attributes` gem is used
21
+
22
+ # 4.9.0 - 2020-08-04
23
+
24
+ * Add check for CVE-2020-8166 (Jamie Finnigan)
25
+ * Avoid warning when `safe_yaml` is used via `YAML.load(..., safe: true)`
26
+ * Add check for user input in `ERB.new` (Matt Hickman)
27
+ * Add `--ensure-ignore-notes` (Eli Block)
28
+ * Remove whitelist/blacklist language, add clarifications
29
+ * Do not warn about mass assignment with `params.permit!.slice`
30
+ * Add "full call" information to call index results
31
+ * Ignore `params.permit!` in path helpers
32
+ * Treat `Dir.glob` as safe source of values in guards
33
+ * Always scan `environment.rb`
34
+
35
+ # 4.8.2 - 2020-05-12
36
+
37
+ * Add check for CVE-2020-8159
38
+ * Fix `authenticate_or_request_with_http_basic` check for passed blocks (Hugo Corbucci)
39
+ * Add `--text-fields` option
40
+ * Add check for escaping HTML entities in JSON configuration
41
+
1
42
  # 4.8.1 - 2020-04-06
2
43
 
3
44
  * Check SQL query strings using `String#strip` or `String.squish`
data/README.md CHANGED
@@ -16,9 +16,11 @@ Using RubyGems:
16
16
 
17
17
  Using Bundler:
18
18
 
19
- group :development do
20
- gem 'brakeman'
21
- end
19
+ ```ruby
20
+ group :development do
21
+ gem 'brakeman'
22
+ end
23
+ ```
22
24
 
23
25
  Using Docker:
24
26
 
@@ -74,7 +76,7 @@ To specify an output file for the results:
74
76
 
75
77
  brakeman -o output_file
76
78
 
77
- The output format is determined by the file extension or by using the `-f` option. Current options are: `text`, `html`, `tabs`, `json`, `junit`, `markdown`, `csv`, and `codeclimate`.
79
+ The output format is determined by the file extension or by using the `-f` option. Current options are: `text`, `html`, `tabs`, `json`, `junit`, `markdown`, `csv`, `codeclimate`, and `sonar`.
78
80
 
79
81
  Multiple output files can be specified:
80
82
 
@@ -20,6 +20,10 @@ module Brakeman
20
20
  #option is set
21
21
  Errors_Found_Exit_Code = 7
22
22
 
23
+ #Exit code returned when an ignored warning has no note and
24
+ #--ensure-ignore-notes is set
25
+ Empty_Ignore_Note_Exit_Code = 8
26
+
23
27
  @debug = false
24
28
  @quiet = false
25
29
  @loaded_dependencies = []
@@ -62,6 +66,7 @@ module Brakeman
62
66
  # * :run_checks - array of checks to run (run all if not specified)
63
67
  # * :safe_methods - array of methods to consider safe
64
68
  # * :skip_libs - do not process lib/ directory (default: false)
69
+ # * :skip_vendor - do not process vendor/ directory (default: true)
65
70
  # * :skip_checks - checks not to run (run all if not specified)
66
71
  # * :absolute_paths - show absolute path of each file (default: false)
67
72
  # * :summary_only - only output summary section of report for plain/table (:summary_only, :no_summary, true)
@@ -187,6 +192,7 @@ module Brakeman
187
192
  :report_progress => true,
188
193
  :safe_methods => Set.new,
189
194
  :skip_checks => Set.new,
195
+ :skip_vendor => true,
190
196
  }
191
197
  end
192
198
 
@@ -233,6 +239,10 @@ module Brakeman
233
239
  [:to_table]
234
240
  when :junit, :to_junit
235
241
  [:to_junit]
242
+ when :sarif, :to_sarif
243
+ [:to_sarif]
244
+ when :sonar, :to_sonar
245
+ [:to_sonar]
236
246
  else
237
247
  [:to_text]
238
248
  end
@@ -262,6 +272,10 @@ module Brakeman
262
272
  :to_table
263
273
  when /\.junit$/i
264
274
  :to_junit
275
+ when /\.sarif$/i
276
+ :to_sarif
277
+ when /\.sonar$/i
278
+ :to_sonar
265
279
  else
266
280
  :to_text
267
281
  end
@@ -498,6 +512,18 @@ module Brakeman
498
512
  end
499
513
  end
500
514
 
515
+ # Returns an array of alert fingerprints for any ignored warnings without
516
+ # notes found in the specified ignore file (if it exists).
517
+ def self.ignore_file_entries_with_empty_notes file
518
+ return [] unless file
519
+
520
+ require 'brakeman/report/ignore/config'
521
+
522
+ config = IgnoreConfig.new(file, nil)
523
+ config.read_from_file
524
+ config.already_ignored_entries_with_empty_notes.map { |i| i[:fingerprint] }
525
+ end
526
+
501
527
  def self.filter_warnings tracker, options
502
528
  require 'brakeman/report/ignore/config'
503
529
 
@@ -21,6 +21,7 @@ module Brakeman
21
21
  end
22
22
  init_options[:additional_libs_path] = options[:additional_libs_path]
23
23
  init_options[:engine_paths] = options[:engine_paths]
24
+ init_options[:skip_vendor] = options[:skip_vendor]
24
25
  new(root, init_options)
25
26
  end
26
27
 
@@ -62,6 +63,7 @@ module Brakeman
62
63
  @engine_paths = init_options[:engine_paths] || []
63
64
  @absolute_engine_paths = @engine_paths.select { |path| path.start_with?(File::SEPARATOR) }
64
65
  @relative_engine_paths = @engine_paths - @absolute_engine_paths
66
+ @skip_vendor = init_options[:skip_vendor]
65
67
  @gemspec = nil
66
68
  @root_search_pattern = nil
67
69
  end
@@ -96,6 +98,10 @@ module Brakeman
96
98
  end
97
99
  end
98
100
 
101
+ def ruby_file_paths
102
+ find_paths(".").uniq
103
+ end
104
+
99
105
  def initializer_paths
100
106
  @initializer_paths ||= prioritize_concerns(find_paths("config/initializers"))
101
107
  end
@@ -109,8 +115,8 @@ module Brakeman
109
115
  end
110
116
 
111
117
  def template_paths
112
- @template_paths ||= find_paths("app/**/views", "*.{#{VIEW_EXTENSIONS}}") +
113
- find_paths("app/**/views", "*.{erb,haml,slim}").reject { |path| File.basename(path).count(".") > 1 }
118
+ @template_paths ||= find_paths(".", "*.{#{VIEW_EXTENSIONS}}") +
119
+ find_paths("**", "*.{erb,haml,slim}").reject { |path| File.basename(path).count(".") > 1 }
114
120
  end
115
121
 
116
122
  def layout_exists?(name)
@@ -163,7 +169,8 @@ module Brakeman
163
169
  def select_files(paths)
164
170
  paths = select_only_files(paths)
165
171
  paths = reject_skipped_files(paths)
166
- convert_to_file_paths(paths)
172
+ paths = convert_to_file_paths(paths)
173
+ reject_global_excludes(paths)
167
174
  end
168
175
 
169
176
  def select_only_files(paths)
@@ -182,6 +189,32 @@ module Brakeman
182
189
  end
183
190
  end
184
191
 
192
+ EXCLUDED_PATHS = %w[
193
+ /generators/
194
+ lib/tasks/
195
+ lib/templates/
196
+ db/
197
+ spec/
198
+ test/
199
+ tmp/
200
+ public/
201
+ log/
202
+ ]
203
+
204
+ def reject_global_excludes(paths)
205
+ paths.reject do |path|
206
+ relative_path = path.relative
207
+
208
+ if @skip_vendor and relative_path.include? 'vendor/'
209
+ true
210
+ else
211
+ EXCLUDED_PATHS.any? do |excluded|
212
+ relative_path.include? excluded
213
+ end
214
+ end
215
+ end
216
+ end
217
+
185
218
  def match_path files, path
186
219
  absolute_path = Pathname.new(path)
187
220
  # relative root never has a leading separator. But, we use a leading
@@ -467,7 +467,7 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
467
467
  end
468
468
 
469
469
  def gemfile_or_environment gem_name = :rails
470
- if gem_name and info = tracker.config.get_gem(gem_name)
470
+ if gem_name and info = tracker.config.get_gem(gem_name.to_sym)
471
471
  info
472
472
  elsif @app_tree.exists?("Gemfile")
473
473
  @app_tree.file_path "Gemfile"
@@ -57,6 +57,8 @@ class Brakeman::CheckBasicAuth < Brakeman::BaseCheck
57
57
 
58
58
  # Check if the block of a result contains a comparison of password to string
59
59
  def include_password_literal? result
60
+ return false if result[:block_args].nil?
61
+
60
62
  @password_var = result[:block_args].last
61
63
  @include_password = false
62
64
  process result[:block]
@@ -0,0 +1,28 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckCSRFTokenForgeryCVE < Brakeman::BaseCheck
4
+ Brakeman::Checks.add self
5
+
6
+ @description = "Checks for versions with CSRF token forgery vulnerability (CVE-2020-8166)"
7
+
8
+ def run_check
9
+ fix_version = case
10
+ when version_between?('0.0.0', '5.2.4.2')
11
+ '5.2.4.3'
12
+ when version_between?('6.0.0', '6.0.3')
13
+ '6.0.3.1'
14
+ else
15
+ nil
16
+ end
17
+
18
+ if fix_version
19
+ warn :warning_type => "Cross-Site Request Forgery",
20
+ :warning_code => :CVE_2020_8166,
21
+ :message => msg(msg_version(rails_version), " has a vulnerability that may allow CSRF token forgery. Upgrade to ", msg_version(fix_version), " or patch"),
22
+ :confidence => :medium,
23
+ :gem_info => gemfile_or_environment,
24
+ :link => "https://groups.google.com/g/rubyonrails-security/c/NOjKiGeXUgw"
25
+ end
26
+ end
27
+ end
28
+
@@ -13,7 +13,23 @@ class Brakeman::CheckDeserialize < Brakeman::BaseCheck
13
13
  end
14
14
 
15
15
  def check_yaml
16
- check_methods :YAML, :load, :load_documents, :load_stream, :parse_documents, :parse_stream
16
+ check_methods :YAML, :load_documents, :load_stream, :parse_documents, :parse_stream
17
+
18
+ # Check for safe_yaml gem use with YAML.load(..., safe: true)
19
+ if uses_safe_yaml?
20
+ tracker.find_call(target: :YAML, method: :load).each do |result|
21
+ call = result[:call]
22
+ options = call.second_arg
23
+
24
+ if hash? options and true? hash_access(options, :safe)
25
+ next
26
+ else
27
+ check_deserialize result, :YAML
28
+ end
29
+ end
30
+ else
31
+ check_methods :YAML, :load
32
+ end
17
33
  end
18
34
 
19
35
  def check_csv
@@ -102,4 +118,8 @@ class Brakeman::CheckDeserialize < Brakeman::BaseCheck
102
118
 
103
119
  false
104
120
  end
121
+
122
+ def uses_safe_yaml?
123
+ tracker.config.has_gem? :safe_yaml
124
+ end
105
125
  end
@@ -0,0 +1,38 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckJSONEntityEscape < Brakeman::BaseCheck
4
+ Brakeman::Checks.add self
5
+
6
+ @description = "Check if HTML escaping is disabled for JSON output"
7
+
8
+ def run_check
9
+ check_config_setting
10
+ check_manual_disable
11
+ end
12
+
13
+ def check_config_setting
14
+ if false? tracker.config.rails.dig(:active_support, :escape_html_entities_in_json)
15
+ warn :warning_type => "Cross-Site Scripting",
16
+ :warning_code => :json_html_escape_config,
17
+ :message => msg("HTML entities in JSON are not escaped by default"),
18
+ :confidence => :medium,
19
+ :file => "config/environments/production.rb",
20
+ :line => 1
21
+ end
22
+ end
23
+
24
+ def check_manual_disable
25
+ tracker.find_call(targets: [:ActiveSupport, :'ActiveSupport::JSON::Encoding'], method: :escape_html_entities_in_json=).each do |result|
26
+ setting = result[:call].first_arg
27
+
28
+ if false? setting
29
+ warn :result => result,
30
+ :warning_type => "Cross-Site Scripting",
31
+ :warning_code => :json_html_escape_module,
32
+ :message => msg("HTML entities in JSON are not escaped by default"),
33
+ :confidence => :medium,
34
+ :file => "config/environments/production.rb"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -160,12 +160,27 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
160
160
  # Look for and warn about uses of Parameters#permit! for mass assignment
161
161
  def check_permit!
162
162
  tracker.find_call(:method => :permit!, :nested => true).each do |result|
163
- if params? result[:call].target and not result[:chain].include? :slice
164
- warn_on_permit! result
163
+ if params? result[:call].target
164
+ unless inside_safe_method? result or calls_slice? result
165
+ warn_on_permit! result
166
+ end
165
167
  end
166
168
  end
167
169
  end
168
170
 
171
+ # Ignore blah_some_path(params.permit!)
172
+ def inside_safe_method? result
173
+ parent_call = result.dig(:parent, :call)
174
+
175
+ call? parent_call and
176
+ parent_call.method.match(/_path$/)
177
+ end
178
+
179
+ def calls_slice? result
180
+ result[:chain].include? :slice or
181
+ (result[:full_call] and result[:full_call][:chain].include? :slice)
182
+ end
183
+
169
184
  # Look for actual use of params in mass assignment to avoid
170
185
  # warning about uses of Parameters#permit! without any mass assignment
171
186
  # or when mass assignment is restricted by model instead.
@@ -191,7 +206,7 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
191
206
  warn :result => result,
192
207
  :warning_type => "Mass Assignment",
193
208
  :warning_code => :mass_assign_permit!,
194
- :message => "Parameters should be whitelisted for mass assignment",
209
+ :message => msg('Specify exact keys allowed for mass assignment instead of using ', msg_code('permit!'), ' which allows any keys'),
195
210
  :confidence => confidence
196
211
  end
197
212
 
@@ -203,7 +218,7 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
203
218
  warn :result => result,
204
219
  :warning_type => "Mass Assignment",
205
220
  :warning_code => :mass_assign_permit_all,
206
- :message => "Parameters should be whitelisted for mass assignment",
221
+ :message => msg('Mass assignment is globally enabled. Disable and specify exact keys using ', msg_code('params.permit'), ' instead'),
207
222
  :confidence => :high
208
223
  end
209
224
  end
@@ -8,7 +8,7 @@ require 'brakeman/checks/base_check'
8
8
  class Brakeman::CheckModelAttrAccessible < Brakeman::BaseCheck
9
9
  Brakeman::Checks.add self
10
10
 
11
- @description = "Reports models which have dangerous attributes defined under the attr_accessible whitelist."
11
+ @description = "Reports models which have dangerous attributes defined via attr_accessible"
12
12
 
13
13
  SUSP_ATTRS = [
14
14
  [:admin, :high], # Very dangerous unless some Rails authorization used
@@ -8,7 +8,7 @@ class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
8
8
  @description = "Reports models which do not use attr_restricted and warns on models that use attr_protected"
9
9
 
10
10
  def run_check
11
- return if mass_assign_disabled?
11
+ return if mass_assign_disabled? or tracker.config.has_gem?(:protected_attributes)
12
12
 
13
13
  #Roll warnings into one warning for all models
14
14
  if tracker.options[:collapse_mass_assignment]
@@ -0,0 +1,37 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckPageCachingCVE < Brakeman::BaseCheck
4
+ Brakeman::Checks.add self
5
+
6
+ @description = "Check for page caching vulnerability (CVE-2020-8159)"
7
+
8
+ def run_check
9
+ gem_name = 'actionpack-page_caching'
10
+ gem_version = tracker.config.gem_version(gem_name.to_sym)
11
+ upgrade_version = '1.2.2'
12
+ cve = 'CVE-2020-8159'
13
+
14
+ return unless gem_version and version_between?('0.0.0', '1.2.1', gem_version)
15
+
16
+ message = msg("Directory traversal vulnerability in ", msg_version(gem_version, gem_name), " ", msg_cve(cve), ". Upgrade to ", msg_version(upgrade_version, gem_name))
17
+
18
+ if uses_caches_page?
19
+ confidence = :high
20
+ else
21
+ confidence = :weak
22
+ end
23
+
24
+ warn :warning_type => 'Directory Traversal',
25
+ :warning_code => :CVE_2020_8159,
26
+ :message => message,
27
+ :confidence => confidence,
28
+ :link_path => 'https://groups.google.com/d/msg/rubyonrails-security/CFRVkEytdP8/c5gmICECAgAJ',
29
+ :gem_info => gemfile_or_environment(gem_name)
30
+ end
31
+
32
+ def uses_caches_page?
33
+ tracker.controllers.any? do |name, controller|
34
+ controller.options.has_key? :caches_page
35
+ end
36
+ end
37
+ end