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
@@ -54,7 +54,7 @@ class Brakeman::CheckModelSerialize < Brakeman::BaseCheck
54
54
  confidence = :high
55
55
  end
56
56
 
57
- warn :model => model.name,
57
+ warn :model => model,
58
58
  :warning_type => "Remote Code Execution",
59
59
  :warning_code => :CVE_2013_0277,
60
60
  :message => msg("Serialized attributes are vulnerable in ", msg_version(rails_version), ", upgrade to ", msg_version(@upgrade_version), " or patch"),
@@ -22,17 +22,17 @@ class Brakeman::CheckNestedAttributesBypass < Brakeman::BaseCheck
22
22
  if opts = model.options[:accepts_nested_attributes_for]
23
23
  opts.each do |args|
24
24
  if args.any? { |a| allow_destroy? a } and args.any? { |a| reject_if? a }
25
- warn_about_nested_attributes name, model, args
25
+ warn_about_nested_attributes model, args
26
26
  end
27
27
  end
28
28
  end
29
29
  end
30
30
  end
31
31
 
32
- def warn_about_nested_attributes name, model, args
32
+ def warn_about_nested_attributes model, args
33
33
  message = msg(msg_version(rails_version), " does not call ", msg_code(":reject_if"), " option when ", msg_code(":allow_destroy"), " is ", msg_code("false"), " ", msg_cve("CVE-2015-7577"))
34
34
 
35
- warn :model => name,
35
+ warn :model => model,
36
36
  :warning_type => "Nested Attributes",
37
37
  :warning_code => :CVE_2015_7577,
38
38
  :message => message,
@@ -53,6 +53,6 @@ class Brakeman::CheckNestedAttributesBypass < Brakeman::BaseCheck
53
53
  end
54
54
 
55
55
  def workaround?
56
- tracker.check_initializers([], :will_be_destroyed?).any?
56
+ tracker.find_call(method: :will_be_destroyed?).any?
57
57
  end
58
58
  end
@@ -0,0 +1,58 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckReverseTabnabbing < Brakeman::BaseCheck
4
+ Brakeman::Checks.add_optional self
5
+
6
+ @description = "Checks for reverse tabnabbing cases on 'link_to' calls"
7
+
8
+ def run_check
9
+ calls = tracker.find_call :methods => :link_to
10
+ calls.each do |call|
11
+ process_result call
12
+ end
13
+ end
14
+
15
+ def process_result result
16
+ return unless original? result and result[:call].last_arg
17
+
18
+ html_opts = result[:call].last_arg
19
+ return unless hash? html_opts
20
+
21
+ target = hash_access html_opts, :target
22
+ unless target &&
23
+ (string?(target) && target.value == "_blank" ||
24
+ symbol?(target) && target.value == :_blank)
25
+ return
26
+ end
27
+
28
+ target_url = result[:block] ? result[:call].first_arg : result[:call].second_arg
29
+
30
+ # `url_for` and `_path` calls lead to urls on to the same origin.
31
+ # That means that an adversary would need to run javascript on
32
+ # the victim application's domain. If that is the case, the adversary
33
+ # already has the ability to redirect the victim user anywhere.
34
+ # Also statically provided URLs (interpolated or otherwise) are also
35
+ # ignored as they produce many false positives.
36
+ return if !call?(target_url) || target_url.method.match(/^url_for$|_path$/)
37
+
38
+ rel = hash_access html_opts, :rel
39
+ confidence = :medium
40
+
41
+ if rel && string?(rel) then
42
+ rel_opt = rel.value
43
+ return if rel_opt.include?("noopener") && rel_opt.include?("noreferrer")
44
+
45
+ if rel_opt.include?("noopener") ^ rel_opt.include?("noreferrer") then
46
+ confidence = :weak
47
+ end
48
+ end
49
+
50
+ warn :result => result,
51
+ :warning_type => "Reverse Tabnabbing",
52
+ :warning_code => :reverse_tabnabbing,
53
+ :message => msg("When opening a link in a new tab without setting ", msg_code('rel: "noopener noreferrer"'),
54
+ ", the new tab can control the parent tab's location. For example, an attacker could redirect to a phishing page."),
55
+ :confidence => confidence,
56
+ :user_input => rel
57
+ end
58
+ end
@@ -70,7 +70,7 @@ class Brakeman::CheckSanitizeMethods < Brakeman::BaseCheck
70
70
 
71
71
  def check_cve_2018_8048
72
72
  if loofah_vulnerable_cve_2018_8048?
