brakeman 4.10.1 → 5.0.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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +25 -0
  3. data/README.md +1 -1
  4. data/bundle/load.rb +2 -2
  5. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/History.rdoc +6 -0
  6. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/Manifest.txt +0 -0
  7. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/README.rdoc +0 -0
  8. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/compare/normalize.rb +0 -0
  9. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/debugging.md +0 -0
  10. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/rp_extensions.rb +0 -0
  11. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/rp_stringscanner.rb +0 -0
  12. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby20_parser.rb +0 -0
  13. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby20_parser.y +0 -0
  14. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby21_parser.rb +0 -0
  15. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby21_parser.y +0 -0
  16. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby22_parser.rb +0 -0
  17. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby22_parser.y +0 -0
  18. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby23_parser.rb +0 -0
  19. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby23_parser.y +0 -0
  20. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby24_parser.rb +0 -0
  21. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby24_parser.y +0 -0
  22. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby25_parser.rb +0 -0
  23. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby25_parser.y +0 -0
  24. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby26_parser.rb +0 -0
  25. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby26_parser.y +0 -0
  26. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby27_parser.rb +0 -0
  27. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby27_parser.y +0 -0
  28. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby_lexer.rb +0 -0
  29. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby_lexer.rex +0 -0
  30. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby_lexer.rex.rb +0 -0
  31. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby_parser.rb +0 -0
  32. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby_parser.yy +0 -0
  33. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/lib/ruby_parser_extras.rb +1 -1
  34. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/tools/munge.rb +0 -0
  35. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.0 → ruby_parser-3.15.1}/tools/ripper.rb +0 -0
  36. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.1 → sexp_processor-4.15.2}/History.rdoc +6 -0
  37. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.1 → sexp_processor-4.15.2}/Manifest.txt +0 -0
  38. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.1 → sexp_processor-4.15.2}/README.rdoc +0 -0
  39. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.1 → sexp_processor-4.15.2}/lib/composite_sexp_processor.rb +0 -0
  40. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.1 → sexp_processor-4.15.2}/lib/pt_testcase.rb +0 -0
  41. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.1 → sexp_processor-4.15.2}/lib/sexp.rb +0 -0
  42. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.1 → sexp_processor-4.15.2}/lib/sexp_matcher.rb +0 -0
  43. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.1 → sexp_processor-4.15.2}/lib/sexp_processor.rb +1 -1
  44. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.1 → sexp_processor-4.15.2}/lib/strict_sexp.rb +0 -0
  45. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.1 → sexp_processor-4.15.2}/lib/unique.rb +0 -0
  46. data/lib/brakeman.rb +6 -0
  47. data/lib/brakeman/app_tree.rb +36 -3
  48. data/lib/brakeman/checks/base_check.rb +7 -1
  49. data/lib/brakeman/checks/check_execute.rb +1 -0
  50. data/lib/brakeman/checks/check_sql.rb +1 -1
  51. data/lib/brakeman/checks/check_unsafe_reflection_methods.rb +68 -0
  52. data/lib/brakeman/checks/check_verb_confusion.rb +75 -0
  53. data/lib/brakeman/file_parser.rb +19 -18
  54. data/lib/brakeman/options.rb +5 -1
  55. data/lib/brakeman/parsers/template_parser.rb +2 -3
  56. data/lib/brakeman/processors/alias_processor.rb +18 -2
  57. data/lib/brakeman/processors/lib/file_type_detector.rb +64 -0
  58. data/lib/brakeman/processors/lib/rails3_config_processor.rb +16 -16
  59. data/lib/brakeman/report.rb +8 -0
  60. data/lib/brakeman/report/report_base.rb +0 -2
  61. data/lib/brakeman/report/report_csv.rb +37 -60
  62. data/lib/brakeman/report/report_junit.rb +2 -2
  63. data/lib/brakeman/report/report_sarif.rb +1 -1
  64. data/lib/brakeman/report/report_sonar.rb +38 -0
  65. data/lib/brakeman/report/report_tabs.rb +1 -1
  66. data/lib/brakeman/report/report_text.rb +1 -1
  67. data/lib/brakeman/rescanner.rb +7 -5
  68. data/lib/brakeman/scanner.rb +44 -18
  69. data/lib/brakeman/tracker.rb +6 -0
  70. data/lib/brakeman/tracker/config.rb +73 -0
  71. data/lib/brakeman/util.rb +7 -2
  72. data/lib/brakeman/version.rb +1 -1
  73. data/lib/brakeman/warning.rb +10 -2
  74. data/lib/brakeman/warning_codes.rb +2 -0
  75. metadata +48 -44
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1266c840ed2a8f9b6e44686cba353eb6f75eb8d4ec87c7bbdcc56d81785b9227
4
- data.tar.gz: a0ed358121968434f3289b25685970d338d72f1d1f97b0c27103a81a9792cb16
3
+ metadata.gz: 1d660b98db2252a6aa69d39bb56c6950aa7d9713f10831807d6ab837df54657d
4
+ data.tar.gz: 6999959ba9f8380f36c1d999e04b0d79e48ea9536fd9820485c4960bce769d60
5
5
  SHA512:
6
- metadata.gz: 22f064e0f38f304c3d9a18e0c7d36999b7161f9e33ff5c5cd4bca669d19b331e0f92651eb238faf1fd58de66c14da166d7de89353913d806d970f77eb87fa992
7
- data.tar.gz: 4345e389eb3f592139f32cf70b8d618fcb8695cee9f4a8398fdaa636ac5e9be7b2b0ebe6667810f5cd0667f33898effcd9fbf8beac36f48883da73b68fa7c76f
6
+ metadata.gz: b6738f567478a47fd36de992706968c1c42a237dd97d4527434a60fa9ddea5b7a7acb54d8b72e6bc282fd1805126953a358e399a19dab4c0c5e7fd92b4a857ed
7
+ data.tar.gz: 43f16437835dabb65a7b73981779460e7648e1fa2ba772320132e7500af55c8861effda46f3b181310bdd753dbf1c59af12b3ecdfed5844505e2cf5cbff866fa
data/CHANGES.md CHANGED
@@ -1,3 +1,18 @@
1
+ # 5.0.0 - 2021-01-26
2
+
3
+ * Ignore `uuid` as a safe attribute
4
+ * Collapse `__send__` calls
5
+ * Ignore `Tempfile#path` in shell commands
6
+ * Ignore development environment
7
+ * Revamp CSV report to a CSV list of warnings
8
+ * Set Rails configuration defaults based on `load_defaults` version
9
+ * Add check for (more) unsafe method reflection
10
+ * Suggest using `--force` if no Rails application is detected
11
+ * Add Sonarqube report format (Adam England)
12
+ * Add check for potential HTTP verb confusion
13
+ * Add `--[no-]skip-vendor` option
14
+ * Scan (almost) all Ruby files in project
15
+
1
16
  # 4.10.1 - 2020-12-24
2
17
 
3
18
  * Declare REXML as a dependency (Ruby 3.0 compatibility)
@@ -6,6 +21,16 @@
6
21
  * Ensure RubyParser is passed file path as a String
7
22
  * Support new Haml 5.2.0 escaping method
8
23
 
24
+ # 5.0.0.pre1 - 2020-11-17
25
+
26
+ * Add check for (more) unsafe method reflection
27
+ * Suggest using `--force` if no Rails application is detected
28
+ * Add Sonarqube report format (Adam England)
29
+ * Add check for potential HTTP verb confusion
30
+ * Add `--[no-]skip-vendor` option
31
+ * Scan (almost) all Ruby files in project
32
+ * Add support for Haml 5.2.0
33
+
9
34
  # 4.10.0 - 2020-09-28
10
35
 
11
36
  * Add SARIF report format (Steve Winton)
data/README.md CHANGED
@@ -76,7 +76,7 @@ To specify an output file for the results:
76
76
 
77
77
  brakeman -o output_file
78
78
 
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`.
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`.
80
80
 
81
81
  Multiple output files can be specified:
82
82
 
data/bundle/load.rb CHANGED
@@ -1,10 +1,9 @@
1
1
  path = File.expand_path('../..', __FILE__)
2
- $:.unshift "#{path}/bundle/ruby/2.7.0/gems/ruby_parser-3.15.0/lib"
3
2
  $:.unshift "#{path}/bundle/ruby/2.7.0/gems/temple-0.8.2/lib"
4
3
  $:.unshift "#{path}/bundle/ruby/2.7.0/gems/unicode-display_width-1.7.0/lib"
5
- $:.unshift "#{path}/bundle/ruby/2.7.0/gems/sexp_processor-4.15.1/lib"
6
4
  $:.unshift "#{path}/bundle/ruby/2.7.0/gems/tilt-2.0.10/lib"
7
5
  $:.unshift "#{path}/bundle/ruby/2.7.0/gems/slim-4.1.0/lib"
6
+ $:.unshift "#{path}/bundle/ruby/2.7.0/gems/sexp_processor-4.15.2/lib"
8
7
  $:.unshift "#{path}/bundle/ruby/2.7.0/gems/highline-2.0.3/lib"
9
8
  $:.unshift "#{path}/bundle/ruby/2.7.0/gems/ruby2ruby-2.4.4/lib"
10
9
  $:.unshift "#{path}/bundle/ruby/2.7.0/gems/terminal-table-1.8.0/lib"
