brakeman-lib 4.4.0 → 4.7.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +63 -0
  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 +75 -56
  9. data/lib/brakeman/checks/check_content_tag.rb +12 -0
  10. data/lib/brakeman/checks/check_cookie_serialization.rb +22 -0
  11. data/lib/brakeman/checks/check_cross_site_scripting.rb +15 -10
  12. data/lib/brakeman/checks/check_default_routes.rb +5 -0
  13. data/lib/brakeman/checks/check_deserialize.rb +49 -0
  14. data/lib/brakeman/checks/check_dynamic_finders.rb +1 -1
  15. data/lib/brakeman/checks/check_evaluation.rb +0 -1
  16. data/lib/brakeman/checks/check_execute.rb +44 -1
  17. data/lib/brakeman/checks/check_file_access.rb +7 -1
  18. data/lib/brakeman/checks/check_force_ssl.rb +27 -0
  19. data/lib/brakeman/checks/check_header_dos.rb +2 -2
  20. data/lib/brakeman/checks/check_i18n_xss.rb +2 -2
  21. data/lib/brakeman/checks/check_jruby_xml.rb +2 -2
  22. data/lib/brakeman/checks/check_json_parsing.rb +7 -2
  23. data/lib/brakeman/checks/check_link_to_href.rb +6 -1
  24. data/lib/brakeman/checks/check_mail_to.rb +1 -1
  25. data/lib/brakeman/checks/check_mime_type_dos.rb +2 -2
  26. data/lib/brakeman/checks/check_model_attr_accessible.rb +1 -1
  27. data/lib/brakeman/checks/check_model_attributes.rb +12 -50
  28. data/lib/brakeman/checks/check_model_serialize.rb +1 -1
  29. data/lib/brakeman/checks/check_nested_attributes_bypass.rb +4 -4
  30. data/lib/brakeman/checks/check_reverse_tabnabbing.rb +54 -0
  31. data/lib/brakeman/checks/check_sanitize_methods.rb +2 -2
  32. data/lib/brakeman/checks/check_secrets.rb +1 -1
  33. data/lib/brakeman/checks/check_send.rb +0 -1
  34. data/lib/brakeman/checks/check_session_manipulation.rb +0 -1
  35. data/lib/brakeman/checks/check_session_settings.rb +15 -12
  36. data/lib/brakeman/checks/check_simple_format.rb +5 -0
  37. data/lib/brakeman/checks/check_skip_before_filter.rb +1 -1
  38. data/lib/brakeman/checks/check_sql.rb +27 -20
  39. data/lib/brakeman/checks/check_validation_regex.rb +1 -1
  40. data/lib/brakeman/checks/check_xml_dos.rb +2 -2
  41. data/lib/brakeman/checks/check_yaml_parsing.rb +10 -18
  42. data/lib/brakeman/differ.rb +16 -28
  43. data/lib/brakeman/file_parser.rb +6 -8
  44. data/lib/brakeman/file_path.rb +85 -0
  45. data/lib/brakeman/options.rb +7 -0
  46. data/lib/brakeman/parsers/haml_embedded.rb +44 -0
  47. data/lib/brakeman/parsers/slim_embedded.rb +44 -0
  48. data/lib/brakeman/parsers/template_parser.rb +8 -8
  49. data/lib/brakeman/processor.rb +4 -5
  50. data/lib/brakeman/processors/alias_processor.rb +49 -7
  51. data/lib/brakeman/processors/base_processor.rb +10 -7
  52. data/lib/brakeman/processors/controller_alias_processor.rb +10 -7
  53. data/lib/brakeman/processors/controller_processor.rb +9 -13
  54. data/lib/brakeman/processors/gem_processor.rb +10 -2
  55. data/lib/brakeman/processors/haml_template_processor.rb +92 -123
  56. data/lib/brakeman/processors/lib/call_conversion_helper.rb +4 -0
  57. data/lib/brakeman/processors/lib/find_all_calls.rb +27 -4
  58. data/lib/brakeman/processors/lib/find_call.rb +3 -64
  59. data/lib/brakeman/processors/lib/module_helper.rb +8 -8
  60. data/lib/brakeman/processors/lib/processor_helper.rb +3 -3
  61. data/lib/brakeman/processors/lib/rails2_config_processor.rb +4 -4
  62. data/lib/brakeman/processors/lib/rails2_route_processor.rb +2 -2
  63. data/lib/brakeman/processors/lib/rails3_config_processor.rb +3 -3
  64. data/lib/brakeman/processors/lib/rails3_route_processor.rb +2 -2
  65. data/lib/brakeman/processors/lib/render_helper.rb +2 -2
  66. data/lib/brakeman/processors/lib/render_path.rb +18 -1
  67. data/lib/brakeman/processors/library_processor.rb +5 -5
  68. data/lib/brakeman/processors/model_processor.rb +4 -5
  69. data/lib/brakeman/processors/output_processor.rb +5 -0
  70. data/lib/brakeman/processors/slim_template_processor.rb +16 -0
  71. data/lib/brakeman/processors/template_alias_processor.rb +32 -5
  72. data/lib/brakeman/processors/template_processor.rb +14 -10
  73. data/lib/brakeman/report.rb +3 -3
  74. data/lib/brakeman/report/ignore/config.rb +2 -3
  75. data/lib/brakeman/report/ignore/interactive.rb +2 -2
  76. data/lib/brakeman/report/pager.rb +1 -0
  77. data/lib/brakeman/report/report_base.rb +51 -6
  78. data/lib/brakeman/report/report_codeclimate.rb +3 -3
  79. data/lib/brakeman/report/report_hash.rb +1 -1
  80. data/lib/brakeman/report/report_html.rb +2 -2
  81. data/lib/brakeman/report/report_json.rb +1 -24
  82. data/lib/brakeman/report/report_table.rb +20 -4
  83. data/lib/brakeman/report/report_tabs.rb +1 -1
  84. data/lib/brakeman/report/report_text.rb +2 -2
  85. data/lib/brakeman/rescanner.rb +13 -12
  86. data/lib/brakeman/scanner.rb +24 -18
  87. data/lib/brakeman/tracker.rb +35 -7
  88. data/lib/brakeman/tracker/collection.rb +4 -3
  89. data/lib/brakeman/tracker/config.rb +44 -48
  90. data/lib/brakeman/tracker/constants.rb +2 -1
  91. data/lib/brakeman/util.rb +18 -147
  92. data/lib/brakeman/version.rb +1 -1
  93. data/lib/brakeman/warning.rb +27 -13
  94. data/lib/brakeman/warning_codes.rb +4 -0
  95. data/lib/ruby_parser/bm_sexp.rb +1 -1
  96. data/lib/ruby_parser/bm_sexp_processor.rb +1 -0
  97. metadata +58 -43
