brakeman 1.9.5 → 2.0.0.pre2

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +27 -0
  3. data/README.md +5 -2
  4. data/bin/brakeman +20 -15
  5. data/lib/brakeman.rb +106 -80
  6. data/lib/brakeman/app_tree.rb +22 -11
  7. data/lib/brakeman/call_index.rb +4 -4
  8. data/lib/brakeman/checks/base_check.rb +33 -5
  9. data/lib/brakeman/checks/check_basic_auth.rb +2 -1
  10. data/lib/brakeman/checks/check_content_tag.rb +8 -29
  11. data/lib/brakeman/checks/check_cross_site_scripting.rb +10 -19
  12. data/lib/brakeman/checks/check_deserialize.rb +57 -0
  13. data/lib/brakeman/checks/check_execute.rb +3 -11
  14. data/lib/brakeman/checks/check_file_access.rb +1 -14
  15. data/lib/brakeman/checks/check_forgery_setting.rb +5 -4
  16. data/lib/brakeman/checks/check_link_to.rb +4 -15
  17. data/lib/brakeman/checks/check_link_to_href.rb +1 -8
  18. data/lib/brakeman/checks/check_mass_assignment.rb +6 -2
  19. data/lib/brakeman/checks/check_model_attributes.rb +1 -0
  20. data/lib/brakeman/checks/check_model_serialize.rb +2 -1
  21. data/lib/brakeman/checks/check_render.rb +2 -15
  22. data/lib/brakeman/checks/check_select_tag.rb +1 -1
  23. data/lib/brakeman/checks/check_select_vulnerability.rb +2 -2
  24. data/lib/brakeman/checks/check_send.rb +3 -0
  25. data/lib/brakeman/checks/check_session_settings.rb +2 -3
  26. data/lib/brakeman/checks/check_skip_before_filter.rb +5 -2
  27. data/lib/brakeman/checks/check_sql.rb +59 -50
  28. data/lib/brakeman/checks/check_symbol_dos.rb +5 -14
  29. data/lib/brakeman/checks/check_unsafe_reflection.rb +2 -15
  30. data/lib/brakeman/options.rb +14 -9
  31. data/lib/brakeman/processors/controller_alias_processor.rb +4 -10
  32. data/lib/brakeman/processors/controller_processor.rb +53 -14
  33. data/lib/brakeman/processors/lib/find_all_calls.rb +40 -40
  34. data/lib/brakeman/processors/lib/processor_helper.rb +5 -1
  35. data/lib/brakeman/processors/lib/rails3_config_processor.rb +8 -0
  36. data/lib/brakeman/processors/output_processor.rb +23 -1
  37. data/lib/brakeman/report.rb +28 -14
  38. data/lib/brakeman/scanner.rb +5 -3
  39. data/lib/brakeman/tracker.rb +7 -7
  40. data/lib/brakeman/util.rb +5 -3
  41. data/lib/brakeman/version.rb +1 -1
  42. data/lib/brakeman/warning.rb +12 -9
  43. data/lib/ruby_parser/bm_sexp.rb +5 -1
  44. data/lib/tasks/brakeman.rake +10 -0
  45. metadata +11 -9
  46. data/lib/brakeman/checks/check_yaml_load.rb +0 -55
@@ -13,10 +13,11 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
13
13
 
14
14
  #Process the given source. Provide either class and method being searched
15
15
  #or the template. These names are used when reporting results.
16
- def process_source exp, klass = nil, method = nil, template = nil
17
- @current_class = klass
18
- @current_method = method
19
- @current_template = template
16
+ def process_source exp, opts
17
+ @current_class = opts[:class]
18
+ @current_method = opts[:method]
19
+ @current_template = opts[:template]
20
+ @current_file = opts[:file]
20
21
  process exp
21
22
  end
22
23
 
@@ -48,15 +49,12 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
48
49
  method = exp.method
49
50
  process_call_args exp
50
51
 
