brakeman 1.9.4 → 1.9.5

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +15 -0
  3. data/README.md +6 -0
  4. data/lib/brakeman.rb +7 -2
  5. data/lib/brakeman/checks/check_link_to.rb +59 -67
  6. data/lib/brakeman/checks/check_mass_assignment.rb +5 -1
  7. data/lib/brakeman/checks/check_session_settings.rb +18 -7
  8. data/lib/brakeman/checks/check_symbol_dos.rb +50 -3
  9. data/lib/brakeman/processors/alias_processor.rb +116 -42
  10. data/lib/brakeman/processors/controller_processor.rb +5 -0
  11. data/lib/brakeman/processors/erb_template_processor.rb +2 -1
  12. data/lib/brakeman/processors/erubis_template_processor.rb +2 -1
  13. data/lib/brakeman/processors/haml_template_processor.rb +2 -1
  14. data/lib/brakeman/processors/lib/find_all_calls.rb +17 -0
  15. data/lib/brakeman/processors/lib/find_return_value.rb +3 -3
  16. data/lib/brakeman/processors/lib/render_helper.rb +1 -1
  17. data/lib/brakeman/processors/slim_template_processor.rb +1 -1
  18. data/lib/brakeman/processors/template_processor.rb +1 -1
  19. data/lib/brakeman/report.rb +60 -151
  20. data/lib/brakeman/report/initializers/faster_csv.rb +7 -0
  21. data/lib/brakeman/report/initializers/multi_json.rb +29 -0
  22. data/lib/brakeman/report/renderer.rb +22 -0
  23. data/lib/brakeman/{templates → report/templates}/controller_overview.html.erb +0 -0
  24. data/lib/brakeman/{templates → report/templates}/controller_warnings.html.erb +0 -0
  25. data/lib/brakeman/{templates → report/templates}/error_overview.html.erb +0 -0
  26. data/lib/brakeman/{templates → report/templates}/header.html.erb +2 -2
  27. data/lib/brakeman/{templates → report/templates}/model_warnings.html.erb +0 -0
  28. data/lib/brakeman/{templates → report/templates}/overview.html.erb +2 -2
  29. data/lib/brakeman/{templates → report/templates}/security_warnings.html.erb +0 -0
  30. data/lib/brakeman/{templates → report/templates}/template_overview.html.erb +0 -0
  31. data/lib/brakeman/{templates → report/templates}/view_warnings.html.erb +0 -0
  32. data/lib/brakeman/{templates → report/templates}/warning_overview.html.erb +0 -0
  33. data/lib/brakeman/scanner.rb +3 -0
  34. data/lib/brakeman/util.rb +6 -0
  35. data/lib/brakeman/version.rb +1 -1
  36. data/lib/brakeman/warning_codes.rb +1 -0
  37. data/lib/ruby_parser/bm_sexp.rb +14 -39
  38. metadata +17 -14
@@ -187,6 +187,11 @@ class Brakeman::ControllerProcessor < Brakeman::BaseProcessor
187
187
  #We build a new method and process that the same way as usual
188
188
  #methods and filters.
189
189
  def add_fake_filter exp
190
+ unless @controller
191
+ Brakeman.debug "Skipping before_filter outside controller: #{exp}"
192
+ return exp
193
+ end
194
+
190
195
  filter_name = ("fake_filter" + rand.to_s[/\d+$/]).to_sym
191
196
  args = exp.block_call.arglist
192
197
  args.insert(1, Sexp.new(:lit, filter_name))
@@ -48,7 +48,7 @@ class Brakeman::ErbTemplateProcessor < Brakeman::TemplateProcessor
48
48
  else
49
49
  #TODO: Is it really necessary to create a new Sexp here?
50
50
  call = make_call target, method, process_all!(exp.args)
51
- call.original_line(exp.original_line)
51
+ call.original_line = exp.original_line
52
52
  call.line(exp.line)
53
53
  call
54
54
  end
@@ -56,6 +56,7 @@ class Brakeman::ErbTemplateProcessor < Brakeman::TemplateProcessor
56
56
 
57
57
  #Process block, removing irrelevant expressions
58
58
  def process_block exp
59
+ exp = exp.dup
59
60
  exp.shift
60
61
  if @inside_concat
61
62
  @inside_concat = false
@@ -47,7 +47,7 @@ class Brakeman::ErubisTemplateProcessor < Brakeman::TemplateProcessor
47
47
  else
48
48
  #TODO: Is it really necessary to create a new Sexp here?
49
49
  call = make_call target, method, process_all!(exp.args)