@@ -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
@@ -5,16 +5,16 @@ module Brakeman
5
5
  class FileParser
6
6
  attr_reader :file_list
7
7
 
8
- def initialize tracker, app_tree
8
+ def initialize tracker
9
9
  @tracker = tracker
10
10
  @timeout = @tracker.options[:parser_timeout]
11
- @app_tree = app_tree
11
+ @app_tree = @tracker.app_tree
12
12
  @file_list = {}
13
13
  end
14
14
 
15
15
  def parse_files list, type
16
16
  read_files list, type do |path, contents|
17
- if ast = parse_ruby(contents, path)
17
+ if ast = parse_ruby(contents, path.relative)
18
18
  ASTFile.new(path, ast)
19
19
  end
20
20
  end
@@ -24,7 +24,9 @@ module Brakeman
24
24
  @file_list[type] ||= []
25
25
 
26
26
  list.each do |path|
27
- result = yield path, read_path(path)
27
+ file = @app_tree.file_path(path)
28
+
29
+ result = yield file, file.read
28
30
  if result
29
31
  @file_list[type] << result
30
32
  end
@@ -46,9 +48,5 @@ module Brakeman
46
48
  nil
47
49
  end
48
50
  end
49
-
50
- def read_path path
51
- @app_tree.read_path path
52
- end
53
51
  end
