brakeman-lib 4.5.0 → 4.7.1

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +164 -108
  3. data/README.md +6 -7
  4. data/lib/brakeman.rb +7 -0
  5. data/lib/brakeman/app_tree.rb +34 -22
  6. data/lib/brakeman/call_index.rb +54 -15
  7. data/lib/brakeman/checks.rb +7 -7
  8. data/lib/brakeman/checks/base_check.rb +59 -56
  9. data/lib/brakeman/checks/check_cookie_serialization.rb +22 -0
  10. data/lib/brakeman/checks/check_cross_site_scripting.rb +9 -4
  11. data/lib/brakeman/checks/check_default_routes.rb +5 -0
  12. data/lib/brakeman/checks/check_deserialize.rb +49 -0
  13. data/lib/brakeman/checks/check_dynamic_finders.rb +1 -1
  14. data/lib/brakeman/checks/check_execute.rb +26 -1
  15. data/lib/brakeman/checks/check_file_access.rb +7 -1
  16. data/lib/brakeman/checks/check_force_ssl.rb +27 -0
  17. data/lib/brakeman/checks/check_header_dos.rb +2 -2
  18. data/lib/brakeman/checks/check_i18n_xss.rb +2 -2
  19. data/lib/brakeman/checks/check_jruby_xml.rb +2 -2
  20. data/lib/brakeman/checks/check_json_parsing.rb +7 -2
  21. data/lib/brakeman/checks/check_link_to_href.rb +6 -1
  22. data/lib/brakeman/checks/check_mail_to.rb +1 -1
  23. data/lib/brakeman/checks/check_mime_type_dos.rb +2 -2
  24. data/lib/brakeman/checks/check_model_attr_accessible.rb +1 -1
  25. data/lib/brakeman/checks/check_model_attributes.rb +12 -50
  26. data/lib/brakeman/checks/check_model_serialize.rb +1 -1
  27. data/lib/brakeman/checks/check_nested_attributes_bypass.rb +4 -4
  28. data/lib/brakeman/checks/check_reverse_tabnabbing.rb +58 -0
  29. data/lib/brakeman/checks/check_sanitize_methods.rb +2 -2
  30. data/lib/brakeman/checks/check_secrets.rb +1 -1
  31. data/lib/brakeman/checks/check_session_settings.rb +15 -12
  32. data/lib/brakeman/checks/check_simple_format.rb +5 -0
  33. data/lib/brakeman/checks/check_skip_before_filter.rb +1 -1
  34. data/lib/brakeman/checks/check_sql.rb +15 -17
  35. data/lib/brakeman/checks/check_validation_regex.rb +1 -1
  36. data/lib/brakeman/checks/check_xml_dos.rb +2 -2
  37. data/lib/brakeman/checks/check_yaml_parsing.rb +10 -18
  38. data/lib/brakeman/differ.rb +16 -28
  39. data/lib/brakeman/file_parser.rb +10 -16
  40. data/lib/brakeman/file_path.rb +85 -0
  41. data/lib/brakeman/options.rb +7 -0
  42. data/lib/brakeman/parsers/haml_embedded.rb +1 -1
  43. data/lib/brakeman/parsers/template_parser.rb +6 -4
  44. data/lib/brakeman/processor.rb +4 -5
  45. data/lib/brakeman/processors/alias_processor.rb +27 -7
  46. data/lib/brakeman/processors/base_processor.rb +10 -7
  47. data/lib/brakeman/processors/controller_alias_processor.rb +10 -7
  48. data/lib/brakeman/processors/controller_processor.rb +9 -13
  49. data/lib/brakeman/processors/gem_processor.rb +10 -2
  50. data/lib/brakeman/processors/haml_template_processor.rb +92 -123
  51. data/lib/brakeman/processors/lib/call_conversion_helper.rb +5 -4
  52. data/lib/brakeman/processors/lib/find_all_calls.rb +27 -4
  53. data/lib/brakeman/processors/lib/find_call.rb +3 -64
  54. data/lib/brakeman/processors/lib/module_helper.rb +8 -8
  55. data/lib/brakeman/processors/lib/processor_helper.rb +3 -3
  56. data/lib/brakeman/processors/lib/rails2_config_processor.rb +4 -4
  57. data/lib/brakeman/processors/lib/rails2_route_processor.rb +2 -2
  58. data/lib/brakeman/processors/lib/rails3_config_processor.rb +3 -3
  59. data/lib/brakeman/processors/lib/rails3_route_processor.rb +2 -2
  60. data/lib/brakeman/processors/lib/render_helper.rb +2 -2
  61. data/lib/brakeman/processors/lib/render_path.rb +18 -1
  62. data/lib/brakeman/processors/library_processor.rb +5 -5
  63. data/lib/brakeman/processors/model_processor.rb +4 -5
  64. data/lib/brakeman/processors/output_processor.rb +5 -0
  65. data/lib/brakeman/processors/template_alias_processor.rb +32 -5
  66. data/lib/brakeman/processors/template_processor.rb +14 -10
  67. data/lib/brakeman/report.rb +3 -3
  68. data/lib/brakeman/report/ignore/config.rb +2 -3
  69. data/lib/brakeman/report/ignore/interactive.rb +2 -2
  70. data/lib/brakeman/report/pager.rb +1 -0
  71. data/lib/brakeman/report/report_base.rb +51 -6
  72. data/lib/brakeman/report/report_codeclimate.rb +3 -3
  73. data/lib/brakeman/report/report_hash.rb +1 -1
  74. data/lib/brakeman/report/report_html.rb +2 -2
  75. data/lib/brakeman/report/report_json.rb +1 -24
  76. data/lib/brakeman/report/report_table.rb +20 -4
  77. data/lib/brakeman/report/report_tabs.rb +1 -1
  78. data/lib/brakeman/report/report_text.rb +6 -7
  79. data/lib/brakeman/rescanner.rb +13 -12
  80. data/lib/brakeman/scanner.rb +19 -14
  81. data/lib/brakeman/tracker.rb +30 -6
  82. data/lib/brakeman/tracker/collection.rb +4 -3
  83. data/lib/brakeman/tracker/config.rb +44 -73
  84. data/lib/brakeman/tracker/constants.rb +2 -1
  85. data/lib/brakeman/util.rb +1 -147
  86. data/lib/brakeman/version.rb +1 -1
  87. data/lib/brakeman/warning.rb +27 -13
  88. data/lib/brakeman/warning_codes.rb +4 -0
  89. data/lib/ruby_parser/bm_sexp.rb +7 -2
  90. data/lib/ruby_parser/bm_sexp_processor.rb +1 -0
  91. metadata +27 -22