51
- call = { :target => target, :method => method, :call => exp, :nested => @in_target, :chain => get_chain(exp) }
52
-
53
- if @current_template
54
- call[:location] = [:template, @current_template]
55
- else
56
- call[:location] = [:class, @current_class, @current_method]
57
- end
58
-
59
- @calls << call
52
+ @calls << { :target => target,
53
+ :method => method,
54
+ :call => exp,
55
+ :nested => @in_target,
56
+ :chain => get_chain(exp),
57
+ :location => make_location }
60
58
 
61
59
  exp
62
60
  end
@@ -66,15 +64,11 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
66
64
  def process_render exp
67
65
  process exp.last if sexp? exp.last
68
66
 
69
- call = { :target => nil, :method => :render, :call => exp, :nested => false }
70
-
71
- if @current_template
72
- call[:location] = [:template, @current_template]
73
- else
74
- call[:location] = [:class, @current_class, @current_method]
75
- end
76
-
77
- @calls << call
67
+ @calls << { :target => nil,
68
+ :method => :render,
69
+ :call => exp,
70
+ :nested => false,
71
+ :location => make_location }
78
72
 
79
73
  exp
80
74
  end
@@ -84,15 +78,11 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
84
78
  def process_dxstr exp
85
79
  process exp.last if sexp? exp.last
86
80
 
87
- call = { :target => nil, :method => :`, :call => exp, :nested => false }
88
-
89
- if @current_template
90
- call[:location] = [:template, @current_template]
91
- else
92
- call[:location] = [:class, @current_class, @current_method]
93
- end
94
-
95
- @calls << call
81
+ @calls << { :target => nil,
82
+ :method => :`,
83
+ :call => exp,
84
+ :nested => false,
85
+ :location => make_location }
96
86
 
97
87
  exp
98
88
  end
@@ -101,15 +91,11 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
101
91
  def process_dsym exp
102
92
  exp.each { |arg| process arg if sexp? arg }
103
93
 
104
- call = { :target => nil, :method => :literal_to_sym, :call => exp, :nested => false }
105
-
106
- if @current_template
107
- call[:location] = [:template, @current_template]
108
- else
109
- call[:location] = [:class, @current_class, @current_method]
110
- end
111
-
112
- @calls << call
94
+ @calls << { :target => nil,
95
+ :method => :literal_to_sym,
96
+ :call => exp,
97
+ :nested => false,
98
+ :location => make_location }
113
99
 
114
100
  exp
115
101
  end
@@ -155,4 +141,18 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
155
141
  [get_target(call)]
156
142
  end
157
143
  end
144
+
145
+ def make_location
146
+ if @current_template
147
+ { :type => :template,
148
+ :template => @current_template,
149
+ :file => @current_file }
150
+ else
151
+ { :type => :class,
152
+ :class => @current_class,
153
+ :method => @current_method,
154
+ :file => @current_file }
155
+ end
156
+
157
+ end
158
158
  end
@@ -40,7 +40,11 @@ module Brakeman::ProcessorHelper
40
40
  @current_module = module_name
41
41
  end
42
42
 
43
- process_all exp.body
43
+ if block_given?
44
+ yield
45
+ else
46
+ process_all exp.body
47
+ end
44
48
 
45
49
  @current_module = prev_module
46
50
 
@@ -72,6 +72,14 @@ class Brakeman::Rails3ConfigProcessor < Brakeman::BaseProcessor
72
72
  level = @tracker.config[:rails]
73
73
  options[0..-2].each do |o|
74
74
  level[o] ||= {}
75
+
76
+ option = level[o]
77
+
78
+ if not option.is_a? Hash
79
+ Brakeman.debug "[Notice] Skipping config setting: #{options.map(&:to_s).join(".")}"
80
+ return exp
81
+ end
82
+
75
83
  level = level[o]
76
84
  end
77
85
 
@@ -14,7 +14,6 @@ class Brakeman::OutputProcessor < Ruby2Ruby
14
14
  end
15
15
 
16
16
  alias process_safely format
17
- alias process_methdef process_defn
18
17
 
19
18
  def process exp
20
19
  begin
@@ -99,6 +98,29 @@ class Brakeman::OutputProcessor < Ruby2Ruby
99
98
  out
100
99
  end
101
100
 