54
52
  end
@@ -0,0 +1,85 @@
1
+ require 'pathname'
2
+
3
+ module Brakeman
4
+ # Class to represent file paths within Brakeman.
5
+ # FilePath objects track both the relative and absolute paths
6
+ # to make it easier to manage paths.
7
+ class FilePath
8
+ attr_reader :absolute, :relative
9
+ @cache = {}
10
+
11
+ # Create a new FilePath using an AppTree object.
12
+ #
13
+ # Note that if the path is already a FilePath, that path will
14
+ # be returned unaltered.
15
+ #
16
+ # Additionally, paths are cached. If the absolute path already has
17
+ # a FilePath in the cache, that existing FilePath will be returned.
18
+ def self.from_app_tree app_tree, path
19
+ return path if path.is_a? Brakeman::FilePath
20
+
21
+ absolute = app_tree.expand_path(path).freeze
22
+
23
+ if fp = @cache[absolute]
24
+ return fp
25
+ end
26
+
27
+ relative = app_tree.relative_path(path).freeze
28
+
29
+ self.new(absolute, relative).tap { |fp| @cache[absolute] = fp }
30
+ end
31
+
32
+ # Create a new FilePath with the given absolute and relative paths.
33
+ def initialize absolute_path, relative_path
34
+ @absolute = absolute_path
35
+ @relative = relative_path
36
+ end
37
+
38
+ # Just the file name, no path
39
+ def basename
40
+ @basename ||= File.basename(self.relative)
41
+ end
42
+
43
+ # Read file from absolute path.
44
+ def read
45
+ File.read self.absolute
46
+ end
47
+
48
+ # Check if absolute path exists.
49
+ def exists?
50
+ File.exist? self.absolute
51
+ end
52
+
53
+ # Compare FilePaths. Raises an ArgumentError unless both objects are FilePaths.
54
+ def <=> rhs
55
+ raise ArgumentError unless rhs.is_a? Brakeman::FilePath
56
+ self.relative <=> rhs.relative
57
+ end
58
+
59
+ # Compare FilePaths. Raises an ArgumentError unless both objects are FilePaths.
60
+ def == rhs
61
+ return false unless rhs.is_a? Brakeman::FilePath
62
+
63
+ self.absolute == rhs.absolute
64
+ end
65
+
66
+ # Returns a string with the absolute path.
67
+ def to_str
68
+ self.absolute
69
+ end
70
+
71
+ # Returns a string with the absolute path.
72
+ def to_s
73
+ self.to_str
74
+ end
75
+
76
+ def hash
77
+ @hash ||= [@absolute, @relative].hash
78
+ end
79
+
80
+ def eql? rhs
81
+ @absolute == rhs.absolute and
82
+ @relative == rhs.relative
83
+ end
84
+ end
85
+ end
@@ -82,6 +82,13 @@ module Brakeman::Options
82
82
  options[:rails5] = true
83
83
  end
84
84
 
85
+ opts.on "-6", "--rails6", "Force Rails 6 mode" do
86
+ options[:rails3] = true
87
+ options[:rails4] = true
88
+ options[:rails5] = true
89
+ options[:rails6] = true
90
+ end
91
+
85
92
  opts.separator ""
86
93
  opts.separator "Scanning options:"
87
94
 