73
- message = msg(msg_version(tracker.config.gem_version(:loofah), "loofah gem"), " is vulnerable (CVE-2018-8048). Upgrade to 2.1.2")
73
+ message = msg(msg_version(tracker.config.gem_version(:loofah), "loofah gem"), " is vulnerable (CVE-2018-8048). Upgrade to 2.2.1")
74
74
 
75
75
  if tracker.find_call(:target => false, :method => :sanitize).any?
76
76
  confidence = :high
@@ -90,7 +90,7 @@ class Brakeman::CheckSanitizeMethods < Brakeman::BaseCheck
90
90
  def loofah_vulnerable_cve_2018_8048?
91
91
  loofah_version = tracker.config.gem_version(:loofah)
92
92
 
93
- loofah_version and loofah_version < "2.1.2"
93
+ loofah_version and loofah_version < "2.2.1"
94
94
  end
95
95
 
96
96
  def warn_sanitizer_cve cve, link, upgrade_version
@@ -35,6 +35,6 @@ class Brakeman::CheckSecrets < Brakeman::BaseCheck
35
35
 
36
36
  def looks_like_secret? name
37
37
  # REST_AUTH_SITE_KEY is the pepper in Devise
38
- name.match /password|secret|(rest_auth_site|api)_key$/i
38
+ name.match(/password|secret|(rest_auth_site|api)_key$/i)
39
39
  end
40
40
  end
@@ -19,10 +19,13 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
19
19
  def run_check
20
20
  settings = tracker.config.session_settings
21
21
 
22
- check_for_issues settings, @app_tree.expand_path("config/environment.rb")
22
+ check_for_issues settings, @app_tree.file_path("config/environment.rb")
23
23
 
24
- ["session_store.rb", "secret_token.rb"].each do |file|
25
- if tracker.initializers[file] and not ignored? file
24
+ session_store = @app_tree.file_path("config/initializers/session_store.rb")
25
+ secret_token = @app_tree.file_path("config/initializers/secret_token.rb")
26
+
27
+ [session_store, secret_token].each do |file|
28
+ if tracker.initializers[file] and not ignored? file.basename
26
29
  process tracker.initializers[file]
27
30
  end
28
31
  end
@@ -42,13 +45,13 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
42
45
  #in Rails 4.x apps
43
46
  def process_attrasgn exp
44
47
  if not tracker.options[:rails3] and exp.target == @session_settings and exp.method == :session=
45
- check_for_issues exp.first_arg, @app_tree.expand_path("config/initializers/session_store.rb")
48
+ check_for_issues exp.first_arg, @app_tree.file_path("config/initializers/session_store.rb")
46
49
  end
47
50
 
48
51
  if tracker.options[:rails3] and settings_target?(exp.target) and
49
52
  (exp.method == :secret_token= or exp.method == :secret_key_base=) and string? exp.first_arg
50
53
 
51
- warn_about_secret_token exp.line, @app_tree.expand_path("config/initializers/secret_token.rb")
54
+ warn_about_secret_token exp.line, @app_tree.file_path("config/initializers/secret_token.rb")
52
55
  end
53
56
 
54
57
  exp
@@ -58,7 +61,7 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
58
61
  #in Rails 3.x apps
59
62
  def process_call exp
60
63
  if tracker.options[:rails3] and settings_target?(exp.target) and exp.method == :session_store
61
- check_for_rails3_issues exp.second_arg, @app_tree.expand_path("config/initializers/session_store.rb")
64
+ check_for_rails3_issues exp.second_arg, @app_tree.file_path("config/initializers/session_store.rb")
62
65
  end
63
66
 
64
67
  exp
@@ -109,10 +112,10 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
109
112
  end
110
113
 
111
114
  def check_secrets_yaml
112
- secrets_file = "config/secrets.yml"
115
+ secrets_file = @app_tree.file_path("config/secrets.yml")
113
116
 
114
- if @app_tree.exists? secrets_file and not ignored? "secrets.yml" and not ignored? "config/*.yml"
115
- yaml = @app_tree.read secrets_file
117
+ if secrets_file.exists? and not ignored? "secrets.yml" and not ignored? "config/*.yml"
118
+ yaml = secrets_file.read
116
119
  require 'date' # https://github.com/dtao/safe_yaml/issues/80
117
120
  require 'safe_yaml/load'
118
121
  begin
@@ -127,7 +130,7 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
127
130
  unless secret.include? "<%="
128
131
  line = yaml.lines.find_index { |l| l.include? secret } + 1