@@ -12,4 +11,5 @@ $:.unshift "#{path}/bundle/ruby/2.7.0/gems/rexml-3.2.4/lib"
12
11
  $:.unshift "#{path}/bundle/ruby/2.7.0/gems/ruby_parser-legacy-1.0.0/lib"
13
12
  $:.unshift "#{path}/bundle/ruby/2.7.0/gems/erubis-2.7.0/lib"
14
13
  $:.unshift "#{path}/bundle/ruby/2.7.0/gems/haml-5.2.1/lib"
14
+ $:.unshift "#{path}/bundle/ruby/2.7.0/gems/ruby_parser-3.15.1/lib"
15
15
  $:.unshift "#{path}/bundle/ruby/2.7.0/gems/safe_yaml-1.0.5/lib"
@@ -1,3 +1,9 @@
1
+ === 3.15.1 / 2021-01-10
2
+
3
+ * 1 bug fix:
4
+
5
+ * Bumped ruby version to include < 4 (trunk).
6
+
1
7
  === 3.15.0 / 2020-08-31
2
8
 
3
9
  * 1 major enhancement:
@@ -29,7 +29,7 @@ class Sexp
29
29
  end
30
30
 
31
31
  module RubyParserStuff
32
- VERSION = "3.15.0"
32
+ VERSION = "3.15.1"
33
33
 
34
34
  attr_accessor :lexer, :in_def, :in_single, :file
35
35
  attr_accessor :in_kwarg
@@ -1,3 +1,9 @@
1
+ === 4.15.2 / 2021-01-10
2
+
3
+ * 1 bug fix:
4
+
5
+ * Bumped ruby version to include < 4 (trunk).
6
+
1
7
  === 4.15.1 / 2020-08-31
2
8
 
3
9
  * 1 bug fix:
@@ -34,7 +34,7 @@ require "sexp"
34
34
  class SexpProcessor
35
35
 
36
36
  # duh
37
- VERSION = "4.15.1"
37
+ VERSION = "4.15.2"
38
38
 
39
39
  ##
40
40
  # Automatically shifts off the Sexp type before handing the
data/lib/brakeman.rb CHANGED
@@ -66,6 +66,7 @@ module Brakeman
66
66
  # * :run_checks - array of checks to run (run all if not specified)
67
67
  # * :safe_methods - array of methods to consider safe
68
68
  # * :skip_libs - do not process lib/ directory (default: false)
69
+ # * :skip_vendor - do not process vendor/ directory (default: true)
69
70
  # * :skip_checks - checks not to run (run all if not specified)
70
71
  # * :absolute_paths - show absolute path of each file (default: false)
71
72
  # * :summary_only - only output summary section of report for plain/table (:summary_only, :no_summary, true)
@@ -191,6 +192,7 @@ module Brakeman
191
192
  :report_progress => true,
192
193
  :safe_methods => Set.new,
193
194
  :skip_checks => Set.new,
195
+ :skip_vendor => true,
194
196
  }
195
197
  end
196
198
 
@@ -239,6 +241,8 @@ module Brakeman
239
241
  [:to_junit]
240
242
  when :sarif, :to_sarif
241
243
  [:to_sarif]
244
+ when :sonar, :to_sonar
245
+ [:to_sonar]
242
246
  else
243
247
  [:to_text]
244
248
  end
@@ -270,6 +274,8 @@ module Brakeman
270
274
  :to_junit
271
275
  when /\.sarif$/i
272
276
  :to_sarif
277
+ when /\.sonar$/i
278
+ :to_sonar
273
279
  else
274
280
  :to_text
275
281
  end
@@ -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
@@ -40,7 +40,7 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
40
40
  @mass_assign_disabled = nil
41
41
  @has_user_input = nil
42
42
  @in_array = false
43
- @safe_input_attributes = Set[:to_i, :to_f, :arel_table, :id]
43
+ @safe_input_attributes = Set[:to_i, :to_f, :arel_table, :id, :uuid]
44
44
  @comparison_ops = Set[:==, :!=, :>, :<, :>=, :<=]
45
45
  end
46
46
 
@@ -151,6 +151,12 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
151
151
  method[-1] == "?"
152
152
  end
153
153
 
154
+ TEMP_FILE_PATH = s(:call, s(:call, s(:const, :Tempfile), :new), :path).freeze
155
+
156
+ def temp_file_path? exp
157
+ exp == TEMP_FILE_PATH
158
+ end
159
+
154
160
  #Report a warning
155
161
  def warn options
156
162
  extra_opts = { :check => self.class.to_s }
@@ -204,6 +204,7 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
204
204
  next if node_type? e, :lit, :str
205
205
  next if SAFE_VALUES.include? e
206
206
  next if shell_escape? e
207
+ next if temp_file_path? e
207
208
 
208
209
  if node_type? e, :if
209
210
  # If we're in a conditional, evaluate the `then` and `else` clauses to