50
- call.original_line(exp.original_line)
50
+ call.original_line = exp.original_line
51
51
  call.line(exp.line)
52
52
  call
53
53
  end
@@ -55,6 +55,7 @@ class Brakeman::ErubisTemplateProcessor < Brakeman::TemplateProcessor
55
55
 
56
56
  #Process blocks, ignoring :ignore exps
57
57
  def process_block exp
58
+ exp = exp.dup
58
59
  exp.shift
59
60
  exp.map! do |e|
60
61
  res = process e
@@ -95,7 +95,7 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
95
95
  else
96
96
  #TODO: Do we really need a new Sexp here?
97
97
  call = make_call target, method, process_all!(exp.args)
98
- call.original_line(exp.original_line)
98
+ call.original_line = exp.original_line
99
99
  call.line(exp.line)
100
100
  call
101
101
  end
@@ -103,6 +103,7 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
103
103
 
104
104
  #If inside an output stream, only return the final expression
105
105
  def process_block exp
106
+ exp = exp.dup
106
107
  exp.shift
107
108
  if @inside_concat
108
109
  @inside_concat = false
@@ -97,6 +97,23 @@ class Brakeman::FindAllCalls < Brakeman::BaseProcessor
97
97
  exp
98
98
  end
99
99
 
100
+ #:"string" is equivalent to "string".to_sym
101
+ def process_dsym exp
102
+ exp.each { |arg| process arg if sexp? arg }
103
+
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
113
+
114
+ exp
115
+ end
116
+
100
117
  #Process an assignment like a call
101
118
  def process_attrasgn exp
102
119
  process_call exp
@@ -28,7 +28,7 @@ class Brakeman::FindReturnValue
28
28
  def get_return_value exp, env = nil
29
29
  process_method exp, env
30
30
  value = make_return_value
31
- value.original_line(exp.line)
31
+ value.original_line = exp.line
32
32
  value
33
33
  end
34
34
 
@@ -91,7 +91,7 @@ class Brakeman::FindReturnValue
91
91
 
92
92
  if true_branch and false_branch
93
93
  value = make_or(true_branch, false_branch)
94
- value.original_line(value.rhs.line)
94
+ value.original_line = value.rhs.line
95
95
  value
96
96
  else #Unlikely?
97
97
  true_branch or false_branch
@@ -102,7 +102,7 @@ class Brakeman::FindReturnValue
102
102
  when :return
103
103
  exp.value
104
104
  else
105
- exp.original_line(exp.line) unless exp.original_line
105
+ exp.original_line = exp.line unless exp.original_line
106
106
  exp
107
107
  end
108
108
  end
@@ -120,7 +120,7 @@ module Brakeman::RenderHelper
120
120
  unless value.original_line
121
121
  #TODO: This has been broken for a while now and no one noticed
122
122
  #so maybe we can skip it
123
- value.original_line(value.line)
123
+ value.original_line = value.line
124
124
  end
125
125
  end
126
126
 
@@ -39,7 +39,7 @@ class Brakeman::SlimTemplateProcessor < Brakeman::TemplateProcessor
39
39
  make_render_in_view exp
40
40
  else
41
41
  call = make_call target, method, process_all!(exp.args)
42
- call.original_line(exp.original_line)
42
+ call.original_line = exp.original_line
43
43
  call.line(exp.line)
44
44
  call
45
45
  end
@@ -47,7 +47,7 @@ class Brakeman::TemplateProcessor < Brakeman::BaseProcessor
47
47
 
48
48
  #Adds output to the list of outputs.
49
49
  def process_output exp
50
- process exp.value
50
+ exp.value = process exp.value
51
51
  @current_template[:outputs] << exp
52
52
  exp
53
53
  end
@@ -7,42 +7,8 @@ require 'highline/system_extensions'
7
7
  require "csv"
8
8
  require 'multi_json'
9
9
  require 'brakeman/version'
10
-
11
- if CSV.const_defined? :Reader
12
- # Ruby 1.8 compatible
13
- require 'fastercsv'
14
- Object.send(:remove_const, :CSV)
15
- CSV = FasterCSV
16
- else
17
- # CSV is now FasterCSV in ruby 1.9
18
- end
19
-
20
- #MultiJson interface changed in 1.3.0, but need
21
- #to support older MultiJson for Rails 3.1.
22
- if MultiJson.respond_to? :default_adapter
23
- mj_engine = MultiJson.default_adapter
24
- else
25
- mj_engine = MultiJson.default_engine
26
-
27
- module MultiJson
28
- def self.dump *args
29
- encode *args
30
- end
31
-
32
- def self.load *args
33
- decode *args
34
- end
35
- end
36
- end
37
-
38
- #This is so OkJson will work with symbol values
39
- if mj_engine == :ok_json
40
- class Symbol
41
- def to_json
42
- self.to_s.inspect
43
- end
44
- end
45
- end
10
+ require 'brakeman/report/renderer'
11
+ Dir.glob(File.dirname(__FILE__) + '/report/initializers/*.rb').each {|file| require file }
46
12
 
