brakeman-lib 4.5.0 → 4.7.1

Sign up to get free protection for your applications and to get access to all the features.
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]