@@ -576,7 +576,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
576
576
  :sanitize_sql_for_assignment, :sanitize_sql_for_conditions, :sanitize_sql_hash,
577
577
  :sanitize_sql_hash_for_assignment, :sanitize_sql_hash_for_conditions,
578
578
  :to_sql, :sanitize, :primary_key, :table_name_prefix, :table_name_suffix,
579
- :where_values_hash, :foreign_key
579
+ :where_values_hash, :foreign_key, :uuid
580
580
  ]
581
581
 
582
582
  def safe_value? exp
@@ -0,0 +1,68 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckUnsafeReflectionMethods < Brakeman::BaseCheck
4
+ Brakeman::Checks.add self
5
+
6
+ @description = "Checks for unsafe reflection to access methods"
7
+
8
+ def run_check
9
+ check_method
10
+ check_tap
11
+ check_to_proc
12
+ end
13
+
14
+ def check_method
15
+ tracker.find_call(method: :method, nested: true).each do |result|
16
+ argument = result[:call].first_arg
17
+
18
+ if user_input = include_user_input?(argument)
19
+ warn_unsafe_reflection(result, user_input)
20
+ end
21
+ end
22
+ end
23
+
24
+ def check_tap
25
+ tracker.find_call(method: :tap, nested: true).each do |result|
26
+ argument = result[:call].first_arg
27
+
28
+ # Argument is passed like a.tap(&argument)
29
+ if node_type? argument, :block_pass
30
+ argument = argument.value
31
+ end
32
+
33
+ if user_input = include_user_input?(argument)
34
+ warn_unsafe_reflection(result, user_input)
35
+ end
36
+ end
37
+ end
38
+
39
+ def check_to_proc
40
+ tracker.find_call(method: :to_proc, nested: true).each do |result|
41
+ target = result[:call].target
42
+
43
+ if user_input = include_user_input?(target)
44
+ warn_unsafe_reflection(result, user_input)
45
+ end
46
+ end
47
+ end
48
+
49
+ def warn_unsafe_reflection result, input
50
+ return unless original? result
51
+ method = result[:call].method
52
+
53
+ confidence = if input.type == :params
54
+ :high
55
+ else
56
+ :medium
57
+ end
58
+
59
+ message = msg("Unsafe reflection method ", msg_code(method), " called with ", msg_input(input))
60
+
61
+ warn :result => result,
62
+ :warning_type => "Remote Code Execution",
63
+ :warning_code => :unsafe_method_reflection,
64
+ :message => message,
65
+ :user_input => input,
66
+ :confidence => confidence
67
+ end
68
+ end
@@ -0,0 +1,75 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckVerbConfusion < Brakeman::BaseCheck
4
+ Brakeman::Checks.add self
5
+
6
+ @description = "Check for uses of `request.get?` that might have unintentional behavior"
7
+
8
+ #Process calls
9
+ def run_check
10
+ calls = tracker.find_call(target: :request, methods: [:get?])
11
+
12
+ calls.each do |call|
13
+ process_result call
14
+ end
15
+ end
16
+
17
+ def process_result result
18
+ @current_result = result
19
+ @matched_call = result[:call]
20
+ klass = tracker.find_class(result[:location][:class])
21
+
22
+ # TODO: abstract into tracker.find_location ?
23
+ if klass.nil?
24
+ Brakeman.debug "No class found: #{result[:location][:class]}"
25
+ return
26
+ end
27
+
28
+ method = klass.get_method(result[:location][:method])
29
+
30
+ if method.nil?
31
+ Brakeman.debug "No method found: #{result[:location][:method]}"
32
+ return
33
+ end
34
+
35
+ process method[:src]
36
+ end
37
+
38
+ def process_if exp
39
+ if exp.condition == @matched_call
40
+ # Found `if request.get?`
41
+
42
+ # Do not warn if there is an `elsif` clause
43
+ if node_type? exp.else_clause, :if
44
+ return exp
45
+ end
46
+
47
+ warn_about_result @current_result, exp
48
+ end
49
+
50
+ exp
51
+ end
52
+
53
+ def warn_about_result result, code
54
+ return unless original? result
55
+
56
+ confidence = :weak
57
+ message = msg('Potential HTTP verb confusion. ',
58
+ msg_code('HEAD'),
59
+ ' is routed like ',
60
+ msg_code('GET'),
61
+ ' but ',
62
+ msg_code('request.get?'),
63
+ ' will return ',
64
+ msg_code('false')
65
+ )
66
+
67
+ warn :result => result,
68
+ :warning_type => "HTTP Verb Confusion",
69
+ :warning_code => :http_verb_confusion,
70
+ :message => message,
71
+ :code => code,
72
+ :user_input => result[:call],
73
+ :confidence => confidence
74
+ end
75
+ end