brakeman 1.8.3 → 1.9.0.pre1
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/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
|