brakeman-min 4.7.2 → 4.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +46 -0
  3. data/README.md +12 -4
  4. data/lib/brakeman.rb +20 -0
  5. data/lib/brakeman/checks/base_check.rb +13 -10
  6. data/lib/brakeman/checks/check_basic_auth.rb +2 -0
  7. data/lib/brakeman/checks/check_content_tag.rb +1 -2
  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_execute.rb +40 -5
  11. data/lib/brakeman/checks/check_json_entity_escape.rb +38 -0
  12. data/lib/brakeman/checks/check_link_to.rb +1 -1
  13. data/lib/brakeman/checks/check_link_to_href.rb +1 -3
  14. data/lib/brakeman/checks/check_mass_assignment.rb +33 -3
  15. data/lib/brakeman/checks/check_model_attr_accessible.rb +1 -1
  16. data/lib/brakeman/checks/check_model_attributes.rb +1 -1
  17. data/lib/brakeman/checks/check_page_caching_cve.rb +37 -0
  18. data/lib/brakeman/checks/check_permit_attributes.rb +1 -1
  19. data/lib/brakeman/checks/check_skip_before_filter.rb +4 -4
  20. data/lib/brakeman/checks/check_sql.rb +1 -12
  21. data/lib/brakeman/checks/check_template_injection.rb +32 -0
  22. data/lib/brakeman/commandline.rb +25 -1
  23. data/lib/brakeman/differ.rb +0 -5
  24. data/lib/brakeman/options.rb +21 -1
  25. data/lib/brakeman/processors/alias_processor.rb +2 -3
  26. data/lib/brakeman/processors/lib/call_conversion_helper.rb +1 -1
  27. data/lib/brakeman/processors/lib/find_all_calls.rb +30 -14
  28. data/lib/brakeman/processors/lib/render_helper.rb +3 -1
  29. data/lib/brakeman/report.rb +4 -1
  30. data/lib/brakeman/report/ignore/config.rb +10 -2
  31. data/lib/brakeman/report/report_junit.rb +104 -0
  32. data/lib/brakeman/report/report_markdown.rb +0 -1
  33. data/lib/brakeman/report/report_text.rb +37 -16
  34. data/lib/brakeman/scanner.rb +4 -1
  35. data/lib/brakeman/tracker.rb +3 -1
  36. data/lib/brakeman/tracker/config.rb +7 -4
  37. data/lib/brakeman/tracker/constants.rb +8 -7
  38. data/lib/brakeman/util.rb +16 -0
  39. data/lib/brakeman/version.rb +1 -1
  40. data/lib/brakeman/warning_codes.rb +7 -0
  41. metadata +22 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a6cc961dcc300bf0fd78eeee53b715dc62ae4d7d0e1d27320cc6a6ef00ff5e3
4
- data.tar.gz: 0ba044de5c3a3274b90ed2da001914ffa26aa74fd6b2c7d4b15d7b3d120b5c7d
3
+ metadata.gz: b0fcf3c3ee13f623d43462b52e575c0d89670f9efd97029f024e1dd4428ecdad
4
+ data.tar.gz: 28ffc613573a1be76a17daa9d47b97fe82c83efb85beeb475543f536f5f16dd9
5
5
  SHA512:
6
- metadata.gz: ba6ea029432adb3c9c56fa53fc11292f8d71b857eeb03bbacc2ae9f349cfbedfa13c13a74be732616844e5243d8d3bc3690a776e056a9e9f4441f7e0a9d61637
7
- data.tar.gz: ba2cf378cc029a7b583b47f69715897012ddc5d07b39749638128ea8cfc92e190b05bb6ea919d68d62c91bfb80ca7ee95d293f7c3b16096d204d15f11d13f197
6
+ metadata.gz: 9ae9718ffe7c7d062a0de46bd3bc1505c2c626fbfaede605505cad16ffaf89a8c50bc9e134d27f63d5450ce286c8c4aca67b26c8265dc730a83fa0b423cef6cf
7
+ data.tar.gz: fe49548d88cc579e7a8c540655bcde6107a51015a812440cb385d024e301932e52155f28ec12efa4078305b34b7e7ff21487c102b54cc17d718b715b37c49b17
data/CHANGES.md CHANGED
@@ -1,3 +1,49 @@
1
+ # 4.9.1 - 2020-09-04
2
+
3
+ * Check `chomp`ed strings for SQL injection
4
+ * Use version from `active_record` for non-Rails apps (Ulysse Buonomo)
5
+ * Always set line number for joined arrays
6
+ * Avoid warning about missing `attr_accessible` if `protected_attributes` gem is used
7
+
8
+ # 4.9.0 - 2020-08-04
9
+
10
+ * Add check for CVE-2020-8166 (Jamie Finnigan)
11
+ * Avoid warning when `safe_yaml` is used via `YAML.load(..., safe: true)`
12
+ * Add check for user input in `ERB.new` (Matt Hickman)
13
+ * Add `--ensure-ignore-notes` (Eli Block)
14
+ * Remove whitelist/blacklist language, add clarifications
15
+ * Do not warn about mass assignment with `params.permit!.slice`
16
+ * Add "full call" information to call index results
17
+ * Ignore `params.permit!` in path helpers
18
+ * Treat `Dir.glob` as safe source of values in guards
19
+ * Always scan `environment.rb`
20
+
21
+ # 4.8.2 - 2020-05-12
22
+
23
+ * Add check for CVE-2020-8159
24
+ * Fix `authenticate_or_request_with_http_basic` check for passed blocks (Hugo Corbucci)
25
+ * Add `--text-fields` option
26
+ * Add check for escaping HTML entities in JSON configuration
27
+
28
+ # 4.8.1 - 2020-04-06
29
+
30
+ * Check SQL query strings using `String#strip` or `String.squish`
31
+ * Handle non-symbol keys in locals hash for render()
32
+ * Warn about global(!) mass assignment
33
+ * Index calls in render arguments
34
+
35
+ # 4.8.0 - 2020-02-18
36
+
37
+ * Add JUnit-XML report format (Naoki Kimura)
38
+ * Sort ignore files by fingerprint and line (Ngan Pham)
39
+ * Freeze call index results
40
+ * Fix output test when using newer Minitest
41
+ * Properly render confidence in Markdown report
42
+ * Report old warnings as fixed if zero warnings reported
43
+ * Catch dangerous concatenation in `CheckExecute` (Jacob Evelyn)
44
+ * Show user-friendly message when ignore config file has invalid JSON (D. Hicks)
45
+ * Initialize Rails version with `nil` (Carsten Wirth)
46
+
1
47
  # 4.7.2 - 2019-11-25
2
48
 