101
+ def process_defn exp
102
+ # Copied from Ruby2Ruby except without the whole
103
+ # "convert methods to attr_*" stuff
104
+ name = exp.shift
105
+ args = process exp.shift
106
+ args = "" if args == "()"
107
+
108
+ exp.shift if exp == s(s(:nil)) # empty it out of a default nil expression
109
+
110
+ body = []
111
+ until exp.empty? do
112
+ body << indent(process(exp.shift))
113
+ end
114
+
115
+ body << indent("# do nothing") if body.empty?
116
+
117
+ body = body.join("\n")
118
+
119
+ return "def #{name}#{args}\n#{body}\nend".gsub(/\n\s*\n+/, "\n")
120
+ end
121
+
122
+ alias process_methdef process_defn
123
+
102
124
  def process_call_with_block exp
103
125
  call = process exp[0]
104
126
  block = process_rlist exp[2..-1]
@@ -3,7 +3,7 @@ require 'set'
3
3
  require 'brakeman/processors/output_processor'
4
4
  require 'brakeman/util'
5
5
  require 'terminal-table'
6
- require 'highline/system_extensions'
6
+ require 'highline'
7
7
  require "csv"
8
8
  require 'multi_json'
9
9
  require 'brakeman/version'
@@ -283,6 +283,26 @@ class Brakeman::Report
283
283
  end
284
284
  end
285
285
 
286
+ # format output from filename or format
287
+ def format(filename_or_format)
288
+ case filename_or_format
289
+ when /\.html/, :to_html
290
+ to_html
291
+ when /\.pdf/, :to_pdf
292
+ to_pdf
293
+ when /\.csv/, :to_csv
294
+ to_csv
295
+ when /\.json/, :to_json
296
+ to_json
297
+ when /\.tabs/, :to_tabs
298
+ to_tabs
299
+ when :to_test
300
+ to_test
301
+ else
302
+ to_s
303
+ end
304
+ end
305
+
286
306
  #Generate HTML output
287
307
  def to_html
288
308
  out = html_header <<
@@ -504,7 +524,7 @@ HEADER
504
524
  message
505
525
  end <<
506
526
  "<table id='#{code_id}' class='context' style='display:none'>" <<
507
- "<caption>#{warning_file(warning, :relative) || ''}</caption>"
527
+ "<caption>#{warning_file(warning) || ''}</caption>"
508
528
 
509
529
  unless context.empty?
510
530
  if warning.line - 1 == 1 or warning.line + 1 == 1
@@ -567,7 +587,7 @@ HEADER
567
587
  checks.send(meth).map do |w|
568
588
  line = w.line || 0
569
589
  w.warning_type.gsub!(/[^\w\s]/, ' ')
570
- "#{warning_file w}\t#{line}\t#{w.warning_type}\t#{category}\t#{w.format_message}\t#{TEXT_CONFIDENCE[w.confidence]}"
590
+ "#{warning_file(w, :absolute)}\t#{line}\t#{w.warning_type}\t#{category}\t#{w.format_message}\t#{TEXT_CONFIDENCE[w.confidence]}"
571
591
  end.join "\n"
572
592
 
573
593
  end.join "\n"
@@ -584,11 +604,6 @@ HEADER
584
604
  report[meth] = @checks.send(meth)
585
605
  report[meth].each do |w|
586
606
  w.message = w.format_message
587
- if w.code
588
- w.code = w.format_code
589
- else
590
- w.code = ""
591
- end
592
607
  w.context = context_for(@app_tree, w).join("\n")
593
608
  end
594
609
  end
@@ -614,10 +629,9 @@ HEADER
614
629
  :security_warnings => all_warnings.length,
615
630
  :start_time => tracker.start_time.to_s,
616
631
  :end_time => tracker.end_time.to_s,
617
- :timestamp => tracker.end_time.to_s,
618
632
  :duration => tracker.duration,
619
633
  :checks_performed => checks.checks_run.sort,
620
- :number_of_controllers =>tracker.controllers.length,
634
+ :number_of_controllers => tracker.controllers.length,
621
635
  # ignore the "fake" model
622
636
  :number_of_models => tracker.models.length - 1,