@@ -4,10 +4,8 @@ module Brakeman
4
4
  class Config
5
5
  include Util
6
6
 
7
- attr_reader :rails, :tracker
8
- attr_accessor :rails_version, :ruby_version
7
+ attr_reader :gems, :rails, :ruby_version, :tracker
9
8
  attr_writer :erubis, :escape_html
10
- attr_reader :gems
11
9
 
12
10
  def initialize tracker
13
11
  @tracker = tracker
@@ -19,16 +17,9 @@ module Brakeman
19
17
  @ruby_version = ""
20
18
  end
21
19
 
22
- def allow_forgery_protection?
23
- @rails[:action_controller] and
24
- @rails[:action_controller][:allow_forgery_protection] == Sexp.new(:false)
25
- end
26
-
27
20
  def default_protect_from_forgery?
28
- if version_between? "5.2.0", "9.9.9"
29
- if @rails[:action_controller] and
30
- @rails[:action_controller][:default_protect_from_forgery] == Sexp.new(:false)
31
-
21
+ if version_between? "5.2.0.beta1", "9.9.9"
22
+ if @rails.dig(:action_controller, :default_protect_from_forgery) == Sexp.new(:false)
32
23
  return false
33
24
  else
34
25
  return true
@@ -48,17 +39,21 @@ module Brakeman
48
39
 