129
132
 
130
- warn_about_secret_token line, @app_tree.expand_path(secrets_file)
133
+ warn_about_secret_token line, @app_tree.file_path(secrets_file)
131
134
  end
132
135
  end
133
136
  end
@@ -163,9 +166,9 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
163
166
 
164
167
  def ignored? file
165
168
  [".", "config", "config/initializers"].each do |dir|
166
- ignore_file = "#{dir}/.gitignore"
169
+ ignore_file = @app_tree.file_path("#{dir}/.gitignore")
167
170
  if @app_tree.exists? ignore_file
168
- input = @app_tree.read(ignore_file)
171
+ input = ignore_file.read
169
172
 
170
173
  return true if input.include? file
171
174
  end
@@ -5,6 +5,11 @@ class Brakeman::CheckSimpleFormat < Brakeman::CheckCrossSiteScripting
5
5
 
6
6
  @description = "Checks for simple_format XSS vulnerability (CVE-2013-6416) in certain versions"
7
7
 
8
+ def initialize *args
9
+ super
10
+ @found_any = false
11
+ end
12
+
8
13
  def run_check
9
14
  if version_between? "4.0.0", "4.0.1"
10
15
  @inspect_arguments = true
@@ -38,7 +38,7 @@ class Brakeman::CheckSkipBeforeFilter < Brakeman::BaseCheck
38
38
  :message => msg("Use whitelist (", msg_code(":only => [..]"), ") when skipping authentication"),
39
39
  :code => filter,
40
40
  :confidence => :medium,
41
- :link => "authentication_whitelist",
41
+ :link_path => "authentication_whitelist",
42
42
  :file => controller.file
43
43
  end
44
44
  end
@@ -21,7 +21,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
21
21
  @sql_targets = [:average, :calculate, :count, :count_by_sql, :delete_all, :destroy_all,
22
22
  :find_by_sql, :maximum, :minimum, :pluck, :sum, :update_all]
23
23
  @sql_targets.concat [:from, :group, :having, :joins, :lock, :order, :reorder, :where] if tracker.options[:rails3]
24
- @sql_targets << :find_by << :find_by! << :not if tracker.options[:rails4]
24
+ @sql_targets.concat [:find_by, :find_by!, :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, :not] if tracker.options[:rails4]
25
+ @sql_targets << :delete_by << :destroy_by if tracker.options[:rails6]
25
26
 
26
27
  if version_between?("2.0.0", "3.9.9") or tracker.config.rails_version.nil?
27
28
  @sql_targets << :first << :last << :all
@@ -71,23 +72,23 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
71
72
  scope_calls = []
72
73
 
73
74
  if version_between?("2.1.0", "3.0.9")
74
- ar_scope_calls(:named_scope) do |name, args|
75
+ ar_scope_calls(:named_scope) do |model, args|
75
76
  call = make_call(nil, :named_scope, args).line(args.line)
76
- scope_calls << scope_call_hash(call, name, :named_scope)
77
+ scope_calls << scope_call_hash(call, model, :named_scope)
77
78
  end
78
79
  elsif version_between?("3.1.0", "9.9.9")
79
- ar_scope_calls(:scope) do |name, args|
80
+ ar_scope_calls(:scope) do |model, args|
80
81
  second_arg = args[2]
81
82
  next unless sexp? second_arg
82
83
 
83
84
  if second_arg.node_type == :iter and node_type? second_arg.block, :block, :call, :safe_call
84
- process_scope_with_block(name, args)
85
+ process_scope_with_block(model, args)
85
86
  elsif call? second_arg
86
87
  call = second_arg
87
- scope_calls << scope_call_hash(call, name, call.method)
88
+ scope_calls << scope_call_hash(call, model, call.method)
88
89
  else
89
90
  call = make_call(nil, :scope, args).line(args.line)
90
- scope_calls << scope_call_hash(call, name, :scope)
91
+ scope_calls << scope_call_hash(call, model, :scope)
91
92
  end
92
93
  end
93
94
  end
@@ -96,37 +97,34 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
96
97
  end
97
98
 
98
99
  def ar_scope_calls(symbol_name = :named_scope, &block)
99
- return_array = []
100
100
  active_record_models.each do |name, model|
101
101
  model_args = model.options[symbol_name]
102
102
  if model_args
103
103
  model_args.each do |args|
104
- yield name, args
105
- return_array << [name, args]
104
+ yield model, args
106
105
  end
107
106
  end
108
107
  end
109
- return_array
110
108
  end
111
109
 
