brakeman-lib 4.4.0 → 4.7.0

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