49
40
  def escape_html_entities_in_json?
50
41
  #TODO add version-specific information here
51
- @rails[:active_support] and
52
- true? @rails[:active_support][:escape_html_entities_in_json]
42
+ true? @rails.dig(:active_support, :escape_html_entities_in_json)
43
+ end
44
+
45
+ def escape_filter_interpolations?
46
+ # TODO see if app is actually turning this off itself
47
+ has_gem?(:haml) and
48
+ version_between? "5.0.0", "5.99", gem_version(:haml)
53
49
  end
54
50
 
55
51
  def whitelist_attributes?
56
- @rails[:active_record] and
57
- @rails[:active_record][:whitelist_attributes] == Sexp.new(:true)
52
+ @rails.dig(:active_record, :whitelist_attributes) == Sexp.new(:true)
58
53
  end
59
54
 
60
55
  def gem_version name
61
- @gems[name] and @gems[name][:version]
56
+ extract_version @gems.dig(name, :version)
62
57
  end
63
58
 
64
59
  def add_gem name, version, file, line
@@ -78,11 +73,16 @@ module Brakeman
78
73
  @gems[name]
79
74
  end
80
75
 
81
- def set_rails_version
82
- # Ignore ~>, etc. when using values from Gemfile
83
- version = gem_version(:rails) || gem_version(:railties)
84
- if version and version.match(/(\d+\.\d+(\.\d+.*)?)/)
85
- @rails_version = $1
76
+ def set_rails_version version = nil
77
+ version = if version
78
+ # Only used by Rails2ConfigProcessor right now
79
+ extract_version(version)
80
+ else
81
+ gem_version(:rails) || gem_version(:railties)
82
+ end
83
+
84
+ if version
85
+ @rails_version = version
86
86
 
87
87
  if tracker.options[:rails3].nil? and tracker.options[:rails4].nil?
88
88
  if @rails_version.start_with? "3"
@@ -97,6 +97,12 @@ module Brakeman
97
97
  tracker.options[:rails4] = true
98
98
  tracker.options[:rails5] = true
99
99
  Brakeman.notify "[Notice] Detected Rails 5 application"
100
+ elsif @rails_version.start_with? "6"
101
+ tracker.options[:rails3] = true
102
+ tracker.options[:rails4] = true
103
+ tracker.options[:rails5] = true
104
+ tracker.options[:rails6] = true
105
+ Brakeman.notify "[Notice] Detected Rails 6 application"
100
106
  end
101
107
  end
102
108
  end
@@ -107,12 +113,20 @@ module Brakeman
107
113
  end
108
114
  end
109
115
 
116
+ def rails_version
117
+ # This needs to be here because Util#rails_version calls Tracker::Config#rails_version
118
+ # but Tracker::Config includes Util...
119
+ @rails_version
120
+ end
121
+
110
122
  def set_ruby_version version
123
+ @ruby_version = extract_version(version)
124
+ end
125
+
126
+ def extract_version version
111
127
  return unless version.is_a? String
112
128
 
113
- if version =~ /(\d+\.\d+\.\d+)/
114
- self.ruby_version = $1
115
- end
129
+ version[/\d+\.\d+(\.\d+.*)?/]
116
130
  end
117
131
 
118
132
  #Returns true if low_version <= RAILS_VERSION <= high_version
@@ -122,58 +136,15 @@ module Brakeman
122
136
  current_version ||= rails_version
123
137
  return false unless current_version
124
138
 