112
- def scope_call_hash(call, name, method)
113
- { :call => call, :location => { :type => :class, :class => name }, :method => :named_scope }
110
+ def scope_call_hash(call, model, method)
111
+ { :call => call, :location => { :type => :class, :class => model.name, :file => model.file }, :method => :named_scope }
114
112
  end
115
113
 
116
114
 
117
- def process_scope_with_block model_name, args
115
+ def process_scope_with_block model, args
118
116
  scope_name = args[1][1]
119
117
  block = args[-1][-1]
120
118
 
121
119
  # Search lambda for calls to query methods
122
120
  if block.node_type == :block
123
121
  find_calls = Brakeman::FindAllCalls.new(tracker)
124
- find_calls.process_source(block, :class => model_name, :method => scope_name)
122
+ find_calls.process_source(block, :class => model.name, :method => scope_name, :file => model.file)
125
123
  find_calls.calls.each { |call| process_result(call) if @sql_targets.include?(call[:method]) }
126
124
  elsif call? block
127
125
  while call? block
128
126
  process_result :target => block.target, :method => block.method, :call => block,
129
- :location => { :type => :class, :class => model_name, :method => scope_name }
127
+ :location => { :type => :class, :class => model.name, :method => scope_name, :file => model.file }
130
128
 
131
129
  block = block.target
132
130
  end
@@ -187,7 +185,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
187
185
  else
188
186
  check_find_arguments call.last_arg
189
187
  end
190
- when :where, :having, :find_by, :find_by!, :not
188
+ when :where, :having, :find_by, :find_by!, :find_or_create_by, :find_or_create_by!, :find_or_initialize_by,:not, :delete_by, :destroy_by
191
189
  check_query_arguments call.arglist
192
190
  when :order, :group, :reorder
193
191
  check_order_arguments call.arglist
@@ -17,7 +17,7 @@ class Brakeman::CheckValidationRegex < Brakeman::BaseCheck
17
17
 
18
18
  def run_check
19
19
  active_record_models.each do |name, model|
20
- @current_model = name
20
+ @current_model = model
21
21
  format_validations = model.options[:validates_format_of]
22
22
 
23
23
  if format_validations
@@ -34,8 +34,8 @@ class Brakeman::CheckXMLDoS < Brakeman::BaseCheck
34
34
  end
35
35
 
36
36
  def has_workaround?
37
- tracker.check_initializers(:"ActiveSupport::XmlMini", :backend=).any? do |match|
38
- arg = match.call.first_arg
37
+ tracker.find_call(target: :"ActiveSupport::XmlMini", method: :backend=).any? do |match|
38
+ arg = match[:call].first_arg
39
39
  if string? arg
40
40
  value = arg.value
41
41
  value == 'Nokogiri' or value == 'LibXML'
@@ -48,21 +48,17 @@ class Brakeman::CheckYAMLParsing < Brakeman::BaseCheck
48
48
  def disabled_xml_parser?
49
49
  if version_between? "0.0.0", "2.3.14"
50
50
  #Look for ActionController::Base.param_parsers.delete(Mime::XML)
51
- params_parser = s(:call,
52
- s(:colon2, s(:const, :ActionController), :Base),
53
- :param_parsers)
54
-
55
- matches = tracker.check_initializers(params_parser, :delete)
51
+ matches = tracker.find_call(target: :"ActionController::Base.param_parsers", method: :delete)
56
52
  else
57
53
  #Look for ActionDispatch::ParamsParser::DEFAULT_PARSERS.delete(Mime::XML)
58
- matches = tracker.check_initializers(:"ActionDispatch::ParamsParser::DEFAULT_PARSERS", :delete)
54
+ matches = tracker.find_call(target: :"ActionDispatch::ParamsParser::DEFAULT_PARSERS", method: :delete)
59
55
  end
60
56
 
61
57
  unless matches.empty?
62
58
  mime_xml = s(:colon2, s(:const, :Mime), :XML)
63
59
 
64
60
  matches.each do |result|
65
- if result.call.first_arg == mime_xml
61
+ if result[:call].first_arg == mime_xml
66
62
  return true
67
63
  end
68
64
  end
@@ -74,18 +70,14 @@ class Brakeman::CheckYAMLParsing < Brakeman::BaseCheck
74
70
  #Look for ActionController::Base.param_parsers[Mime::YAML] = :yaml
75
71
  #in Rails 2.x apps
76
72
  def enabled_yaml_parser?