47
13
  #Generates a report based on the Tracker and the results of
48
14
  #Tracker#run_checks. Be sure to +run_checks+ before generating
@@ -71,7 +37,14 @@ class Brakeman::Report
71
37
  warnings = all_warnings.length
72
38
 
73
39
  if html
74
- load_and_render_erb('overview', binding)
40
+ locals = {
41
+ :tracker => tracker,
42
+ :warnings => warnings,
43
+ :warnings_summary => warnings_summary,
44
+ :number_of_templates => number_of_templates(@tracker),
45
+ }
46
+
47
+ Brakeman::Report::Renderer.new('overview', :locals => locals).render
75
48
  else
76
49
  Terminal::Table.new(:headings => ['Scanned/Reported', 'Total']) do |t|
77
50
  t.add_row ['Controllers', tracker.controllers.length]
@@ -87,34 +60,26 @@ class Brakeman::Report
87
60
  def generate_warning_overview html = false
88
61
  types = warnings_summary.keys
89
62
  types.delete :high_confidence
63
+ values = types.sort.collect{|warning_type| [warning_type, warnings_summary[warning_type]] }
64
+ locals = {:types => types, :warnings_summary => warnings_summary}
90
65
 
91
- unless types.empty?
92
- if html
93
- load_and_render_erb('warning_overview', binding)
94
- else
95
- Terminal::Table.new(:headings => ['Warning Type', 'Total']) do |t|
96
- types.sort.each do |warning_type|
97
- t.add_row [warning_type, warnings_summary[warning_type]]
98
- end
99
- end
100
- end
101
- end
66
+ render_array('warning_overview', ['Warning Type', 'Total'], values, locals, html)
102
67
  end
103
68
 
104
69
  #Generate table of errors or return nil if no errors
105
70
  def generate_errors html = false
106
- if tracker.errors.any?
107
- if html
108
- load_and_render_erb('error_overview', binding)
109
- else
110
- Terminal::Table.new(:headings => ['Error', 'Location']) do |t|
111
- tracker.errors.each do |error|
112
- t.add_row [error[:error], error[:backtrace][0]]
113
- end
114
- end
115
- end
71
+ values = tracker.errors.collect{|error| [error[:error], error[:backtrace][0]]}
72
+ render_array('error_overview', ['Error', 'Location'], values, {:tracker => tracker}, html)
73
+ end
74
+
75
+ def render_array(template, headings, value_array, locals, html = false)
76
+ return if value_array.empty?
77
+ if html
78
+ Brakeman::Report::Renderer.new(template, :locals => locals).render
116
79
  else
117
- nil
80
+ Terminal::Table.new(:headings => headings) do |t|
81
+ value_array.each { |value_row| t.add_row value_row }
82
+ end
118
83
  end
119
84
  end
120
85
 
@@ -139,17 +104,9 @@ class Brakeman::Report
139
104
  stabilizer = 0
140
105
  warning_messages = warning_messages.sort_by{|row| stabilizer += 1; [row['Confidence'], row['Warning Type'], row['Class'], stabilizer]}
141
106
 
142
- unless warning_messages.empty?
143
- if html
144
- load_and_render_erb('security_warnings', binding)
145
- else
146
- Terminal::Table.new(:headings => ["Confidence", "Class", "Method", "Warning Type", "Message"]) do |t|
147
- warning_messages.each do |row|
148
- t.add_row [row["Confidence"], row["Class"], row["Method"], row["Warning Type"], row["Message"]]
149
- end
150
- end
151
- end
152
- end
107
+ locals = {:warning_messages => warning_messages}
108
+ values = warning_messages.collect{|row| [row["Confidence"], row["Class"], row["Method"], row["Warning Type"], row["Message"]] }
109
+ render_array('security_warnings', ["Confidence", "Class", "Method", "Warning Type", "Message"], values, locals, html)
153
110
  end
154
111
 
155
112
  #Generate table of template warnings or return nil if no warnings
@@ -177,15 +134,10 @@ class Brakeman::Report
177
134
 
178
135
  stabilizer = 0
