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.
- checksums.yaml +4 -4
- data/CHANGES +15 -0
- data/README.md +6 -0
- data/lib/brakeman.rb +7 -2
- data/lib/brakeman/checks/check_link_to.rb +59 -67
- data/lib/brakeman/checks/check_mass_assignment.rb +5 -1
- data/lib/brakeman/checks/check_session_settings.rb +18 -7
- data/lib/brakeman/checks/check_symbol_dos.rb +50 -3
- data/lib/brakeman/processors/alias_processor.rb +116 -42
- data/lib/brakeman/processors/controller_processor.rb +5 -0
- data/lib/brakeman/processors/erb_template_processor.rb +2 -1
- data/lib/brakeman/processors/erubis_template_processor.rb +2 -1
- data/lib/brakeman/processors/haml_template_processor.rb +2 -1
- data/lib/brakeman/processors/lib/find_all_calls.rb +17 -0
- data/lib/brakeman/processors/lib/find_return_value.rb +3 -3
- data/lib/brakeman/processors/lib/render_helper.rb +1 -1
- data/lib/brakeman/processors/slim_template_processor.rb +1 -1
- data/lib/brakeman/processors/template_processor.rb +1 -1
- data/lib/brakeman/report.rb +60 -151
- data/lib/brakeman/report/initializers/faster_csv.rb +7 -0
- data/lib/brakeman/report/initializers/multi_json.rb +29 -0
- data/lib/brakeman/report/renderer.rb +22 -0
- data/lib/brakeman/{templates → report/templates}/controller_overview.html.erb +0 -0
- data/lib/brakeman/{templates → report/templates}/controller_warnings.html.erb +0 -0
- data/lib/brakeman/{templates → report/templates}/error_overview.html.erb +0 -0
- data/lib/brakeman/{templates → report/templates}/header.html.erb +2 -2
- data/lib/brakeman/{templates → report/templates}/model_warnings.html.erb +0 -0
- data/lib/brakeman/{templates → report/templates}/overview.html.erb +2 -2
- data/lib/brakeman/{templates → report/templates}/security_warnings.html.erb +0 -0
- data/lib/brakeman/{templates → report/templates}/template_overview.html.erb +0 -0
- data/lib/brakeman/{templates → report/templates}/view_warnings.html.erb +0 -0
- data/lib/brakeman/{templates → report/templates}/warning_overview.html.erb +0 -0
- data/lib/brakeman/scanner.rb +3 -0
- data/lib/brakeman/util.rb +6 -0
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning_codes.rb +1 -0
- data/lib/ruby_parser/bm_sexp.rb +14 -39
- 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
|
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
|
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
|
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
|
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
|
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
|
105
|
+
exp.original_line = exp.line unless exp.original_line
|
106
106
|
exp
|
107
107
|
end
|
108
108
|
end
|
@@ -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
|
42
|
+
call.original_line = exp.original_line
|
43
43
|
call.line(exp.line)
|
44
44
|
call
|
45
45
|
end
|
data/lib/brakeman/report.rb
CHANGED
@@ -7,42 +7,8 @@ require 'highline/system_extensions'
|
|
7
7
|
require "csv"
|
8
8
|
require 'multi_json'
|
9
9
|
require 'brakeman/version'
|
10
|
-
|
11
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
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
|
-
|
143
|
-
|
144
|
-
|
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
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
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
|
-
|
218
|
-
|
219
|
-
|
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
|
-
|
256
|
-
|
257
|
-
|
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
|
-
|
305
|
-
|
306
|
-
|
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
|
-
|
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
|
-
|
473
|
-
|
474
|
-
|
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
|
-
|
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
|