brakeman 1.8.3 → 1.9.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +3 -27
- data/lib/brakeman.rb +36 -38
- data/lib/brakeman/app_tree.rb +90 -0
- data/lib/brakeman/call_index.rb +5 -38
- data/lib/brakeman/checks.rb +11 -11
- data/lib/brakeman/checks/base_check.rb +53 -29
- data/lib/brakeman/checks/check_cross_site_scripting.rb +11 -9
- data/lib/brakeman/checks/check_evaluation.rb +1 -1
- data/lib/brakeman/checks/check_execute.rb +3 -3
- data/lib/brakeman/checks/check_link_to.rb +15 -13
- data/lib/brakeman/checks/check_link_to_href.rb +1 -1
- data/lib/brakeman/checks/check_mail_to.rb +1 -1
- data/lib/brakeman/checks/check_mass_assignment.rb +27 -13
- data/lib/brakeman/checks/check_redirect.rb +4 -4
- data/lib/brakeman/checks/check_select_tag.rb +1 -1
- data/lib/brakeman/checks/check_select_vulnerability.rb +1 -1
- data/lib/brakeman/checks/check_send.rb +2 -2
- data/lib/brakeman/checks/check_session_settings.rb +12 -5
- data/lib/brakeman/checks/check_single_quotes.rb +3 -3
- data/lib/brakeman/checks/check_skip_before_filter.rb +4 -3
- data/lib/brakeman/checks/check_sql.rb +30 -30
- data/lib/brakeman/checks/check_translate_bug.rb +11 -10
- data/lib/brakeman/checks/check_validation_regex.rb +36 -11
- data/lib/brakeman/checks/check_without_protection.rb +1 -1
- data/lib/brakeman/options.rb +6 -2
- data/lib/brakeman/processor.rb +6 -5
- data/lib/brakeman/processors/alias_processor.rb +153 -38
- data/lib/brakeman/processors/base_processor.rb +16 -21
- data/lib/brakeman/processors/controller_alias_processor.rb +24 -11
- data/lib/brakeman/processors/controller_processor.rb +25 -25
- data/lib/brakeman/processors/erb_template_processor.rb +6 -7
- data/lib/brakeman/processors/erubis_template_processor.rb +2 -3
- data/lib/brakeman/processors/gem_processor.rb +5 -4
- data/lib/brakeman/processors/haml_template_processor.rb +4 -6
- data/lib/brakeman/processors/lib/find_all_calls.rb +3 -3
- data/lib/brakeman/processors/lib/find_call.rb +2 -2
- data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
- data/lib/brakeman/processors/lib/processor_helper.rb +24 -2
- data/lib/brakeman/processors/lib/rails2_config_processor.rb +13 -14
- data/lib/brakeman/processors/lib/rails2_route_processor.rb +9 -4
- data/lib/brakeman/processors/lib/rails3_config_processor.rb +8 -8
- data/lib/brakeman/processors/lib/rails3_route_processor.rb +23 -21
- data/lib/brakeman/processors/lib/render_helper.rb +2 -2
- data/lib/brakeman/processors/library_processor.rb +2 -2
- data/lib/brakeman/processors/model_processor.rb +16 -12
- data/lib/brakeman/processors/output_processor.rb +2 -1
- data/lib/brakeman/processors/template_alias_processor.rb +12 -8
- data/lib/brakeman/report.rb +28 -14
- data/lib/brakeman/rescanner.rb +5 -5
- data/lib/brakeman/scanner.rb +56 -94
- data/lib/brakeman/templates/header.html.erb +7 -2
- data/lib/brakeman/tracker.rb +14 -4
- data/lib/brakeman/util.rb +38 -17
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning.rb +14 -6
- data/lib/ruby_parser/bm_sexp.rb +157 -57
- data/lib/ruby_parser/bm_sexp_processor.rb +1 -2
- metadata +26 -25
- data/lib/ruby_parser/ruby18_parser.rb +0 -5544
- data/lib/ruby_parser/ruby19_parser.rb +0 -5756
- data/lib/ruby_parser/ruby_lexer.rb +0 -1349
- data/lib/ruby_parser/ruby_parser.rb +0 -5
- data/lib/ruby_parser/ruby_parser_extras.rb +0 -1057
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'brakeman/checks/base_check'
|
2
2
|
|
3
3
|
#Check for vulnerability in translate() helper that allows cross-site scripting
|
4
|
-
#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2b61d70fb73c7cc5
|
5
4
|
class Brakeman::CheckTranslateBug < Brakeman::BaseCheck
|
6
5
|
Brakeman::Checks.add self
|
7
6
|
|
@@ -12,32 +11,34 @@ class Brakeman::CheckTranslateBug < Brakeman::BaseCheck
|
|
12
11
|
version_between?('3.0.0', '3.0.10') or
|
13
12
|
version_between?('3.1.0', '3.1.1')
|
14
13
|
|
15
|
-
if uses_translate?
|
16
|
-
|
14
|
+
confidence = if uses_translate?
|
15
|
+
CONFIDENCE[:high]
|
17
16
|
else
|
18
|
-
|
17
|
+
CONFIDENCE[:med]
|
19
18
|
end
|
20
19
|
|
21
20
|
version = tracker.config[:rails_version]
|
21
|
+
description = "have a vulnerability in the translate helper with keys ending in _html"
|
22
22
|
|
23
|
-
if version =~ /^3\.1/
|
24
|
-
|
23
|
+
message = if version =~ /^3\.1/
|
24
|
+
"Versions before 3.1.2 #{description}."
|
25
25
|
elsif version =~ /^3\.0/
|
26
|
-
|
26
|
+
"Versions before 3.0.11 #{description}."
|
27
27
|
else
|
28
|
-
|
28
|
+
"Rails 2.3.x using the rails_xss plugin #{description}}."
|
29
29
|
end
|
30
30
|
|
31
31
|
warn :warning_type => "Cross Site Scripting",
|
32
32
|
:message => message,
|
33
33
|
:confidence => confidence,
|
34
|
-
:file => gemfile_or_environment
|
34
|
+
:file => gemfile_or_environment,
|
35
|
+
:link_path => "http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2b61d70fb73c7cc5"
|
35
36
|
end
|
36
37
|
end
|
37
38
|
|
38
39
|
def uses_translate?
|
39
40
|
Brakeman.debug "Finding calls to translate() or t()"
|
40
41
|
|
41
|
-
|
42
|
+
tracker.find_call(:target => nil, :methods => [:t, :translate]).any?
|
42
43
|
end
|
43
44
|
end
|
@@ -13,48 +13,73 @@ class Brakeman::CheckValidationRegex < Brakeman::BaseCheck
|
|
13
13
|
@description = "Report uses of validates_format_of with improper anchors"
|
14
14
|
|
15
15
|
WITH = Sexp.new(:lit, :with)
|
16
|
+
FORMAT = Sexp.new(:lit, :format)
|
16
17
|
|
17
18
|
def run_check
|
18
19
|
active_record_models.each do |name, model|
|
19
20
|
@current_model = name
|
20
21
|
format_validations = model[:options][:validates_format_of]
|
22
|
+
|
21
23
|
if format_validations
|
22
24
|
format_validations.each do |v|
|
23
|
-
|
25
|
+
process_validates_format_of v
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
validates = model[:options][:validates]
|
30
|
+
|
31
|
+
if validates
|
32
|
+
validates.each do |v|
|
33
|
+
process_validates v
|
24
34
|
end
|
25
35
|
end
|
26
36
|
end
|
27
37
|
end
|
28
38
|
|
29
39
|
#Check validates_format_of
|
30
|
-
def
|
40
|
+
def process_validates_format_of validator
|
31
41
|
if value = hash_access(validator.last, WITH)
|
32
42
|
check_regex value, validator
|
33
43
|
end
|
34
44
|
end
|
35
45
|
|
46
|
+
#Check validates ..., :format => ...
|
47
|
+
def process_validates validator
|
48
|
+
hash_arg = validator.last
|
49
|
+
return unless hash? hash_arg
|
50
|
+
|
51
|
+
value = hash_access(hash_arg, FORMAT)
|
52
|
+
|
53
|
+
if hash? value
|
54
|
+
value = hash_access(value, WITH)
|
55
|
+
end
|
56
|
+
|
57
|
+
if value
|
58
|
+
check_regex value, validator
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
36
62
|
#Issue warning if the regular expression does not use
|
37
63
|
#+\A+ and +\z+
|
38
64
|
def check_regex value, validator
|
39
65
|
return unless regexp? value
|
40
66
|
|
41
67
|
regex = value.value.inspect
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
:confidence => CONFIDENCE[:high]
|
49
|
-
end
|
68
|
+
unless regex =~ /\A\/\\A.*\\(z|Z)\/(m|i|x|n|e|u|s|o)*\z/
|
69
|
+
warn :model => @current_model,
|
70
|
+
:warning_type => "Format Validation",
|
71
|
+
:message => "Insufficient validation for '#{get_name validator}' using #{regex}. Use \\A and \\z as anchors",
|
72
|
+
:line => value.line,
|
73
|
+
:confidence => CONFIDENCE[:high]
|
50
74
|
end
|
51
75
|
end
|
52
76
|
|
53
77
|
#Get the name of the attribute being validated.
|
54
78
|
def get_name validator
|
55
79
|
name = validator[1]
|
80
|
+
|
56
81
|
if sexp? name
|
57
|
-
name
|
82
|
+
name.value
|
58
83
|
else
|
59
84
|
name
|
60
85
|
end
|
@@ -33,7 +33,7 @@ class Brakeman::CheckWithoutProtection < Brakeman::BaseCheck
|
|
33
33
|
#All results should be Model.new(...) or Model.attributes=() calls
|
34
34
|
def process_result res
|
35
35
|
call = res[:call]
|
36
|
-
last_arg = call.
|
36
|
+
last_arg = call.last_arg
|
37
37
|
|
38
38
|
if hash? last_arg and not call.original_line and not duplicate? res
|
39
39
|
|
data/lib/brakeman/options.rb
CHANGED
@@ -50,8 +50,8 @@ module Brakeman::Options
|
|
50
50
|
opts.separator ""
|
51
51
|
opts.separator "Scanning options:"
|
52
52
|
|
53
|
-
opts.on "-a", "--assume-routes", "Assume all controller methods are actions" do
|
54
|
-
options[:assume_all_routes] =
|
53
|
+
opts.on "-a", "--[no-]assume-routes", "Assume all controller methods are actions (default)" do |assume|
|
54
|
+
options[:assume_all_routes] = assume
|
55
55
|
end
|
56
56
|
|
57
57
|
opts.on "-e", "--escape-html", "Escape HTML by default" do
|
@@ -71,6 +71,10 @@ module Brakeman::Options
|
|
71
71
|
options[:ignore_attr_protected] = true
|
72
72
|
end
|
73
73
|
|
74
|
+
opts.on "--interprocedural", "Process method calls to known methods" do
|
75
|
+
options[:interprocedural] = true
|
76
|
+
end
|
77
|
+
|
74
78
|
opts.on "--no-branching", "Disable flow sensitivity on conditionals" do
|
75
79
|
options[:ignore_ifs] = true
|
76
80
|
end
|
data/lib/brakeman/processor.rb
CHANGED
@@ -12,8 +12,9 @@ module Brakeman
|
|
12
12
|
class Processor
|
13
13
|
include Util
|
14
14
|
|
15
|
-
def initialize options
|
16
|
-
@
|
15
|
+
def initialize(app_tree, options)
|
16
|
+
@app_tree = app_tree
|
17
|
+
@tracker = Tracker.new(@app_tree, self, options)
|
17
18
|
end
|
18
19
|
|
19
20
|
def tracked_events
|
@@ -38,7 +39,7 @@ module Brakeman
|
|
38
39
|
#Process controller source. +file_name+ is used for reporting
|
39
40
|
def process_controller src, file_name
|
40
41
|
if contains_class? src
|
41
|
-
ControllerProcessor.new(@tracker).process_controller src, file_name
|
42
|
+
ControllerProcessor.new(@app_tree, @tracker).process_controller src, file_name
|
42
43
|
else
|
43
44
|
LibraryProcessor.new(@tracker).process_library src, file_name
|
44
45
|
end
|
@@ -47,13 +48,13 @@ module Brakeman
|
|
47
48
|
#Process variable aliasing in controller source and save it in the
|
48
49
|
#tracker.
|
49
50
|
def process_controller_alias name, src, only_method = nil
|
50
|
-
ControllerAliasProcessor.new(@tracker, only_method).process_controller name, src
|
51
|
+
ControllerAliasProcessor.new(@app_tree, @tracker, only_method).process_controller name, src
|
51
52
|
end
|
52
53
|
|
53
54
|
#Process a model source
|
54
55
|
def process_model src, file_name
|
55
56
|
result = ModelProcessor.new(@tracker).process_model src, file_name
|
56
|
-
AliasProcessor.new(@tracker).
|
57
|
+
AliasProcessor.new(@tracker).process_all result if result
|
57
58
|
end
|
58
59
|
|
59
60
|
#Process either an ERB or HAML template
|
@@ -24,6 +24,8 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
24
24
|
@exp_context = []
|
25
25
|
@current_module = nil
|
26
26
|
@tracker = tracker #set in subclass as necessary
|
27
|
+
@helper_method_cache = {}
|
28
|
+
@helper_method_info = Hash.new({})
|
27
29
|
set_env_defaults
|
28
30
|
end
|
29
31
|
|
@@ -70,11 +72,9 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
70
72
|
@exp_context.push exp
|
71
73
|
|
72
74
|
begin
|
73
|
-
exp.
|
74
|
-
next if i == 0
|
75
|
-
|
75
|
+
exp.map! do |e|
|
76
76
|
if sexp? e and not e.empty?
|
77
|
-
|
77
|
+
process e
|
78
78
|
else
|
79
79
|
e
|
80
80
|
end
|
@@ -107,7 +107,6 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
107
107
|
|
108
108
|
target = exp.target
|
109
109
|
method = exp.method
|
110
|
-
args = exp[3]
|
111
110
|
first_arg = exp.first_arg
|
112
111
|
|
113
112
|
#See if it is possible to simplify some basic cases
|
@@ -154,7 +153,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
154
153
|
temp_exp = process_array_access target, exp.args
|
155
154
|
exp = temp_exp if temp_exp
|
156
155
|
elsif hash? target
|
157
|
-
temp_exp = process_hash_access target,
|
156
|
+
temp_exp = process_hash_access target, first_arg
|
158
157
|
exp = temp_exp if temp_exp
|
159
158
|
end
|
160
159
|
when :merge!, :update
|
@@ -204,7 +203,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
204
203
|
def process_methdef exp
|
205
204
|
env.scope do
|
206
205
|
set_env_defaults
|
207
|
-
|
206
|
+
exp.body = process_all! exp.body
|
208
207
|
end
|
209
208
|
exp
|
210
209
|
end
|
@@ -213,7 +212,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
213
212
|
def process_selfdef exp
|
214
213
|
env.scope do
|
215
214
|
set_env_defaults
|
216
|
-
|
215
|
+
exp.body = process_all! exp.body
|
217
216
|
end
|
218
217
|
exp
|
219
218
|
end
|
@@ -231,8 +230,10 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
231
230
|
|
232
231
|
if @inside_if and val = env[local]
|
233
232
|
#avoid setting to value it already is (e.g. "1 or 1")
|
234
|
-
if val != exp.rhs
|
235
|
-
|
233
|
+
if val != exp.rhs
|
234
|
+
unless node_type?(val, :or) and (val.rhs == exp.rhs or val.lhs == exp.rhs)
|
235
|
+
env[local] = Sexp.new(:or, val, exp.rhs).line(exp.line || -2)
|
236
|
+
end
|
236
237
|
end
|
237
238
|
else
|
238
239
|
env[local] = exp.rhs
|
@@ -300,21 +301,22 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
300
301
|
tar_variable = exp.target
|
301
302
|
target = exp.target = process(exp.target)
|
302
303
|
method = exp.method
|
303
|
-
|
304
|
+
index_arg = exp.first_arg
|
305
|
+
value_arg = exp.second_arg
|
304
306
|
|
305
307
|
if method == :[]=
|
306
|
-
index = exp.first_arg = process(
|
307
|
-
value = exp.second_arg = process(
|
308
|
-
match = Sexp.new(:call, target, :[],
|
308
|
+
index = exp.first_arg = process(index_arg)
|
309
|
+
value = exp.second_arg = process(value_arg)
|
310
|
+
match = Sexp.new(:call, target, :[], index)
|
309
311
|
env[match] = value
|
310
312
|
|
311
313
|
if hash? target
|
312
314
|
env[tar_variable] = hash_insert target.deep_clone, index, value
|
313
315
|
end
|
314
316
|
elsif method.to_s[-1,1] == "="
|
315
|
-
value = exp.first_arg = process(
|
317
|
+
value = exp.first_arg = process(index_arg)
|
316
318
|
#This is what we'll replace with the value
|
317
|
-
match = Sexp.new(:call, target, method.to_s[0..-2].to_sym
|
319
|
+
match = Sexp.new(:call, target, method.to_s[0..-2].to_sym)
|
318
320
|
|
319
321
|
if @inside_if and val = env[match]
|
320
322
|
if val != value
|
@@ -336,7 +338,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
336
338
|
hash = hash.deep_clone
|
337
339
|
hash_iterate args do |key, replacement|
|
338
340
|
hash_insert hash, key, replacement
|
339
|
-
match = Sexp.new(:call, hash, :[],
|
341
|
+
match = Sexp.new(:call, hash, :[], key)
|
340
342
|
env[match] = replacement
|
341
343
|
end
|
342
344
|
hash
|
@@ -361,7 +363,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
361
363
|
target = exp[1] = process(exp[1])
|
362
364
|
index = exp[2][1] = process(exp[2][1])
|
363
365
|
value = exp[4] = process(exp[4])
|
364
|
-
match = Sexp.new(:call, target, :[],
|
366
|
+
match = Sexp.new(:call, target, :[], index)
|
365
367
|
|
366
368
|
unless env[match]
|
367
369
|
if request_value? target
|
@@ -383,7 +385,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
383
385
|
value = exp[4] = process(exp[4])
|
384
386
|
method = exp[2]
|
385
387
|
|
386
|
-
match = Sexp.new(:call, target, method.to_s[0..-2].to_sym
|
388
|
+
match = Sexp.new(:call, target, method.to_s[0..-2].to_sym)
|
387
389
|
|
388
390
|
unless env[match]
|
389
391
|
env[match] = value
|
@@ -392,8 +394,10 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
392
394
|
exp
|
393
395
|
end
|
394
396
|
|
397
|
+
#This is the right hand side value of a multiple assignment,
|
398
|
+
#like `x = y, z`
|
395
399
|
def process_svalue exp
|
396
|
-
exp
|
400
|
+
exp.value
|
397
401
|
end
|
398
402
|
|
399
403
|
#Constant assignments like
|
@@ -423,9 +427,9 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
423
427
|
if true? condition
|
424
428
|
exps = [exp.then_clause]
|
425
429
|
elsif false? condition
|
426
|
-
exps = exp
|
430
|
+
exps = [exp.else_clause]
|
427
431
|
else
|
428
|
-
exps = exp
|
432
|
+
exps = [exp.then_clause, exp.else_clause]
|
429
433
|
end
|
430
434
|
|
431
435
|
was_inside = @inside_if
|
@@ -461,15 +465,9 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
461
465
|
end
|
462
466
|
|
463
467
|
#Process hash access by returning the value associated
|
464
|
-
#with the given
|
465
|
-
def process_hash_access target,
|
466
|
-
|
467
|
-
index = args[0]
|
468
|
-
|
469
|
-
hash_access(target, index)
|
470
|
-
else
|
471
|
-
nil
|
472
|
-
end
|
468
|
+
#with the given argument.
|
469
|
+
def process_hash_access target, index
|
470
|
+
hash_access(target, index)
|
473
471
|
end
|
474
472
|
|
475
473
|
#Join two array literals into one.
|
@@ -482,8 +480,9 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
482
480
|
#Join two string literals into one.
|
483
481
|
def join_strings string1, string2
|
484
482
|
result = Sexp.new(:str)
|
485
|
-
result
|
486
|
-
|
483
|
+
result.value = string1.value + string2.value
|
484
|
+
|
485
|
+
if result.value.length > 50
|
487
486
|
string1
|
488
487
|
else
|
489
488
|
result
|
@@ -492,18 +491,19 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
492
491
|
|
493
492
|
#Returns a new SexpProcessor::Environment containing only instance variables.
|
494
493
|
#This is useful, for example, when processing views.
|
495
|
-
def only_ivars include_request_vars = false
|
494
|
+
def only_ivars include_request_vars = false, lenv = nil
|
495
|
+
lenv ||= env
|
496
496
|
res = SexpProcessor::Environment.new
|
497
497
|
|
498
498
|
if include_request_vars
|
499
|
-
|
499
|
+
lenv.all.each do |k, v|
|
500
500
|
#TODO Why would this have nil values?
|
501
501
|
if (k.node_type == :ivar or request_value? k) and not v.nil?
|
502
502
|
res[k] = v.dup
|
503
503
|
end
|
504
504
|
end
|
505
505
|
else
|
506
|
-
|
506
|
+
lenv.all.each do |k, v|
|
507
507
|
#TODO Why would this have nil values?
|
508
508
|
if k.node_type == :ivar and not v.nil?
|
509
509
|
res[k] = v.dup
|
@@ -514,6 +514,117 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
514
514
|
res
|
515
515
|
end
|
516
516
|
|
517
|
+
def only_request_vars
|
518
|
+
res = SexpProcessor::Environment.new
|
519
|
+
|
520
|
+
env.all.each do |k, v|
|
521
|
+
if request_value? k and not v.nil?
|
522
|
+
res[k] = v.dup
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
res
|
527
|
+
end
|
528
|
+
|
529
|
+
def get_call_value call
|
530
|
+
method_name = call.method
|
531
|
+
|
532
|
+
#Look for helper methods and see if we can get a return value
|
533
|
+
if found_method = find_method(method_name, @current_class)
|
534
|
+
helper = found_method[:method]
|
535
|
+
|
536
|
+
if sexp? helper
|
537
|
+
value = process_helper_method helper, call.args
|
538
|
+
value.line(call.line)
|
539
|
+
return value
|
540
|
+
else
|
541
|
+
raise "Unexpected value for method: #{found_method}"
|
542
|
+
end
|
543
|
+
else
|
544
|
+
call
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
def process_helper_method method_exp, args
|
549
|
+
method_name = method_exp.method_name
|
550
|
+
Brakeman.debug "Processing method #{method_name}"
|
551
|
+
|
552
|
+
info = @helper_method_info[method_name]
|
553
|
+
|
554
|
+
#If method uses instance variables, then include those and request
|
555
|
+
#variables (params, etc) in the method environment. Otherwise,
|
556
|
+
#only include request variables.
|
557
|
+
if info[:uses_ivars]
|
558
|
+
meth_env = only_ivars(:include_request_vars)
|
559
|
+
else
|
560
|
+
meth_env = only_request_vars
|
561
|
+
end
|
562
|
+
|
563
|
+
#Add arguments to method environment
|
564
|
+
assign_args method_exp, args, meth_env
|
565
|
+
|
566
|
+
|
567
|
+
#Find return values if method does not depend on environment/args
|
568
|
+
values = @helper_method_cache[method_name]
|
569
|
+
|
570
|
+
unless values
|
571
|
+
#Serialize environment for cache key
|
572
|
+
meth_values = meth_env.instance_variable_get(:@env).to_a
|
573
|
+
meth_values.sort!
|
574
|
+
meth_values = meth_values.to_s
|
575
|
+
|
576
|
+
digest = Digest::SHA1.new.update(meth_values << method_name.to_s).to_s.to_sym
|
577
|
+
|
578
|
+
values = @helper_method_cache[digest]
|
579
|
+
end
|
580
|
+
|
581
|
+
if values
|
582
|
+
#Use values from cache
|
583
|
+
values[:ivar_values].each do |var, val|
|
584
|
+
env[var] = val
|
585
|
+
end
|
586
|
+
|
587
|
+
values[:return_value]
|
588
|
+
else
|
589
|
+
#Find return value for method
|
590
|
+
frv = Brakeman::FindReturnValue.new
|
591
|
+
value = frv.get_return_value(method_exp.body_list, meth_env)
|
592
|
+
|
593
|
+
ivars = {}
|
594
|
+
|
595
|
+
only_ivars(false, meth_env).all.each do |var, val|
|
596
|
+
env[var] = val
|
597
|
+
ivars[var] = val
|
598
|
+
end
|
599
|
+
|
600
|
+
if not frv.uses_ivars? and args.length == 0
|
601
|
+
#Store return value without ivars and args if they are not used
|
602
|
+
@helper_method_cache[method_exp.method_name] = { :return_value => value, :ivar_values => ivars }
|
603
|
+
else
|
604
|
+
@helper_method_cache[digest] = { :return_value => value, :ivar_values => ivars }
|
605
|
+
end
|
606
|
+
|
607
|
+
#Store information about method, just ivar usage for now
|
608
|
+
@helper_method_info[method_name] = { :uses_ivars => frv.uses_ivars? }
|
609
|
+
|
610
|
+
value
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
def assign_args method_exp, args, meth_env = SexpProcessor::Environment.new
|
615
|
+
formal_args = method_exp.formal_args
|
616
|
+
|
617
|
+
formal_args.each_with_index do |arg, index|
|
618
|
+
next if index == 0
|
619
|
+
|
620
|
+
if arg.is_a? Symbol and sexp? args[index - 1]
|
621
|
+
meth_env[Sexp.new(:lvar, arg)] = args[index - 1]
|
622
|
+
end
|
623
|
+
end
|
624
|
+
|
625
|
+
meth_env
|
626
|
+
end
|
627
|
+
|
517
628
|
#Set line nunber for +exp+ and every Sexp it contains. Used when replacing
|
518
629
|
#expressions, so warnings indicate the correct line.
|
519
630
|
def set_line exp, line_number
|
@@ -530,8 +641,8 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
530
641
|
|
531
642
|
#Finds the inner most call target which is not the target of a call to <<
|
532
643
|
def find_push_target exp
|
533
|
-
if call? exp and exp
|
534
|
-
find_push_target exp
|
644
|
+
if call? exp and exp.method == :<<
|
645
|
+
find_push_target exp.target
|
535
646
|
else
|
536
647
|
exp
|
537
648
|
end
|
@@ -544,4 +655,8 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
544
655
|
|
545
656
|
false
|
546
657
|
end
|
658
|
+
|
659
|
+
def find_method *args
|
660
|
+
nil
|
661
|
+
end
|
547
662
|
end
|