623
637
  :number_of_templates => number_of_templates(@tracker),
@@ -642,13 +656,13 @@ HEADER
642
656
  Set.new(tracker.templates.map {|k,v| v[:name].to_s[/[^.]+/]}).length
643
657
  end
644
658
 
645
- def warning_file warning, relative = false
659
+ def warning_file warning, absolute = @tracker.options[:absolute_paths]
646
660
  return nil if warning.file.nil?
647
661
 
648
- if @tracker.options[:relative_paths] or relative
649
- relative_path warning.file
650
- else
662
+ if absolute
651
663
  warning.file
664
+ else
665
+ relative_path warning.file
652
666
  end
653
667
  end
654
668
 
@@ -33,7 +33,7 @@ class Brakeman::Scanner
33
33
  @app_tree = Brakeman::AppTree.from_options(options)
34
34
 
35
35
  if !@app_tree.root || !@app_tree.exists?("app")
36
- abort("Please supply the path to a Rails application.")
36
+ raise NoApplication, "Please supply the path to a Rails application."
37
37
  end
38
38
 
39
39
  if @app_tree.exists?("script/rails")
@@ -104,8 +104,8 @@ class Brakeman::Scanner
104
104
  end
105
105
 
106
106
  rescue Exception => e
107
- Brakeman.notify "[Notice] Error while processing config/#{file}"
108
- tracker.error e.exception(e.message + "\nwhile processing Gemfile"), e.backtrace
107
+ Brakeman.notify "[Notice] Error while processing #{path}"
108
+ tracker.error e.exception(e.message + "\nwhile processing #{path}"), e.backtrace
109
109
  end
110
110
 
111
111
  private :process_config_file
@@ -355,4 +355,6 @@ class Brakeman::Scanner
355
355
  def parse_ruby input
356
356
  @ruby_parser.new.parse input
357
357
  end
358
+
359
+ class NoApplication < RuntimeError; end
358
360
  end
@@ -25,7 +25,7 @@ class Brakeman::Tracker
25
25
  @processor = processor
26
26
  @options = options
27
27
 
28
- @config = {}
28
+ @config = { :rails => {} }
29
29
  @templates = {}
30
30
  @controllers = {}
31
31
  #Initialize models with the unknown model so
@@ -86,7 +86,7 @@ class Brakeman::Tracker
86
86
  method_name = "#{definition[1]}.#{method_name}"
87
87
  end
88
88
 
89
- yield definition, set_name, method_name
89
+ yield definition, set_name, method_name, info[:file]
90
90
 
91
91
  end
92
92
  end
@@ -155,12 +155,12 @@ class Brakeman::Tracker
155
155
  def index_call_sites
156
156
  finder = Brakeman::FindAllCalls.new self
157
157
 
158
- self.each_method do |definition, set_name, method_name|
159
- finder.process_source definition, set_name, method_name
158
+ self.each_method do |definition, set_name, method_name, file|
159
+ finder.process_source definition, :class => set_name, :method => method_name, :file => file
160
160
  end
161
161
 
162
162
  self.each_template do |name, template|
163
- finder.process_source template[:src], nil, nil, template
163
+ finder.process_source template[:src], :template => template, :file => template[:file]
164
164
  end
165
165
 
166
166
  @call_index = Brakeman::CallIndex.new finder.calls
@@ -208,7 +208,7 @@ class Brakeman::Tracker
208
208
  method_name = "#{definition[1]}.#{method_name}"
209
209
  end
210
210
 
211
- finder.process_source definition, set_name, method_name
211
+ finder.process_source definition, :class => set_name, :method => method_name, :file => info[:file]
212
212
 
213
213
  end
214
214
  end
@@ -217,7 +217,7 @@ class Brakeman::Tracker
217
217
 
218
218
  if locations.include? :templates
219
219
  self.each_template do |name, template|
220
- finder.process_source template[:src], nil, nil, template
220
+ finder.process_source template[:src], :template => template, :file => template[:file]
221
221
  end
222
222
  end
223
223
 
data/lib/brakeman/util.rb CHANGED
@@ -275,6 +275,8 @@ module Brakeman::Util
275
275
 