@@ -0,0 +1,44 @@
1
+ module Brakeman
2
+ module FakeHamlFilter
3
+ # Copied from Haml 4 - force delayed compilation
4
+ def compile(compiler, text)
5
+ filter = self
6
+ compiler.instance_eval do
7
+ text = unescape_interpolation(text).gsub(/(\\+)n/) do |s|
8
+ escapes = $1.size
9
+ next s if escapes % 2 == 0
10
+ ("\\" * (escapes - 1)) + "\n"
11
+ end
12
+ # We need to add a newline at the beginning to get the
13
+ # filter lines to line up (since the Haml filter contains
14
+ # a line that doesn't show up in the source, namely the
15
+ # filter name). Then we need to escape the trailing
16
+ # newline so that the whole filter block doesn't take up
17
+ # too many.
18
+ text = "\n" + text.sub(/\n"\Z/, "\\n\"")
19
+ push_script <<RUBY.rstrip, :escape_html => false
20
+ find_and_preserve(#{filter.inspect}.render_with_options(#{text}, _hamlout.options))
21
+ RUBY
22
+ return
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ # Fake CoffeeScript filter for Haml
29
+ module Haml::Filters::Coffee
30
+ include Haml::Filters::Base
31
+ extend Brakeman::FakeHamlFilter
32
+ end
33
+
34
+ # Fake Markdown filter for Haml
35
+ module Haml::Filters::Markdown
36
+ include Haml::Filters::Base
37
+ extend Brakeman::FakeHamlFilter
38
+ end
39
+
40
+ # Fake Sass filter for Haml
41
+ module Haml::Filters::Sass
42
+ include Haml::Filters::Base
43
+ extend Brakeman::FakeHamlFilter
44
+ end
@@ -0,0 +1,44 @@
1
+ # Fake filters for Slim
2
+ module Slim
3
+ class Embedded
4
+ class TiltEngine
5
+ def on_slim_embedded(engine, body, attrs)
6
+ # Override this method to avoid Slim trying to load sass/scss and failing
7
+ case engine
8
+ when :sass, :scss, :coffee
9
+ tilt_engine = nil # Doesn't really matter, ignored below
10
+ else
11
+ # Original Slim code
12
+ tilt_engine = Tilt[engine] || raise(Temple::FilterError, "Tilt engine #{engine} is not available.")
13
+ end
14
+
15
+ tilt_options = options[engine.to_sym] || {}
16
+ tilt_options[:default_encoding] ||= 'utf-8'
17
+
18
+ [:multi, tilt_render(tilt_engine, tilt_options, collect_text(body)), collect_newlines(body)]
19
+ end
20
+ end
21
+
22
+ class SassEngine
23
+ protected
24
+
25
+ def tilt_render(tilt_engine, tilt_options, text)
26
+ [:dynamic,
27
+ "BrakemanFilter.render(#{text.inspect}, #{self.class})"]
28
+ end
29
+ end
30
+
31
+ class CoffeeEngine < TiltEngine
32
+ protected
33
+
34
+ def tilt_render(tilt_engine, tilt_options, text)
35
+ [:dynamic,
36
+ "BrakemanFilter.render(#{text.inspect}, #{self.class})"]
37
+ end
38
+ end
39
+
40
+ # Override the engine for CoffeeScript, because Slim doesn't have
41
+ # one, it just uses Tilt's
42
+ register :coffee, JavaScriptEngine, engine: CoffeeEngine
43
+ end
44
+ end
@@ -13,7 +13,7 @@ module Brakeman
13
13
  end
14
14
 
15
15
  def parse_template path, text
16
- type = path.match(KNOWN_TEMPLATE_EXTENSIONS)[1].to_sym
16
+ type = path.relative.match(KNOWN_TEMPLATE_EXTENSIONS)[1].to_sym
17
17
  type = :erb if type == :rhtml
18
18
  name = template_path_to_name path
19
19
  Brakeman.debug "Parsing #{path}"
@@ -63,7 +63,7 @@ module Brakeman
63
63
  else
64
64
  ERB.new(text, nil, '-').src
65
65
  end
66
- src.sub!(/^#.*\n/, '') if Brakeman::Scanner::RUBY_1_9
66
+ src.sub!(/^#.*\n/, '')
67
67
  src
68
68
  end
69
69
  end
@@ -75,21 +75,21 @@ module Brakeman
75
75
 
76
76
  def parse_haml path, text
77
77
  Brakeman.load_brakeman_dependency 'haml'
78
- Brakeman.load_brakeman_dependency 'sass'
78
+ require_relative 'haml_embedded'
79
79
 
80
80
  Haml::Engine.new(text,
81
81
  :filename => path,
82
- :escape_html => tracker.config.escape_html?).precompiled.gsub(/([^\\])\\n/, '\1')
82
+ :escape_html => tracker.config.escape_html?,
83
+ :escape_filter_interpolations => tracker.config.escape_filter_interpolations?
84
+ ).precompiled.gsub(/([^\\])\\n/, '\1')
83
85
  rescue Haml::Error => e
84
86
  tracker.error e, ["While compiling HAML in #{path}"] << e.backtrace
85
87
  nil
86
- rescue Sass::SyntaxError => e
87
- tracker.error e, "While processing #{path}"
88
- nil
89
88
  end
90
89
 
91
90
  def parse_slim path, text
92
91
  Brakeman.load_brakeman_dependency 'slim'
92
+ require_relative 'slim_embedded'
93
93
 
94
94
  Slim::Template.new(path,
95
95
  :disable_capture => true,
@@ -97,7 +97,7 @@ module Brakeman
97
97
  end
98
98
 
99
99
  def self.parse_inline_erb tracker, text
100
- fp = Brakeman::FileParser.new(tracker, nil)
100
+ fp = Brakeman::FileParser.new(tracker)
101
101
  tp = self.new(tracker, fp)
102
102
  src = tp.parse_erb '_inline_', text
103
103
  type = tp.erubis? ? :erubis : :erb
@@ -13,8 +13,7 @@ module Brakeman
13
13
  include Util
14
14
 
15
15
  def initialize(app_tree, options)
16
- @app_tree = app_tree
17
- @tracker = Tracker.new(@app_tree, self, options)
16
+ @tracker = Tracker.new(app_tree, self, options)
18
17
  end
19
18
 
20
19
  def tracked_events
@@ -39,7 +38,7 @@ module Brakeman
39
38
  #Process controller source. +file_name+ is used for reporting
40
39
  def process_controller src, file_name
41
40
  if contains_class? src
42
- ControllerProcessor.new(@app_tree, @tracker).process_controller src, file_name
41
+ ControllerProcessor.new(@tracker).process_controller src, file_name
43
42
  else
44
43
  LibraryProcessor.new(@tracker).process_library src, file_name
45
44
  end
@@ -48,7 +47,7 @@ module Brakeman
48
47
  #Process variable aliasing in controller source and save it in the
49
48
  #tracker.
50
49
  def process_controller_alias name, src, only_method = nil, file = nil
51
- ControllerAliasProcessor.new(@app_tree, @tracker, only_method).process_controller name, src, file
50
+ ControllerAliasProcessor.new(@tracker, only_method).process_controller name, src, file
52
51
  end
53
52
 
54
53
  #Process a model source
@@ -91,7 +90,7 @@ module Brakeman
91
90
  def process_initializer file_name, src
92
91
  res = BaseProcessor.new(@tracker).process_file src, file_name
93
92
  res = AliasProcessor.new(@tracker).process_safely res, nil, file_name
94
- @tracker.initializers[Pathname.new(file_name).basename.to_s] = res
93
+ @tracker.initializers[file_name] = res
95
94
  end
96
95
 
97
96
  #Process source for a library file
@@ -20,19 +20,18 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
20
20
  #The recommended usage is:
21
21
  #
22
22
  # AliasProcessor.new.process_safely src
23
- def initialize tracker = nil, file_name = nil
23
+ def initialize tracker = nil, current_file = nil
24
24
  super()
25
25
  @env = SexpProcessor::Environment.new
26
26
  @inside_if = false
27
27
  @ignore_ifs = nil
28
28
  @exp_context = []
29
- @current_module = nil
30
29
  @tracker = tracker #set in subclass as necessary
31
30
  @helper_method_cache = {}
32
31
  @helper_method_info = Hash.new({})
33
32
  @or_depth_limit = (tracker && tracker.options[:branch_limit]) || 5 #arbitrary default
34
33
  @meth_env = nil
35
- @file_name = file_name
34
+ @current_file = current_file
36
35
  set_env_defaults
37
36
  end
38
37
 
@@ -44,8 +43,8 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
44
43
  #
45
44
  #This method returns a new Sexp with variables replaced with their values,
46
45
  #where possible.
47
- def process_safely src, set_env = nil, file_name = nil
48
- @file_name = file_name
46
+ def process_safely src, set_env = nil, current_file = @current_file
47
+ @current_file = current_file
49
48
  @env = set_env || SexpProcessor::Environment.new
50
49
  @result = src.deep_clone
51
50
  process @result
@@ -66,7 +65,11 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
66
65
  end
67
66
  end
68
67
  rescue => err
69
- @tracker.error err if @tracker
68
+ if @tracker
69
+ @tracker.error err
70
+ else
71
+ raise err
72
+ end
70
73
  end
71
74
 
72
75
  result = replace(exp)
@@ -246,6 +249,9 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
246
249
  end
247
250
  env[target_var] = target
248
251
  return first_arg
252
+ elsif new_string? target
253
+ env[target_var] = first_arg
254
+ return first_arg
249
255
  elsif array? target
250
256
  target << first_arg
251
257
  env[target_var] = target
@@ -262,10 +268,19 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
262
268
  unless target.nil?
263
269
  exp = target
264
270
  end
271
+ when :dup
272
+ unless target.nil?
273
+ exp = target
274
+ end
265
275
  when :join
266
276
  if array? target and target.length > 2 and (string? first_arg or first_arg.nil?)
267
277
  exp = process_array_join(target, first_arg)
268
278
  end
279
+ when :!
280
+ # Convert `!!a` to boolean
281
+ if call? target and target.method == :!
282
+ exp = s(:or, s(:true).line(exp.line), s(:false).line(exp.line)).line(exp.line)
283
+ end
269
284
  end
270
285
 
271
286
  exp
@@ -364,6 +379,8 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
364
379
  elsif e.is_a? Symbol
365
380
  local = Sexp.new(:lvar, e)
366
381
  env.current[local] = local
382
+ elsif e.nil? # trailing comma, argument destructuring
383
+ next # Punt for now
367
384
  else
368
385
  raise "Unexpected value in block args: #{e.inspect}"
369
386
  end
@@ -585,6 +602,24 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
585
602
  exp
586
603
  end
587
604
 
605
+ def process_hash exp
606
+ exp = process_default(exp)
607
+
608
+ # Handle { **kw }
609
+ if node_type? exp, :hash
610
+ if exp.any? { |e| node_type? e, :kwsplat and node_type? e.value, :hash }
611
+ kwsplats, rest = exp.partition { |e| node_type? e, :kwsplat and node_type? e.value, :hash }
612
+ exp = Sexp.new.concat(rest).line(exp.line)
613
+
614
+ kwsplats.each do |e|
615
+ exp = process_hash_merge! exp, e.value
616
+ end
617
+ end
618
+ end
619
+
620
+ exp
621
+ end
622
+
588
623
  #Merge values into hash when processing
589
624
  #
590
625
  # h.merge! :something => "value"
@@ -671,7 +706,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
671
706
  if @tracker
672
707
  @tracker.add_constant exp.lhs,
673
708
  exp.rhs,
674
- :file => current_file_name,
709
+ :file => @current_file,
675
710
  :module => @current_module,
676
711
  :class => @current_class,
677
712
  :method => @current_method
@@ -1166,6 +1201,13 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
1166
1201
  call? exp and exp.method == :raise
1167
1202
  end
1168
1203
 
1204
+ STRING_NEW = s(:call, s(:const, :String), :new)
1205
+
1206
+ # String.new ?
1207
+ def new_string? exp
1208
+ exp == STRING_NEW
1209
+ end
1210
+
1169
1211
  #Set variable to given value.
1170
1212
  #Creates "branched" versions of values when appropriate.
1171
1213
  #Avoids creating multiple branched versions inside same