125
- version = current_version.split(".").map! { |v| convert_version_number v }
126
- low_version = low_version.split(".").map! { |v| convert_version_number v }
127
- high_version = high_version.split(".").map! { |v| convert_version_number v }
128
-
129
- version.each_with_index do |v, i|
130
- if lower? v, low_version.fetch(i, 0)
131
- return false
132
- elsif higher? v, low_version.fetch(i, 0)
133
- break
134
- end
135
- end
136
-
137
- version.each_with_index do |v, i|
138
- if higher? v, high_version.fetch(i, 0)
139
- return false
140
- elsif lower? v, high_version.fetch(i, 0)
141
- break
142
- end
143
- end
139
+ low = Gem::Version.new(low_version)
140
+ high = Gem::Version.new(high_version)
141
+ current = Gem::Version.new(current_version)
144
142
 
145
- true
143
+ current.between?(low, high)
146
144
  end
147
145
 
148
146
  def session_settings
149
- @rails[:action_controller] &&
150
- @rails[:action_controller][:session]
151
- end
152
-
153
- private
154
-
155
- def convert_version_number value
156
- if value.match(/\A\d+\z/)
157
- value.to_i
158
- else
159
- value
160
- end
161
- end
162
-
163
- def lower? lhs, rhs
164
- if lhs.class == rhs.class
165
- lhs < rhs
166
- else
167
- false
168
- end
169
- end
170
-
171
- def higher? lhs, rhs
172
- if lhs.class == rhs.class
173
- lhs > rhs
174
- else
175
- false
176
- end
147
+ @rails.dig(:action_controller, :session)
177
148
  end
178
149
  end
179
150
  end
@@ -49,7 +49,7 @@ module Brakeman
49
49
  include Brakeman::Util
50
50
 
51
51
  def initialize
52
- @constants = Hash.new { |h, k| h[k] = [] }
52
+ @constants = {}
53
53
  end
54
54
 
55
55
  def size
@@ -103,6 +103,7 @@ module Brakeman
103
103
  end
104
104
 
105
105
  base_name = Constants.get_constant_base_name(name)
106
+ @constants[base_name] ||= []
106
107
  @constants[base_name] << Constant.new(name, value, context)
107
108
  end
108
109
 
data/lib/brakeman/util.rb CHANGED
@@ -346,158 +346,12 @@ module Brakeman::Util
346
346
  @tracker.config.rails_version
347
347
  end
348
348
 
349
- #Return file name related to given warning. Uses +warning.file+ if it exists
350
- def file_for warning, tracker = nil
351
- if tracker.nil?
352
- tracker = @tracker || self.tracker
353
- end
354
-
355
- if warning.file
356
- File.expand_path warning.file, tracker.app_path
357
- elsif warning.template and warning.template.file
358
- warning.template.file
359
- else
360
- case warning.warning_set
361
- when :controller
362
- file_by_name warning.controller, :controller, tracker
363
- when :template
364
- file_by_name warning.template.name, :template, tracker
365
- when :model
366
- file_by_name warning.model, :model, tracker
367
- when :warning
368
- file_by_name warning.class, nil, tracker
369
- else
370
- nil
371
- end
372
- end
373
- end
374
-
375
- #Attempt to determine path to context file based on the reported name
376
- #in the warning.
377
- #
378
- #For example,
379
- #
380
- # file_by_name FileController #=> "/rails/root/app/controllers/file_controller.rb
381
- def file_by_name name, type, tracker = nil
382
- return nil unless name
383
- string_name = name.to_s
384
- name = name.to_sym
385
-
386
- unless type
387
- if string_name =~ /Controller$/
388
- type = :controller
389
- elsif camelize(string_name) == string_name # This is not always true
390
- type = :model
391
- else
392
- type = :template
393
- end
394
- end
395
-
396
- path = tracker.app_path
397
-
398
- case type
399
- when :controller
400
- if tracker.controllers[name]
401
- path = tracker.controllers[name].file
402
- else
403
- path += "/app/controllers/#{underscore(string_name)}.rb"
404
- end
405
- when :model
406
- if tracker.models[name]
407
- path = tracker.models[name].file
408
- else
409
- path += "/app/models/#{underscore(string_name)}.rb"
410
- end
411
- when :template
412
- if tracker.templates[name] and tracker.templates[name].file
413
- path = tracker.templates[name].file
414
- elsif string_name.include? " "
415
- name = string_name.split[0].to_sym
416
- path = file_for tracker, name, :template
417
- else
418
- path = nil
419
- end
420
- end
421
-
422
- path
423
- end
424
-
425
- #Return array of lines surrounding the warning location from the original
426
- #file.
427
- def context_for app_tree, warning, tracker = nil
428
- file = file_for warning, tracker
429
- context = []
430
- return context unless warning.line and file and @app_tree.path_exists? file
431
-
432
- current_line = 0
433
- start_line = warning.line - 5
434
- end_line = warning.line + 5
435
-
436
- start_line = 1 if start_line < 0
437
-
438
- File.open file do |f|
439
- f.each_line do |line|
440
- current_line += 1
441
-
442
- next if line.strip == ""
443
-
444
- if current_line > end_line
445
- break
446
- end
447
-
448
- if current_line >= start_line
449
- context << [current_line, line]
450
- end
451
- end
452
- end
453
-
454
- context
455
- end
456
-
457
- def relative_path file
458
- pname = Pathname.new file
459
- if file and not file.empty? and pname.absolute?
460
- pname.relative_path_from(Pathname.new(@tracker.app_path)).to_s
461
- else
462
- file
463
- end
464
- end
465
-
466
349
  #Convert path/filename to view name
