brakeman-min 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/FEATURES +16 -0
  2. data/README.md +118 -0
  3. data/WARNING_TYPES +69 -0
  4. data/bin/brakeman +269 -0
  5. data/lib/checks.rb +67 -0
  6. data/lib/checks/base_check.rb +353 -0
  7. data/lib/checks/check_cross_site_scripting.rb +324 -0
  8. data/lib/checks/check_default_routes.rb +29 -0
  9. data/lib/checks/check_evaluation.rb +27 -0
  10. data/lib/checks/check_execute.rb +110 -0
  11. data/lib/checks/check_file_access.rb +46 -0
  12. data/lib/checks/check_forgery_setting.rb +42 -0
  13. data/lib/checks/check_mail_to.rb +48 -0
  14. data/lib/checks/check_mass_assignment.rb +72 -0
  15. data/lib/checks/check_model_attributes.rb +36 -0
  16. data/lib/checks/check_nested_attributes.rb +34 -0
  17. data/lib/checks/check_redirect.rb +98 -0
  18. data/lib/checks/check_render.rb +65 -0
  19. data/lib/checks/check_send_file.rb +15 -0
  20. data/lib/checks/check_session_settings.rb +36 -0
  21. data/lib/checks/check_sql.rb +124 -0
  22. data/lib/checks/check_validation_regex.rb +60 -0
  23. data/lib/format/style.css +105 -0
  24. data/lib/processor.rb +83 -0
  25. data/lib/processors/alias_processor.rb +384 -0
  26. data/lib/processors/base_processor.rb +237 -0
  27. data/lib/processors/config_processor.rb +146 -0
  28. data/lib/processors/controller_alias_processor.rb +237 -0
  29. data/lib/processors/controller_processor.rb +202 -0
  30. data/lib/processors/erb_template_processor.rb +84 -0
  31. data/lib/processors/erubis_template_processor.rb +62 -0
  32. data/lib/processors/haml_template_processor.rb +131 -0
  33. data/lib/processors/lib/find_call.rb +176 -0
  34. data/lib/processors/lib/find_model_call.rb +39 -0
  35. data/lib/processors/lib/processor_helper.rb +36 -0
  36. data/lib/processors/lib/render_helper.rb +137 -0
  37. data/lib/processors/library_processor.rb +118 -0
  38. data/lib/processors/model_processor.rb +125 -0
  39. data/lib/processors/output_processor.rb +233 -0
  40. data/lib/processors/params_processor.rb +77 -0
  41. data/lib/processors/route_processor.rb +338 -0
  42. data/lib/processors/template_alias_processor.rb +86 -0
  43. data/lib/processors/template_processor.rb +55 -0
  44. data/lib/report.rb +651 -0
  45. data/lib/scanner.rb +215 -0
  46. data/lib/scanner_erubis.rb +43 -0
  47. data/lib/tracker.rb +144 -0
  48. data/lib/util.rb +141 -0
  49. data/lib/version.rb +1 -0
  50. data/lib/warning.rb +97 -0
  51. metadata +141 -0