3
49
  * Remove version guard for `named_scope` vs. `scope`
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,12 +76,16 @@ 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`, `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`, and `codeclimate`.
78
80
 
79
81
  Multiple output files can be specified:
80
82
 
81
83
  brakeman -o output.html -o output.json
82
84
 
85
+ To output to both a file and to the console, with color:
86
+
87
+ brakeman --color -o /dev/stdout -o output.json
88
+
83
89
  To suppress informational warnings and just output the report:
84
90
 
85
91
  brakeman -q
@@ -167,6 +173,8 @@ There is a [plugin available](http://brakemanscanner.org/docs/jenkins/) for Jenk
167
173
 
168
174
  For even more continuous testing, try the [Guard plugin](https://github.com/guard/guard-brakeman).
169
175
 
176
+ There are a couple [Github Actions](https://github.com/marketplace?type=actions&query=brakeman) available.
177
+
170
178
  # Building
171
179
 
172
180
  git clone git://github.com/presidentbeef/brakeman.git
@@ -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 = []
@@ -231,6 +235,8 @@ module Brakeman
231
235
  [:to_text]
232
236
  when :table, :to_table
233
237
  [:to_table]
238
+ when :junit, :to_junit
239
+ [:to_junit]
234
240
  else
235
241
  [:to_text]
236
242
  end
@@ -258,6 +264,8 @@ module Brakeman
258
264
  :to_text
259
265
  when /\.table$/i
260
266
  :to_table
267
+ when /\.junit$/i
268
+ :to_junit
261
269
  else
262
270
  :to_text
263
271
  end
@@ -494,6 +502,18 @@ module Brakeman
494
502
  end
495
503
  end
496
504
 
505
+ # Returns an array of alert fingerprints for any ignored warnings without
506
+ # notes found in the specified ignore file (if it exists).
507
+ def self.ignore_file_entries_with_empty_notes file
508
+ return [] unless file
509
+
510
+ require 'brakeman/report/ignore/config'
511
+
512
+ config = IgnoreConfig.new(file, nil)
513
+ config.read_from_file
514
+ config.already_ignored_entries_with_empty_notes.map { |i| i[:fingerprint] }
515
+ end
516
+
497
517
  def self.filter_warnings tracker, options
498
518
  require 'brakeman/report/ignore/config'
499
519
 
@@ -280,15 +280,6 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
280
280
  return location, line
281
281
  end
282
282
 
283
- #Checks if an expression contains string interpolation.
284
- #
285
- #Returns Match with :interp type if found.
286
- def include_interp? exp
287
- @string_interp = false
288
- process exp
289
- @string_interp
290
- end
291
-
292
283
  #Checks if _exp_ includes user input in the form of cookies, parameters,
293
284
  #request environment, or model attributes.
294
285
  #
@@ -476,7 +467,7 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
476
467
  end
477
468
 
478
469
  def gemfile_or_environment gem_name = :rails
479
- 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)
480
471
  info
481
472
  elsif @app_tree.exists?("Gemfile")
482
473
  @app_tree.file_path "Gemfile"
@@ -504,4 +495,16 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
504
495
 
505
496
  @active_record_models
506
497
  end
498
+
499
+ STRING_METHODS = Set[:<<, :+, :concat, :prepend]
500
+ private_constant :STRING_METHODS
501
+
502
+ def string_building? exp
503
+ return false unless call? exp and STRING_METHODS.include? exp.method
504
+
505
+ node_type? exp.target, :str, :dstr or
506
+ node_type? exp.first_arg, :str, :dstr or
507
+ string_building? exp.target or
508
+ string_building? exp.first_arg
509
+ end
507
510
  end
@@ -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]
@@ -55,8 +55,7 @@ class Brakeman::CheckContentTag < Brakeman::CheckCrossSiteScripting
55
55
 
56
56
  @current_file = result[:location][:file]
57
57
 
58
- call = result[:call] = result[:call].dup
59
-
58
+ call = result[:call]
60
59
  args = call.arglist
61
60
 
62
61
  tag_name = args[1]
@@ -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
@@ -56,8 +56,20 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
56
56
 
57
57
  case call.method
58
58
  when :popen
59
- unless array? first_arg
60
- failure = include_user_input?(args) || dangerous_interp?(args)
59
+ # Normally, if we're in a `popen` call, we only are worried about shell
60
+ # injection when the argument is not an array, because array elements
61
+ # are always escaped by Ruby. However, an exception is when the array
62
+ # contains two values are something like "bash -c" because then the third
63
+ # element is effectively the command being run and might be a malicious
64
+ # executable if it comes (partially or fully) from user input.
65
+ if !array?(first_arg)
66
+ failure = include_user_input?(first_arg) ||
67
+ dangerous_interp?(first_arg) ||
68
+ dangerous_string_building?(first_arg)
69
+ elsif dash_c_shell_command?(first_arg[1], first_arg[2])
70
+ failure = include_user_input?(first_arg[3]) ||
71
+ dangerous_interp?(first_arg[3]) ||
72
+ dangerous_string_building?(first_arg[3])
61
73
  end
62
74
  when :system, :exec
63
75
  # Normally, if we're in a `system` or `exec` call, we only are worried
@@ -67,12 +79,18 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
67
79
  # the third argument is effectively the command being run and might be
68
80
  # a malicious executable if it comes (partially or fully) from user input.
69
81
  if dash_c_shell_command?(first_arg, call.second_arg)
70
- failure = include_user_input?(args[3]) || dangerous_interp?(args[3])
82
+ failure = include_user_input?(args[3]) ||
83
+ dangerous_interp?(args[3]) ||
84
+ dangerous_string_building?(args[3])
71
85
  else
72
- failure = include_user_input?(first_arg) || dangerous_interp?(first_arg)
86
+ failure = include_user_input?(first_arg) ||
87
+ dangerous_interp?(first_arg) ||
88
+ dangerous_string_building?(first_arg)
73
89
  end
74
90
  else
75
- failure = include_user_input?(args) || dangerous_interp?(args)
91
+ failure = include_user_input?(args) ||
92
+ dangerous_interp?(args) ||
93
+ dangerous_string_building?(args)
76
94
  end
77
95
 
78
96
  if failure and original? result
@@ -219,6 +237,23 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
219
237
  false
220
238
  end
221
239
 
240
+ #Checks if an expression contains string interpolation.
241
+ #
242
+ #Returns Match with :interp type if found.
243
+ def include_interp? exp
244
+ @string_interp = false
245
+ process exp
246
+ @string_interp
247
+ end
248
+
249
+ def dangerous_string_building? exp
250
+ if string_building?(exp) && res = dangerous?(exp)
251
+ return Match.new(:interp, res)
252
+ end
253
+
254
+ false
255
+ end
256
+
222
257
  def shell_escape? exp
223
258
  return false unless call? exp
224
259
 
@@ -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
@@ -34,7 +34,7 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
34
34
 
35
35
  #Have to make a copy of this, otherwise it will be changed to
36
36
  #an ignored method call by the code above.
37
- call = result[:call] = result[:call].dup
37
+ call = result[:call]
38
38
 
39
39
  first_arg = call.first_arg
40
40
  second_arg = call.second_arg
@@ -30,9 +30,7 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
30
30
  end
31
31
 
32
32
  def process_result result
33
- #Have to make a copy of this, otherwise it will be changed to
34
- #an ignored method call by the code above.
35
- call = result[:call] = result[:call].dup
33
+ call = result[:call]
36
34
  @matched = false
37
35
 
38
36
  url_arg = if result[:block]
@@ -17,6 +17,7 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
17
17
  def run_check
18
18
  check_mass_assignment
19
19
  check_permit!
20
+ check_permit_all_parameters
20
21
  end
21
22
 
22
23
  def find_mass_assign_calls
@@ -159,12 +160,27 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
159
160
  # Look for and warn about uses of Parameters#permit! for mass assignment
160
161
  def check_permit!
161
162
  tracker.find_call(:method => :permit!, :nested => true).each do |result|
162
- if params? result[:call].target and not result[:chain].include? :slice
163
- 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
164
167
  end
165
168
  end
166
169
  end
167
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
+
168
184
  # Look for actual use of params in mass assignment to avoid
169
185
  # warning about uses of Parameters#permit! without any mass assignment
170
186
  # or when mass assignment is restricted by model instead.
@@ -190,7 +206,21 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
190
206
  warn :result => result,
191
207
  :warning_type => "Mass Assignment",
192
208
  :warning_code => :mass_assign_permit!,
193
- :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'),
194
210
  :confidence => confidence
195
211
  end
212
+
213
+ def check_permit_all_parameters
214
+ tracker.find_call(target: :"ActionController::Parameters", method: :permit_all_parameters=).each do |result|
215
+ call = result[:call]
216
+
217
+ if true? call.first_arg
218
+ warn :result => result,
219
+ :warning_type => "Mass Assignment",
220
+ :warning_code => :mass_assign_permit_all,
221
+ :message => msg('Mass assignment is globally enabled. Disable and specify exact keys using ', msg_code('params.permit'), ' instead'),
222
+ :confidence => :high
223
+ end
224
+ end
225
+ end
196
226
  end