276
276
  if warning.file
277
277
  File.expand_path warning.file, tracker.options[:app_path]
278
+ elsif warning.template.is_a? Hash and warning.template[:file]
279
+ warning.template[:file]
278
280
  else
279
281
  case warning.warning_set
280
282
  when :controller
@@ -305,7 +307,7 @@ module Brakeman::Util
305
307
  unless type
306
308
  if string_name =~ /Controller$/
307
309
  type = :controller
308
- elsif camelize(string_name) == string_name
310
+ elsif camelize(string_name) == string_name # This is not always true
309
311
  type = :model
310
312
  else
311
313
  type = :template
@@ -325,7 +327,7 @@ module Brakeman::Util
325
327
  if tracker.models[name] and tracker.models[name][:file]
326
328
  path = tracker.models[name][:file]
327
329
  else
328
- path += "/app/controllers/#{underscore(string_name)}.rb"
330
+ path += "/app/models/#{underscore(string_name)}.rb"
329
331
  end
330
332
  when :template
331
333
  if tracker.templates[name] and tracker.templates[name][:file]
@@ -383,7 +385,7 @@ module Brakeman::Util
383
385
 
384
386
  def truncate_table str
385
387
  @terminal_width ||= if $stdin && $stdin.tty?
386
- ::HighLine::SystemExtensions::terminal_size[0]
388
+ ::HighLine.new.terminal_size[0]
387
389
  else
388
390
  80
389
391
  end
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "1.9.5"
2
+ Version = "2.0.0.pre2"
3
3
  end
@@ -5,9 +5,10 @@ require 'brakeman/warning_codes'
5
5
  #The Warning class stores information about warnings
6
6
  class Brakeman::Warning
7
7
  attr_reader :called_from, :check, :class, :confidence, :controller,
8
- :line, :method, :model, :template, :user_input, :warning_set, :warning_type
8
+ :line, :method, :model, :template, :user_input, :warning_code, :warning_set,
9
+ :warning_type
9
10
 
10
- attr_accessor :code, :context, :file, :message
11
+ attr_accessor :code, :context, :file, :message, :relative_path
11
12
 
12
13
  TEXT_CONFIDENCE = [ "High", "Medium", "Weak" ]
13
14
 
@@ -23,13 +24,14 @@ class Brakeman::Warning
23
24
 
24
25
  result = options[:result]
25
26
  if result
26
- if result[:location][0] == :template #template result
27
- @template ||= result[:location][1]
28
- @code ||= result[:call]
27
+ @code ||= result[:call]
28
+ @file ||= result[:location][:file]
29
+
30
+ if result[:location][:type] == :template #template result
31
+ @template ||= result[:location][:template]
29
32
  else
30
- @class ||= result[:location][1]
31
- @method ||= result[:location][2]
32
- @code ||= result[:call]
33
+ @class ||= result[:location][:class]
34
+ @method ||= result[:location][:method]
33
35
  end
34
36
  end
35
37
 
@@ -160,9 +162,10 @@ class Brakeman::Warning
160
162
 
161
163
  def fingerprint
162
164
  loc = self.location
163
- location_string = loc && loc.sort_by { |k, v| k.to_s }.to_s
165
+ location_string = loc && loc.sort_by { |k, v| k.to_s }.inspect
164
166
  warning_code_string = sprintf("%03d", @warning_code)
165
167
  code_string = @code.inspect
168
+
166
169
  Digest::SHA2.new(256).update("#{warning_code_string}#{code_string}#{location_string}#{@relative_path}#{self.confidence}").to_s
167
170
  end
168
171
 
@@ -371,7 +371,11 @@ class Sexp
371
371
  # s(:call, nil, :p, s(:arglist, s(:lvar, :y))))
372
372
  def block_args
373
373
  expect :iter, :call_with_block
374
- self[2]
374
+ if self[2] == 0 # ?! See https://github.com/presidentbeef/brakeman/issues/331
375
+ return Sexp.new(:args)
376
+ else
377
+ self[2]
378
+ end
375
379
  end
376
380
 
377
381
  def first_param