467
350
  #
468
351
  # views/test/something.html.erb -> test/something
469
352
  def template_path_to_name path
470
- names = path.split("/")
353
+ names = path.relative.split("/")
471
354
  names.last.gsub!(/(\.(html|js)\..*|\.(rhtml|haml|erb|slim))$/, '')
472
355
  names[(names.index("views") + 1)..-1].join("/").to_sym
473
356
  end
474
-
475
- def github_url file, line=nil
476
- if repo_url = @tracker.options[:github_url] and file and not file.empty? and file.start_with? '/'
477
- url = "#{repo_url}/#{relative_path(file)}"
478
- url << "#L#{line}" if line
479
- else
480
- nil
481
- end
482
- end
483
-
484
- def truncate_table str
485
- @terminal_width ||= if @tracker.options[:table_width]
486
- @tracker.options[:table_width]
487
- elsif $stdin && $stdin.tty?
488
- Brakeman.load_brakeman_dependency 'highline'
489
- ::HighLine.new.terminal_size[0]
490
- else
491
- 80
492
- end
493
- lines = str.lines
494
-
495
- lines.map do |line|
496
- if line.chomp.length > @terminal_width
497
- line[0..(@terminal_width - 3)] + ">>\n"
498
- else
499
- line
500
- end
501
- end.join
502
- end
503
357
  end
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "4.5.0"
2
+ Version = "4.7.1"
3
3
  end
@@ -9,7 +9,7 @@ class Brakeman::Warning
9
9
  :line, :method, :model, :template, :user_input, :user_input_type,
10
10
  :warning_code, :warning_set, :warning_type
11
11
 
12
- attr_accessor :code, :context, :file, :message, :relative_path
12
+ attr_accessor :code, :context, :file, :message
13
13
 