77
- param_parsers = s(:call,
78
- s(:colon2, s(:const, :ActionController), :Base),
79
- :param_parsers)
80
-
81
- matches = tracker.check_initializers(param_parsers, :[]=)
73
+ matches = tracker.find_call(target: :'ActionController::Base.param_parsers', method: :[]=)
82
74
 
83
75
  mime_yaml = s(:colon2, s(:const, :Mime), :YAML)
84
76
 
85
77
  matches.each do |result|
86
- if result.call.first_arg == mime_yaml and
87
- symbol? result.call.second_arg and
88
- result.call.second_arg.value == :yaml
78
+ if result[:call].first_arg == mime_yaml and
79
+ symbol? result[:call].second_arg and
80
+ result[:call].second_arg.value == :yaml
89
81
 
90
82
  return true
91
83
  end
@@ -96,16 +88,16 @@ class Brakeman::CheckYAMLParsing < Brakeman::BaseCheck
96
88
 
97
89
  def disabled_xml_dangerous_types?
98
90
  if version_between? "0.0.0", "2.3.14"
99
- matches = tracker.check_initializers(:"ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING", :delete)
91
+ matches = tracker.find_call(target: :"ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING", method: :delete)
100
92
  else
101
- matches = tracker.check_initializers(:"ActiveSupport::XmlMini::PARSING", :delete)
93
+ matches = tracker.find_call(target: :"ActiveSupport::XmlMini::PARSING", method: :delete)
102
94
  end
103
95
 
104
96
  symbols_off = false
105
97
  yaml_off = false
106
98
 
107
99
  matches.each do |result|
108
- arg = result.call.first_arg
100
+ arg = result[:call].first_arg
109
101
 
110
102
  if string? arg
111
103
  if arg.value == "yaml"
@@ -24,43 +24,31 @@ class Brakeman::Differ
24
24
  # second pass to cleanup any vulns which have changed in line number only.
25
25
  # Given a list of new warnings, delete pairs of new/fixed vulns that differ
26
26
  # only by line number.
27
- # Horrible O(n^2) performance. Keep n small :-/
28
27
  def second_pass(warnings)
29
- # keep track of the number of elements deleted because the index numbers
30
- # won't update as the list is modified
31
- elements_deleted_offset = 0
28
+ new_fingerprints = Set.new(warnings[:new].map(&method(:fingerprint)))
29
+ fixed_fingerprints = Set.new(warnings[:fixed].map(&method(:fingerprint)))
32
30
 
33
- # dup this list since we will be deleting from it and the iterator gets confused.
34
- # use _with_index for fast deletion as opposed to .reject!{|obj| obj == *_warning}
35
- warnings[:new].dup.each_with_index do |new_warning, new_warning_id|
36
- warnings[:fixed].each_with_index do |fixed_warning, fixed_warning_id|
37
- if eql_except_line_number new_warning, fixed_warning
38
- warnings[:new].delete_at(new_warning_id - elements_deleted_offset)
39
- elements_deleted_offset += 1
40
- warnings[:fixed].delete_at(fixed_warning_id)
41
- break
42
- end
31
+ # Remove warnings which fingerprints are both in :new and :fixed
32
+ shared_fingerprints = new_fingerprints.intersection(fixed_fingerprints)
33
+
34
+ unless shared_fingerprints.empty?
35
+ warnings[:new].delete_if do |warning|
36
+ shared_fingerprints.include?(fingerprint(warning))
37
+ end
38
+
39
+ warnings[:fixed].delete_if do |warning|
40
+ shared_fingerprints.include?(fingerprint(warning))
43
41
  end
44
42
  end
45
43
 
46
44
  warnings
47
45
  end
48
46
 
49
- def eql_except_line_number new_warning, fixed_warning
50
- # can't do this ahead of time, as callers may be expecting a Brakeman::Warning
51
- if new_warning.is_a? Brakeman::Warning
52
- new_warning = new_warning.to_hash
53
- fixed_warning = fixed_warning.to_hash
54
- end
55
-
56
- if new_warning[:fingerprint] and fixed_warning[:fingerprint]
57
- new_warning[:fingerprint] == fixed_warning[:fingerprint]
47
+ def fingerprint(warning)
48
+ if warning.is_a?(Brakeman::Warning)
49
+ warning.fingerprint
58
50
  else
59
- OLD_WARNING_KEYS.each do |attr|
60
- return false if new_warning[attr] != fixed_warning[attr]
61
- end
62
-
63
- true
51
+ warning[:fingerprint]
64
52
  end
65
53
  end
66
54
  end