brakeman 0.0.2
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.
- 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
|