14
14
  TEXT_CONFIDENCE = {
15
15
  0 => "High",
@@ -34,11 +34,11 @@ class Brakeman::Warning
34
34
  :file => :@file,
35
35
  :gem_info => :@gem_info,
36
36
  :line => :@line,
37
+ :link => :@link,
37
38
  :link_path => :@link_path,
38
39
  :message => :@message,
39
40
  :method => :@method,
40
41
  :model => :@model,
41
- :relative_path => :@relative_path,
42
42
  :template => :@template,
43
43
  :user_input => :@user_input,
44
44
  :warning_set => :@warning_set,
@@ -100,9 +100,11 @@ class Brakeman::Warning
100
100
  unless @warning_set
101
101
  if self.model
102
102
  @warning_set = :model
103
+ @file ||= self.model.file
103
104
  elsif self.template
104
105
  @warning_set = :template
105
106
  @called_from = self.template.render_path
107
+ @file ||= self.template.file
106
108
  elsif self.controller
107
109
  @warning_set = :controller
108
110
  else
@@ -112,6 +114,8 @@ class Brakeman::Warning
112
114
 
113
115
  if options[:warning_code]
114
116
  @warning_code = Brakeman::WarningCodes.code options[:warning_code]
117
+ else
118
+ @warning_code = nil
115
119
  end
116
120
 
117
121
  Brakeman.debug("Warning created without warning code: #{options[:warning_code]}") unless @warning_code
@@ -221,7 +225,7 @@ class Brakeman::Warning
221
225
  when :template
222
226
  @row["Template"] = self.view_name.to_s
223
227
  when :model
224
- @row["Model"] = self.model.to_s
228
+ @row["Model"] = self.model.name.to_s
225
229
  when :controller
226
230
  @row["Controller"] = self.controller.to_s
227
231
  when :warning
@@ -235,7 +239,7 @@ class Brakeman::Warning
235
239
  def to_s
236
240
  output = "(#{TEXT_CONFIDENCE[self.confidence]}) #{self.warning_type} - #{self.message}"
237
241
  output << " near line #{self.line}" if self.line
238
- output << " in #{self.file}" if self.file
242
+ output << " in #{self.file.relative}" if self.file
239
243
  output << ": #{self.format_code}" if self.code
240
244
 
241
245
  output
@@ -247,37 +251,47 @@ class Brakeman::Warning
247
251
  warning_code_string = sprintf("%03d", @warning_code)
248
252
  code_string = @code.inspect
249
253
 
250
- Digest::SHA2.new(256).update("#{warning_code_string}#{code_string}#{location_string}#{@relative_path}#{self.confidence}").to_s
254
+ Digest::SHA2.new(256).update("#{warning_code_string}#{code_string}#{location_string}#{self.file.relative}#{self.confidence}").to_s
251
255
  end
252
256
 
253
257
  def location include_renderer = true
254
258
  case @warning_set
255
259
  when :template
256
- location = { :type => :template, :template => self.view_name(include_renderer) }
260
+ { :type => :template, :template => self.view_name(include_renderer) }
257
261
  when :model
258
- location = { :type => :model, :model => self.model }
262
+ { :type => :model, :model => self.model.name }
259
263
  when :controller
260
- location = { :type => :controller, :controller => self.controller }
264
+ { :type => :controller, :controller => self.controller }
261
265
  when :warning
262
266
  if self.class
263
- location = { :type => :method, :class => self.class, :method => self.method }
267
+ { :type => :method, :class => self.class, :method => self.method }
264
268
  else
265
- location = nil
269
+ nil
266
270
  end
267
271
  end
268
272
  end
269
273
 
270
- def to_hash
274
+ def relative_path
275
+ self.file.relative
276
+ end
277
+
278
+ def to_hash absolute_paths: true
279
+ if self.called_from and not absolute_paths
280
+ render_path = self.called_from.with_relative_paths
281
+ else
282
+ render_path = self.called_from
283
+ end
284
+
271
285
  { :warning_type => self.warning_type,
272
286
  :warning_code => @warning_code,
273
287
  :fingerprint => self.fingerprint,
274
288
  :check_name => self.check.gsub(/^Brakeman::Check/, ''),
275
289
  :message => self.message.to_s,
276
- :file => self.file,
290
+ :file => (absolute_paths ? self.file.absolute : self.file.relative),
277
291
  :line => self.line,
278
292
  :link => self.link,
279
293
  :code => (@code && self.format_code(false)),
280
- :render_path => self.called_from,
294
+ :render_path => render_path,
281
295
  :location => self.location(false),
282
296
  :user_input => (@user_input && self.format_user_input(false)),
283
297
  :confidence => TEXT_CONFIDENCE[self.confidence]