179
136
  warnings = warnings.sort_by{|row| stabilizer += 1; [row["Confidence"], row["Warning Type"], row["Template"], stabilizer]}
180
- if html
181
- load_and_render_erb('view_warnings', binding)
182
- else
183
- Terminal::Table.new(:headings => ["Confidence", "Template", "Warning Type", "Message"]) do |t|
184
- warnings.each do |warning|
185
- t.add_row [warning["Confidence"], warning["Template"], warning["Warning Type"], warning["Message"]]
186
- end
187
- end
188
- end
137
+
138
+ locals = {:warnings => warnings}
139
+ values = warnings.collect{|warning| [warning["Confidence"], warning["Template"], warning["Warning Type"], warning["Message"]] }
140
+ render_array('view_warnings', ["Confidence", "Template", "Warning Type", "Message"], values, locals, html)
189
141
  else
190
142
  nil
191
143
  end
@@ -214,15 +166,9 @@ class Brakeman::Report
214
166
  stabilizer = 0
215
167
  warnings = warnings.sort_by{|row| stabilizer +=1; [row["Confidence"],row["Warning Type"], row["Model"], stabilizer]}
216
168
 
217
- if html
218
- load_and_render_erb('model_warnings', binding)
219
- else
220
- Terminal::Table.new(:headings => ["Confidence", "Model", "Warning Type", "Message"]) do |t|
221
- warnings.each do |warning|
222
- t.add_row [warning["Confidence"], warning["Model"], warning["Warning Type"], warning["Message"]]
223
- end
224
- end
225
- end
169
+ locals = {:warnings => warnings}
170
+ values = warnings.collect{|warning| [warning["Confidence"], warning["Model"], warning["Warning Type"], warning["Message"]] }
171
+ render_array('model_warnings', ["Confidence", "Model", "Warning Type", "Message"], values, locals, html)
226
172
  else
227
173
  nil
228
174
  end
@@ -252,15 +198,9 @@ class Brakeman::Report
252
198
  stabilizer = 0
253
199
  warnings = warnings.sort_by{|row| stabilizer +=1; [row["Confidence"], row["Warning Type"], row["Controller"], stabilizer]}
254
200
 
255
- if html
256
- load_and_render_erb('controller_warnings', binding)
257
- else
258
- Terminal::Table.new(:headings => ["Confidence", "Controller", "Warning Type", "Message"]) do |t|
259
- warnings.each do |warning|
260
- t.add_row [warning["Confidence"], warning["Controller"], warning["Warning Type"], warning["Message"]]
261
- end
262
- end
263
- end
201
+ locals = {:warnings => warnings}
202
+ values = warnings.collect{|warning| [warning["Confidence"], warning["Controller"], warning["Warning Type"], warning["Message"]] }
203
+ render_array('controller_warnings', ["Confidence", "Controller", "Warning Type", "Message"], values, locals, html)
264
204
  else
265
205
  nil
266
206
  end
@@ -301,15 +241,9 @@ class Brakeman::Report
301
241
  end
302
242
  controller_rows = controller_rows.sort_by{|row| row['Name']}
303
243
 
304
- if html
305
- load_and_render_erb('controller_overview', binding)
306
- else
307
- Terminal::Table.new(:headings => ['Name', 'Parent', 'Includes', 'Routes']) do |t|
308
- controller_rows.each do |row|
309
- t.add_row [row['Name'], row['Parent'], row['Includes'], row['Routes']]
310
- end
311
- end
312
- end
244
+ locals = {:controller_rows => controller_rows}
245
+ values = controller_rows.collect{|row| [row['Name'], row['Parent'], row['Includes'], row['Routes']] }
246
+ render_array('controller_overview', ['Name', 'Parent', 'Includes', 'Routes'], values, locals, html)
313
247
  end
314
248
 
315
249
  #Generate listings of templates and their output
@@ -330,7 +264,7 @@ class Brakeman::Report
330
264
  template_rows = template_rows.sort_by{|name, value| name.to_s}
331
265
 
332
266
  if html
333
- load_and_render_erb('template_overview', binding)
267
+ Brakeman::Report::Renderer.new('template_overview', :locals => {:template_rows => template_rows}).render
334
268
  else
335
269
  output = ''
336
270
  template_rows.each do |template|
@@ -356,24 +290,15 @@ class Brakeman::Report
356
290
  generate_warning_overview(true).to_s
357
291
 
358
292
  # Return early if only summarizing