@@ -0,0 +1,86 @@
1
+ require 'set'
2
+ require 'processors/alias_processor'
3
+ require 'processors/lib/render_helper'
4
+
5
+ #Processes aliasing in templates.
6
+ #Handles calls to +render+.
7
+ class TemplateAliasProcessor < AliasProcessor
8
+ include RenderHelper
9
+
10
+ FORM_METHODS = Set.new([:form_for, :remote_form_for, :form_remote_for])
11
+
12
+ def initialize tracker, template
13
+ super()
14
+ @tracker = tracker
15
+ @template = template
16
+ end
17
+
18
+ #Process template
19
+ def process_template name, args
20
+ super name, args, "Template:#{@template[:name]}"
21
+ end
22
+
23
+ #Determine template name
24
+ def template_name name
25
+ unless name.to_s.include? "/"
26
+ name = "#{@template[:name].to_s.match(/^(.*\/).*$/)[1]}#{name}"
27
+ end
28
+ name
29
+ end
30
+
31
+ #Looks for form methods and iterating over collections of Models
32
+ def process_call_with_block exp
33
+ process_default exp
34
+
35
+ call = exp[1]
36
+ target = call[1]
37
+ method = call[2]
38
+ args = exp[2]
39
+ block = exp[3]
40
+
41
+ #Check for e.g. Model.find.each do ... end
42
+ if method == :each and args and block and model = get_model_target(target)
43
+ if sexp? args and args.node_type == :lasgn
44
+ if model == target[1]
45
+ env[Sexp.new(:lvar, args[1])] = Sexp.new(:call, model, :new, Sexp.new(:arglist))
46
+ else
47
+ env[Sexp.new(:lvar, args[1])] = Sexp.new(:call, Sexp.new(:const, Tracker::UNKNOWN_MODEL), :new, Sexp.new(:arglist))
48
+ end
49
+
50
+ process block if sexp? block
51
+ end
52
+ elsif FORM_METHODS.include? method
53
+ if sexp? args and args.node_type == :lasgn
54
+ env[Sexp.new(:lvar, args[1])] = Sexp.new(:call, Sexp.new(:const, :FormBuilder), :new, Sexp.new(:arglist))
55
+
56
+ process block if sexp? block
57
+ end
58
+ end
59
+
60
+ exp
61
+ end
62
+
63
+ alias process_iter process_call_with_block
64
+
65
+ #Checks if +exp+ is a call to Model.all or Model.find*
66
+ def get_model_target exp
67
+ if call? exp
68
+ target = exp[1]
69
+
70
+ if exp[2] == :all or exp[2].to_s[0,4] == "find"
71
+ models = Set.new @tracker.models.keys
72
+
73
+ begin
74
+ name = class_name target
75
+ return target if models.include?(name)
76
+ rescue StandardError
77
+ end
78
+
79
+ end
80
+
81
+ return get_model_target(target)
82
+ end
83
+
84
+ false
85
+ end
86
+ end
@@ -0,0 +1,55 @@
1
+ require 'processors/base_processor'
2
+
3
+ #Base Processor for templates/views
4
+ class TemplateProcessor < BaseProcessor
5
+
6
+ #Initializes template information.
7
+ def initialize tracker, template_name, called_from = nil, file_name = nil
8
+ super(tracker)
9
+ @current_template = { :name => template_name,
10
+ :caller => called_from,
11
+ :partial => template_name.to_s[0,1] == "_",
12
+ :outputs => [],
13
+ :src => nil, #set in Processor
14
+ :type => nil, #set in Processor
15
+ :file => file_name }
16
+ if called_from
17
+ template_name = (template_name.to_s + "." + called_from.to_s).to_sym
18
+ end
19
+
20
+ tracker.templates[template_name] = @current_template
21
+
22
+ @inside_concat = false
23
+ self.warn_on_default = false
24
+ end
25
+
26
+ #Process the template Sexp.
27
+ def process exp
28
+ begin
29
+ super
30
+ rescue Exception => e
31
+ except = e.exception("Error when processing #{@current_template[:name]}: #{e.message}")
32
+ except.set_backtrace(e.backtrace)
33
+ raise except
34
+ end
35
+ end
36
+
37
+ #Ignore initial variable assignment
38
+ def process_lasgn exp
39
+ if exp[1] == :_erbout and exp[2].node_type == :str #ignore
40
+ ignore
41
+ elsif exp[1] == :_buf and exp[2].node_type == :str
42
+ ignore
43
+ else
44
+ exp[2] = process exp[2]
45
+ exp
46
+ end
47
+ end
48
+
49
+ #Adds output to the list of outputs.
50
+ def process_output exp
51
+ process exp[1]
52
+ @current_template[:outputs] << exp
53
+ exp
54
+ end
55
+ end
data/lib/report.rb ADDED
@@ -0,0 +1,651 @@
1
+ require 'cgi'
2
+ require 'set'
3
+ require 'processors/output_processor'
4
+ require 'util'
5
+
6
+ #Generates a report based on the Tracker and the results of
7
+ #Tracker#run_checks. Be sure to +run_checks+ before generating
8
+ #a report.
9
+ class Report
10
+ include Util
11
+
12
+ attr_reader :tracker, :checks
13
+
14
+ TEXT_CONFIDENCE = [ "High", "Medium", "Weak" ]
15
+ HTML_CONFIDENCE = [ "<span class='high-confidence'>High</span>",
16
+ "<span class='med-confidence'>Medium</span>",
17
+ "<span class='weak-confidence'>Weak</span>" ]
18
+
19
+ def initialize tracker
20
+ @tracker = tracker
21
+ @checks = tracker.checks
22
+ @element_id = 0 #Used for HTML ids
23
+ @warnings_summary = nil
24
+ end
25
+
26
+ #Generate summary table of what was parsed
27
+ def generate_overview
28
+ templates = Set.new(@tracker.templates.map {|k,v| v[:name].to_s[/[^.]+/]}).length
29
+ warnings = checks.warnings.length +
30
+ checks.controller_warnings.length +
31
+ checks.model_warnings.length +
32
+ checks.template_warnings.length
33
+
34
+ #Add number of high confidence warnings in summary.
35
+ #Skipping for CSV because it makes the cell text instead of
36
+ #a number.
37
+ unless OPTIONS[:output_format] == :to_csv
38
+ summary = warnings_summary
39
+
40
+ if OPTIONS[:output_format] == :to_html
41
+ warnings = "#{warnings} <span class='high-confidence'>(#{summary[:high_confidence]})</span>"
42
+ else
43
+ warnings = "#{warnings} (#{summary[:high_confidence]})"
44
+ end
45
+ end
46
+
47
+ table = Ruport::Data::Table(["Scanned/Reported", "Total"])
48
+ table << { "Scanned/Reported" => "Controllers", "Total" => tracker.controllers.length }
49
+ #One less because of the 'fake' one used for unknown models
50
+ table << { "Scanned/Reported" => "Models", "Total" => tracker.models.length - 1 }
51
+ table << { "Scanned/Reported" => "Templates", "Total" => templates }
52
+ table << { "Scanned/Reported" => "Errors", "Total" => tracker.errors.length }
53
+ table << { "Scanned/Reported" => "Security Warnings", "Total" => warnings}
54
+ end
55
+
56
+ #Generate table of how many warnings of each warning type were reported
57
+ def generate_warning_overview
58
+ table = Ruport::Data::Table(["Warning Type", "Total"])
59
+ types = warnings_summary.keys
60
+ types.delete :high_confidence
61
+ types.sort.each do |warning_type|
62
+ table << { "Warning Type" => warning_type, "Total" => warnings_summary[warning_type] }
63
+ end
64
+ table
65
+ end
66
+
67
+ #Generate table of errors or return nil if no errors
68
+ def generate_errors
69
+ unless tracker.errors.empty?
70
+ table = Ruport::Data::Table(["Error", "Location"])
71
+ tracker.errors.each do |w|
72
+ p w if OPTIONS[:debug]
73
+ table << { "Error" => w[:error], "Location" => w[:backtrace][0] }
74
+ end
75
+
76
+ table
77
+ else
78
+ nil
79
+ end
80
+ end
81
+
82
+ #Generate table of general security warnings
83
+ def generate_warnings
84
+ table = Ruport::Data::Table(["Confidence", "Class", "Method", "Warning Type", "Message"])
85
+ checks.warnings.each do |warning|
86
+ next if warning.confidence > OPTIONS[:min_confidence]
87
+ w = warning.to_row
88
+
89
+ if OPTIONS[:output_format] == :to_html
90
+ w["Confidence"] = HTML_CONFIDENCE[w["Confidence"]]
91
+ w["Message"] = with_context warning, w["Message"]
92
+ else
93
+ w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]]
94
+ end
95
+
96
+ table << w
97
+ end
98
+
99
+ table.sort_rows_by! "Class"
100
+ table.sort_rows_by! "Warning Type"
101
+ table.sort_rows_by! "Confidence"
102
+
103
+ if table.empty?
104
+ table = Ruport::Data::Table("General Warnings")
105
+ table << { "General Warnings" => "[NONE]" }
106
+ end
107
+
108
+ table
109
+ end
110
+
111
+ #Generate table of template warnings or return nil if no warnings
112
+ def generate_template_warnings
113
+ unless checks.template_warnings.empty?
114
+ table = Ruport::Data::Table(["Confidence", "Template", "Warning Type", "Message"])
115
+ checks.template_warnings.each do |warning|
116
+ next if warning.confidence > OPTIONS[:min_confidence]
117
+ w = warning.to_row :template
118
+
119
+ if OPTIONS[:output_format] == :to_html
120
+ w["Confidence"] = HTML_CONFIDENCE[w["Confidence"]]
121
+ w["Message"] = with_context warning, w["Message"]
122
+ else
123
+ w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]]
124
+ end
125
+
126
+ table << w
127
+ end
128
+
129
+ if table.empty?
130
+ nil
131
+ else
132
+ table.sort_rows_by! "Template"
133
+ table.sort_rows_by! "Warning Type"
134
+ table.sort_rows_by! "Confidence"
135
+ table.to_group "View Warnings"
136
+ end
137
+ else
138
+ nil
139
+ end
140
+ end
141
+
142
+ #Generate table of model warnings or return nil if no warnings
143
+ def generate_model_warnings
144
+ unless checks.model_warnings.empty?
145
+ table = Ruport::Data::Table(["Confidence", "Model", "Warning Type", "Message"])
146
+ checks.model_warnings.each do |warning|
147
+ next if warning.confidence > OPTIONS[:min_confidence]
148
+ w = warning.to_row :model
149
+
150
+ if OPTIONS[:output_format] == :to_html
151
+ w["Confidence"] = HTML_CONFIDENCE[w["Confidence"]]
152
+ w["Message"] = with_context warning, w["Message"]
153
+ else
154
+ w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]]
155
+ end
156
+
157
+ table << w
158
+ end
159
+
160
+ if table.empty?
161
+ nil
162
+ else
163
+ table.sort_rows_by! "Model"
164
+ table.sort_rows_by! "Warning Type"
165
+ table.sort_rows_by! "Confidence"
166
+ table.to_group "Model Warnings"
167
+ end
168
+ else
169
+ nil
170
+ end
171
+ end
172
+
173
+ #Generate table of controller warnings or nil if no warnings
174
+ def generate_controller_warnings
175
+ unless checks.controller_warnings.empty?
176
+ table = Ruport::Data::Table(["Confidence", "Controller", "Warning Type", "Message"])
177
+ checks.controller_warnings.each do |warning|
178
+ next if warning.confidence > OPTIONS[:min_confidence]
179
+ w = warning.to_row :controller
180
+
181
+ if OPTIONS[:output_format] == :to_html
182
+ w["Confidence"] = HTML_CONFIDENCE[w["Confidence"]]
183
+ w["Message"] = with_context warning, w["Message"]
184
+ else
185
+ w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]]
186
+ end
187
+
188
+ table << w
189
+ end
190
+
191
+ if table.empty?
192
+ nil
193
+ else
194
+ table.sort_rows_by! "Controller"
195
+ table.sort_rows_by! "Warning Type"
196
+ table.sort_rows_by! "Confidence"
197
+ table.to_group "Controller Warnings"
198
+ end
199
+ else
200
+ nil
201
+ end
202
+ end
203
+
204
+ #Generate table of controllers and routes found for those controllers
205
+ def generate_controllers
206
+ table = Ruport::Data::Table(["Name", "Parent", "Includes", "Routes"])
207
+ tracker.controllers.keys.map{|k| k.to_s}.sort.each do |name|
208
+ name = name.to_sym
209
+ c = tracker.controllers[name]
210
+
211
+ if tracker.routes[:allow_all_actions] or tracker.routes[name] == :allow_all_actions
212
+ routes = c[:public].keys.map{|e| e.to_s}.sort.join(", ")
213
+ elsif tracker.routes[name].nil?
214
+ #No routes defined for this controller.
215
+ #This can happen when it is only a parent class
216
+ #for other controllers, for example.
217
+ routes = "[None]"
218
+
219
+ else
220
+ routes = (Set.new(c[:public].keys) & tracker.routes[name.to_sym]).
221
+ to_a.
222
+ map {|e| e.to_s}.
223
+ sort.
224
+ join(", ")
225
+ end
226
+
227
+ if routes == ""
228
+ routes = "[None]"
229
+ end
230
+
231
+ table << { "Name" => name.to_s,
232
+ "Parent" => c[:parent].to_s,
233
+ "Includes" => c[:includes].join(", "),
234
+ "Routes" => routes
235
+ }
236
+ end
237
+ table.sort_rows_by "Name"
238
+ end
239
+
240
+ #Generate listings of templates and their output
241
+ def generate_templates
242
+ out_processor = OutputProcessor.new
243
+ table = Ruport::Data::Table(["Name", "Output"])
244
+ tracker.templates.each do |name, template|
245
+ unless template[:outputs].empty?
246
+ template[:outputs].each do |out|
247
+ out = out_processor.format out
248
+ out = CGI.escapeHTML(out) if OPTIONS[:output_format] == :to_html
249
+ table << { "Name" => name,
250
+ "Output" => out.gsub("\n", ";").gsub(/\s+/, " ") }
251
+ end
252
+ end
253
+ end
254
+ Ruport::Data::Grouping(table, :by => "Name")
255
+ end
256
+
257
+ #Generate HTML output
258
+ def to_html
259
+ load_ruport
260
+
261
+ out = html_header <<
262
+ "<h2 id='summary'>Summary</h2>" <<
263
+ generate_overview.to_html << "<br/>" <<
264
+ generate_warning_overview.to_html
265
+
266
+ if OPTIONS[:report_routes] or OPTIONS[:debug]
267
+ out << "<h2>Controllers</h2>" <<
268
+ generate_controllers.to_html
269
+ end
270
+
271
+ if OPTIONS[:debug]
272
+ out << "<h2>Templates</h2>" <<
273
+ generate_templates.to_html
274
+ end
275
+
276
+ res = generate_errors
277
+ out << "<h2>Errors</h2>" << res.to_html if res
278
+
279
+ res = generate_warnings
280
+ out << "<h2>Security Warnings</h2>" << res.to_html if res
281
+
282
+ res = generate_controller_warnings
283
+ out << res.to_html if res
284
+
285
+ res = generate_model_warnings
286
+ out << res.to_html if res
287
+
288
+ res = generate_template_warnings
289
+ out << res.to_html if res
290
+
291
+ out << "</body></html>"
292
+ end
293
+
294
+ #Output text version of the report
295
+ def to_s
296
+ load_ruport
297
+
298
+ out = text_header <<
299
+ "\n+SUMMARY+\n" <<
300
+ generate_overview.to_s << "\n" <<
301
+ generate_warning_overview.to_s << "\n"
302
+
303
+ if OPTIONS[:report_routes] or OPTIONS[:debug]
304
+ out << "+CONTROLLERS+\n" <<
305
+ generate_controllers.to_s << "\n"
306
+ end
307
+
308
+ if OPTIONS[:debug]
309
+ out << "+TEMPLATES+\n\n" <<
310
+ generate_templates.to_s << "\n"
311
+ end
312
+
313
+ res = generate_errors
314
+ out << "+ERRORS+\n" << res.to_s << "\n" if res
315
+
316
+ res = generate_warnings
317
+ out << "+SECURITY WARNINGS+\n" << res.to_s << "\n" if res
318
+
319
+ res = generate_controller_warnings
320
+ out << res.to_s << "\n" if res
321
+
322
+ res = generate_model_warnings
323
+ out << res.to_s << "\n" if res
324
+
325
+ res = generate_template_warnings
326
+ out << res.to_s << "\n" if res
327
+
328
+ out
329
+ end
330
+
331
+ #Generate CSV output
332
+ def to_csv
333
+ load_ruport
334
+
335
+ out = csv_header <<
336
+ "\nSUMMARY\n" <<
337
+ generate_overview.to_csv << "\n" <<
338
+ generate_warning_overview.to_csv << "\n"
339
+
340
+ if OPTIONS[:report_routes] or OPTIONS[:debug]
341
+ out << "CONTROLLERS\n" <<
342
+ generate_controllers.to_csv << "\n"
343
+ end
344
+
345
+ if OPTIONS[:debug]
346
+ out << "TEMPLATES\n\n" <<
347
+ generate_templates.to_csv << "\n"
348
+ end
349
+
350
+ res = generate_errors
351
+ out << "ERRORS\n" << res.to_csv << "\n" if res
352
+
353
+ res = generate_warnings
354
+ out << "SECURITY WARNINGS\n" << res.to_csv << "\n" if res
355
+
356
+ res = generate_controller_warnings
357
+ out << res.to_csv << "\n" if res
358
+
359
+ res = generate_model_warnings
360
+ out << res.to_csv << "\n" if res
361
+
362
+ res = generate_template_warnings
363
+ out << res.to_csv << "\n" if res
364
+
365
+ out
366
+ end
367
+
368
+ #Not yet implemented
369
+ def to_pdf
370
+ raise "PDF output is not yet supported."
371
+ end
372
+
373
+ #Return header for HTML output. Uses CSS from OPTIONS[:html_style]
374
+ def html_header
375
+ if File.exist? OPTIONS[:html_style]
376
+ css = File.read OPTIONS[:html_style]
377
+ else
378
+ raise "Cannot find CSS stylesheet for HTML: #{OPTIONS[:html_style]}"
379
+ end
380
+
381
+ <<-HTML
382
+ <!DOCTYPE HTML SYSTEM>
383
+ <html>
384
+ <head>
385
+ <title>Brakeman Report</title>
386
+ <script type="text/javascript">
387
+ function toggle(context){
388
+ if (document.getElementById(context).style.display != "block")
389
+ document.getElementById(context).style.display = "block";
390
+ else
391
+ document.getElementById(context).style.display = "none";
392
+ }
393
+ </script>
394
+ <style type="text/css">
395
+ #{css}
396
+ </style>
397
+ </head>
398
+ <body>
399
+ <h1>Brakeman Report</h1>
400
+ <table>
401
+ <tr>
402
+ <th>Application Path</th>
403
+ <th>Report Generation Time</th>
404
+ <th>Checks Performed</th>
405
+ </tr>
406
+ <tr>
407
+ <td>#{File.expand_path OPTIONS[:app_path]}</td>
408
+ <td>#{Time.now}</td>
409
+ <td>#{checks.checks_run.sort.join(", ")}</td>
410
+ </tr>
411
+ </table>
412
+ HTML
413
+ end
414
+
415
+ #Generate header for text output
416
+ def text_header
417
+ "\n+BRAKEMAN REPORT+\n\nApplication path: #{File.expand_path OPTIONS[:app_path]}\nGenerated at #{Time.now}\nChecks run: #{checks.checks_run.sort.join(", ")}\n"
418
+ end
419
+
420
+ #Generate header for CSV output
421
+ def csv_header
422
+ header = Ruport::Data::Table(["Application Path", "Report Generation Time", "Checks Performed"])
423
+ header << [File.expand_path(OPTIONS[:app_path]), Time.now.to_s, checks.checks_run.sort.join(", ")]
424
+ "BRAKEMAN REPORT\n\n" << header.to_csv
425
+ end
426
+
427
+ #Return summary of warnings in hash and store in @warnings_summary
428
+ def warnings_summary
429
+ return @warnings_summary if @warnings_summary
430
+
431
+ summary = Hash.new(0)
432
+ high_confidence_warnings = 0
433
+
434
+ [checks.warnings,
435
+ checks.controller_warnings,
436
+ checks.model_warnings,
437
+ checks.template_warnings].each do |warnings|
438
+
439
+ warnings.each do |warning|
440
+ summary[warning.warning_type.to_s] += 1
441
+
442
+ if warning.confidence == 0
443
+ high_confidence_warnings += 1
444
+ end
445
+ end
446
+ end
447
+
448
+ summary[:high_confidence] = high_confidence_warnings
449
+ @warnings_summary = summary
450
+ end
451
+
452
+ #Return file name related to given warning. Uses +warning.file+ if it exists
453
+ def file_for warning
454
+ if warning.file
455
+ File.expand_path warning.file, OPTIONS[:app_path]
456
+ else
457
+ case warning.warning_set
458
+ when :controller
459
+ file_by_name warning.controller, :controller
460
+ when :template
461
+ file_by_name warning.template[:name], :template
462
+ when :model
463
+ file_by_name warning.model, :model
464
+ when :warning
465
+ file_by_name warning.class
466
+ else
467
+ nil
468
+ end
469
+ end
470
+ end
471
+
472
+ #Attempt to determine path to context file based on the reported name
473
+ #in the warning.
474
+ #
475
+ #For example,
476
+ #
477
+ # file_by_name FileController #=> "/rails/root/app/controllers/file_controller.rb
478
+ def file_by_name name, type = nil
479
+ return nil unless name
480
+ string_name = name.to_s
481
+ name = name.to_sym
482
+
483
+ unless type
484
+ if string_name =~ /Controller$/
485
+ type = :controller
486
+ elsif camelize(string_name) == string_name
487
+ type = :model
488
+ else
489
+ type = :template
490
+ end
491
+ end
492
+
493
+ path = OPTIONS[:app_path]
494
+
495
+ case type
496
+ when :controller
497
+ if tracker.controllers[name] and tracker.controllers[name][:file]
498
+ path = tracker.controllers[name][:file]
499
+ else
500
+ path += "/app/controllers/#{underscore(string_name)}.rb"
501
+ end
502
+ when :model
503
+ if tracker.models[name] and tracker.models[name][:file]
504
+ path = tracker.models[name][:file]
505
+ else
506
+ path += "/app/controllers/#{underscore(string_name)}.rb"
507
+ end
508
+ when :template
509
+ if tracker.templates[name] and tracker.templates[name][:file]
510
+ path = tracker.templates[name][:file]
511
+ elsif string_name.include? " "
512
+ name = string_name.split[0].to_sym
513
+ path = file_for name, :template
514
+ else
515
+ path = nil
516
+ end
517
+ end
518
+
519
+ path
520
+ end
521
+
522
+ #Return array of lines surrounding the warning location from the original
523
+ #file.
524
+ def context_for warning
525
+ file = file_for warning
526
+ context = []
527
+ return context unless warning.line and file and File.exist? file
528
+
529
+ current_line = 0
530
+ start_line = warning.line - 5
531
+ end_line = warning.line + 5
532
+
533
+ start_line = 1 if start_line < 0
534
+
535
+ File.open file do |f|
536
+ f.each_line do |line|
537
+ current_line += 1
538
+
539
+ next if line.strip == ""
540
+
541
+ if current_line > end_line
542
+ break
543
+ end
544
+
545
+ if current_line >= start_line
546
+ context << [current_line, line]
547
+ end
548
+ end
549
+ end
550
+
551
+ context
552
+ end
553
+
554
+ #Generate HTML for warnings, including context show/hidden via Javascript
555
+ def with_context warning, message
556
+ context = context_for warning
557
+ full_message = nil
558
+
559
+ if OPTIONS[:message_limit] and
560
+ OPTIONS[:message_limit] > 0 and
561
+ message.length > OPTIONS[:message_limit]
562
+
563
+ full_message = message
564
+ message = message[0..OPTIONS[:message_limit]] << "..."
565
+ end
566
+
567
+ if context.empty?
568
+ return CGI.escapeHTML(message)
569
+ end
570
+
571
+ @element_id += 1
572
+ code_id = "context#@element_id"
573
+ message_id = "message#@element_id"
574
+ full_message_id = "full_message#@element_id"
575
+ alt = false
576
+ output = "<div class='warning_message' onClick=\"toggle('#{code_id}');toggle('#{message_id}');toggle('#{full_message_id}')\" >" <<
577
+ if full_message
578
+ "<span id='#{message_id}' style='display:block' >#{CGI.escapeHTML(message)}</span>" <<
579
+ "<span id='#{full_message_id}' style='display:none'>#{CGI.escapeHTML(full_message)}</span>"
580
+ else
581
+ CGI.escapeHTML(message)
582
+ end <<
583
+ "<table id='#{code_id}' class='context' style='display:none'>"
584
+
585
+ unless context.empty?
586
+ if warning.line - 1 == 1 or warning.line + 1 == 1
587
+ error = " near_error"
588
+ elsif 1 == warning.line
589
+ error = " error"
590
+ else
591
+ error = ""
592
+ end
593
+
594
+ output << <<-HTML
595
+ <tr class='context first#{error}'>
596
+ <td class='context_line'>
597
+ <pre class='context'>#{context.first[0]}</pre>
598
+ </td>
599
+ <td class='context'>
600
+ <pre class='context'>#{CGI.escapeHTML context.first[1].chomp}</pre>
601
+ </td>
602
+ </tr>
603
+ HTML
604
+
605
+ if context.length > 1
606
+ output << context[1..-1].map do |code|
607
+ alt = !alt
608
+ if code[0] == warning.line - 1 or code[0] == warning.line + 1
609
+ error = " near_error"
610
+ elsif code[0] == warning.line
611
+ error = " error"
612
+ else
613
+ error = ""
614
+ end
615
+
616
+ <<-HTML
617
+ <tr class='context#{alt ? ' alt' : ''}#{error}'>
618
+ <td class='context_line'>
619
+ <pre class='context'>#{code[0]}</pre>
620
+ </td>
621
+ <td class='context'>
622
+ <pre class='context'>#{CGI.escapeHTML code[1].chomp}</pre>
623
+ </td>
624
+ </tr>
625
+ HTML
626
+ end.join
627
+ end
628
+ end
629
+
630
+ output << "</table></div>"
631
+ end
632
+
633
+ def to_tabs
634
+ [[:warnings, "General"], [:controller_warnings, "Controller"],
635
+ [:model_warnings, "Model"], [:template_warnings, "Template"]].map do |meth, category|
636
+
637
+ checks.send(meth).map do |w|
638
+ "#{file_for w}\t#{w.line}\t#{w.warning_type}\t#{category}\t#{w.message}\t#{TEXT_CONFIDENCE[w.confidence]}"
639
+ end.join "\n"
640
+
641
+ end.join "\n"
642
+ end
643
+
644
+ def load_ruport
645
+ require 'ruport'
646
+ rescue LoadError => e
647
+ $stderr.puts e
648
+ $stderr.puts "Please install Ruport."
649
+ exit!
650
+ end
651
+ end