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