359
- if tracker.options[:summary_only]
360
- return out
361
- end
362
-
363
- if tracker.options[:report_routes] or tracker.options[:debug]
364
- out << generate_controllers(true).to_s
365
- end
366
-
367
- if tracker.options[:debug]
368
- out << generate_templates(true).to_s
369
- end
293
+ return out if tracker.options[:summary_only]
370
294
 
295
+ out << generate_controllers(true).to_s if tracker.options[:report_routes] or tracker.options[:debug]
296
+ out << generate_templates(true).to_s if tracker.options[:debug]
371
297
  out << generate_errors(true).to_s
372
298
  out << generate_warnings(true).to_s
373
299
  out << generate_controller_warnings(true).to_s
374
300
  out << generate_model_warnings(true).to_s
375
301
  out << generate_template_warnings(true).to_s
376
-
377
302
  out << "</body></html>"
378
303
  end
379
304
 
@@ -385,9 +310,7 @@ class Brakeman::Report
385
310
  truncate_table(generate_warning_overview.to_s) << "\n"
386
311
 
387
312
  #Return output early if only summarizing
388
- if tracker.options[:summary_only]
389
- return out
390
- end
313
+ return out if tracker.options[:summary_only]
391
314
 
392
315
  if tracker.options[:report_routes] or tracker.options[:debug]
393
316
  out << "\n+CONTROLLERS+\n" <<
@@ -469,13 +392,9 @@ class Brakeman::Report
469
392
  end
470
393
 
471
394
  def rails_version
472
- if version = tracker.config[:rails_version]
473
- return version
474
- elsif tracker.options[:rails3]
475
- return "3.x"
476
- else
477
- return "Unknown"
478
- end
395
+ return tracker.config[:rails_version] if tracker.config[:rails_version]
396
+ return "3.x" if tracker.options[:rails3]
397
+ "Unknown"
479
398
  end
480
399
 
481
400
  #Return header for HTML output. Uses CSS from tracker.options[:html_style]
@@ -486,7 +405,15 @@ class Brakeman::Report
486
405
  raise "Cannot find CSS stylesheet for HTML: #{tracker.options[:html_style]}"
487
406
  end
488
407
 
489
- load_and_render_erb('header', binding)
408
+ locals = {
409
+ :css => css,
410
+ :tracker => tracker,
411
+ :checks => checks,
412
+ :rails_version => rails_version,
413
+ :brakeman_version => Brakeman::Version
414
+ }
415
+
416
+ Brakeman::Report::Renderer.new('header', :locals => locals).render
490
417
  end
491
418
 
492
419
  #Generate header for text output
@@ -519,13 +446,9 @@ HEADER
519
446
  high_confidence_warnings = 0
520
447
 
521
448
  [all_warnings].each do |warnings|
522
-
523
449
  warnings.each do |warning|
524
450
  summary[warning.warning_type.to_s] += 1
525
-
526
- if warning.confidence == 0
527
- high_confidence_warnings += 1
528
- end
451
+ high_confidence_warnings += 1 if warning.confidence == 0
529
452
  end
530
453
  end
531
454
 
@@ -549,7 +472,6 @@ HEADER
549
472
 
550
473
  if @highlight_user_input and warning.user_input
551
474
  user_input = CGI.escapeHTML(warning.format_user_input)
552
-
553
475
  message.gsub!(user_input, "<span class=\"user_input\">#{user_input}</span>")
554
476
  end
555
477
 
@@ -561,19 +483,13 @@ HEADER
561
483
  context = context_for(@app_tree, warning)
562
484
  full_message = nil
563
485
 
564
- if tracker.options[:message_limit] and
565
- tracker.options[:message_limit] > 0 and
566
- message.length > tracker.options[:message_limit]
567
-
486
+ if tracker.options[:message_limit] and tracker.options[:message_limit] > 0 and message.length > tracker.options[:message_limit]
568
487
  full_message = html_message(warning, message)
569
488
  message = message[0..tracker.options[:message_limit]] << "..."
570
489
  end
571
490
 
572
491
  message = html_message(warning, message)
573
-
574
- if context.empty? and not full_message
575
- return message
576
- end
492
+ return message if context.empty? and not full_message
577
493
 
578
494
  @element_id += 1
579
495
  code_id = "context#@element_id"
@@ -736,11 +652,4 @@ HEADER
736
652
  end
737
653
  end
738
654
 
739
- private
740
-
741
- def load_and_render_erb file, bind
742
- content = File.read(File.expand_path("templates/#{file}.html.erb", File.dirname(__FILE__)))
743
- template = ERB.new(content)
744
- template.result(bind)
745
- end
746
655
  end