brakeman 1.9.5 → 2.0.0.pre2

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