brakeman-lib 4.7.1 → 4.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +47 -0
- data/README.md +13 -5
- data/lib/brakeman.rb +20 -0
- data/lib/brakeman/checks/base_check.rb +13 -10
- data/lib/brakeman/checks/check_basic_auth.rb +2 -0
- data/lib/brakeman/checks/check_content_tag.rb +1 -2
- data/lib/brakeman/checks/check_csrf_token_forgery_cve.rb +28 -0
- data/lib/brakeman/checks/check_deserialize.rb +21 -1
- data/lib/brakeman/checks/check_execute.rb +40 -5
- data/lib/brakeman/checks/check_json_entity_escape.rb +38 -0
- data/lib/brakeman/checks/check_link_to.rb +1 -1
- data/lib/brakeman/checks/check_link_to_href.rb +1 -3
- data/lib/brakeman/checks/check_mass_assignment.rb +34 -4
- data/lib/brakeman/checks/check_model_attr_accessible.rb +1 -1
- data/lib/brakeman/checks/check_page_caching_cve.rb +37 -0
- data/lib/brakeman/checks/check_permit_attributes.rb +1 -1
- data/lib/brakeman/checks/check_skip_before_filter.rb +4 -4
- data/lib/brakeman/checks/check_sql.rb +24 -33
- data/lib/brakeman/checks/check_template_injection.rb +32 -0
- data/lib/brakeman/commandline.rb +25 -1
- data/lib/brakeman/differ.rb +0 -5
- data/lib/brakeman/options.rb +21 -1
- data/lib/brakeman/processor.rb +1 -1
- data/lib/brakeman/processors/alias_processor.rb +2 -3
- data/lib/brakeman/processors/lib/find_all_calls.rb +30 -14
- data/lib/brakeman/processors/lib/render_helper.rb +3 -1
- data/lib/brakeman/report.rb +4 -1
- data/lib/brakeman/report/ignore/config.rb +10 -2
- data/lib/brakeman/report/report_junit.rb +104 -0
- data/lib/brakeman/report/report_markdown.rb +0 -1
- data/lib/brakeman/report/report_text.rb +37 -16
- data/lib/brakeman/scanner.rb +4 -1
- data/lib/brakeman/tracker.rb +3 -1
- data/lib/brakeman/tracker/config.rb +4 -3
- data/lib/brakeman/tracker/constants.rb +8 -7
- data/lib/brakeman/util.rb +21 -3
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning_codes.rb +7 -0
- metadata +33 -8
@@ -8,7 +8,7 @@ require 'brakeman/checks/base_check'
|
|
8
8
|
class Brakeman::CheckModelAttrAccessible < Brakeman::BaseCheck
|
9
9
|
Brakeman::Checks.add self
|
10
10
|
|
11
|
-
@description = "Reports models which have dangerous attributes defined
|
11
|
+
@description = "Reports models which have dangerous attributes defined via attr_accessible"
|
12
12
|
|
13
13
|
SUSP_ATTRS = [
|
14
14
|
[:admin, :high], # Very dangerous unless some Rails authorization used
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'brakeman/checks/base_check'
|
2
|
+
|
3
|
+
class Brakeman::CheckPageCachingCVE < Brakeman::BaseCheck
|
4
|
+
Brakeman::Checks.add self
|
5
|
+
|
6
|
+
@description = "Check for page caching vulnerability (CVE-2020-8159)"
|
7
|
+
|
8
|
+
def run_check
|
9
|
+
gem_name = 'actionpack-page_caching'
|
10
|
+
gem_version = tracker.config.gem_version(gem_name.to_sym)
|
11
|
+
upgrade_version = '1.2.2'
|
12
|
+
cve = 'CVE-2020-8159'
|
13
|
+
|
14
|
+
return unless gem_version and version_between?('0.0.0', '1.2.1', gem_version)
|
15
|
+
|
16
|
+
message = msg("Directory traversal vulnerability in ", msg_version(gem_version, gem_name), " ", msg_cve(cve), ". Upgrade to ", msg_version(upgrade_version, gem_name))
|
17
|
+
|
18
|
+
if uses_caches_page?
|
19
|
+
confidence = :high
|
20
|
+
else
|
21
|
+
confidence = :weak
|
22
|
+
end
|
23
|
+
|
24
|
+
warn :warning_type => 'Directory Traversal',
|
25
|
+
:warning_code => :CVE_2020_8159,
|
26
|
+
:message => message,
|
27
|
+
:confidence => confidence,
|
28
|
+
:link_path => 'https://groups.google.com/d/msg/rubyonrails-security/CFRVkEytdP8/c5gmICECAgAJ',
|
29
|
+
:gem_info => gemfile_or_environment(gem_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def uses_caches_page?
|
33
|
+
tracker.controllers.any? do |name, controller|
|
34
|
+
controller.options.has_key? :caches_page
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -3,7 +3,7 @@ require 'brakeman/checks/base_check'
|
|
3
3
|
class Brakeman::CheckPermitAttributes < Brakeman::BaseCheck
|
4
4
|
Brakeman::Checks.add self
|
5
5
|
|
6
|
-
@description = "Warn on potentially dangerous attributes
|
6
|
+
@description = "Warn on potentially dangerous attributes allowed via permit"
|
7
7
|
|
8
8
|
SUSPICIOUS_KEYS = {
|
9
9
|
admin: :high,
|
@@ -4,8 +4,8 @@ require 'brakeman/checks/base_check'
|
|
4
4
|
#
|
5
5
|
# skip_before_filter :verify_authenticity_token, :except => [...]
|
6
6
|
#
|
7
|
-
#which is essentially a
|
8
|
-
#ones listed) versus a
|
7
|
+
#which is essentially a skip-by-default approach (no actions are checked EXCEPT the
|
8
|
+
#ones listed) versus a enforce-by-default approach (ONLY the actions listed will skip
|
9
9
|
#the check)
|
10
10
|
class Brakeman::CheckSkipBeforeFilter < Brakeman::BaseCheck
|
11
11
|
Brakeman::Checks.add self
|
@@ -26,7 +26,7 @@ class Brakeman::CheckSkipBeforeFilter < Brakeman::BaseCheck
|
|
26
26
|
warn :class => controller.name, #ugh this should be a controller warning, too
|
27
27
|
:warning_type => "Cross-Site Request Forgery",
|
28
28
|
:warning_code => :csrf_blacklist,
|
29
|
-
:message => msg("
|
29
|
+
:message => msg("List specific actions (", msg_code(":only => [..]"), ") when skipping CSRF check"),
|
30
30
|
:code => filter,
|
31
31
|
:confidence => :medium,
|
32
32
|
:file => controller.file
|
@@ -35,7 +35,7 @@ class Brakeman::CheckSkipBeforeFilter < Brakeman::BaseCheck
|
|
35
35
|
warn :controller => controller.name,
|
36
36
|
:warning_code => :auth_blacklist,
|
37
37
|
:warning_type => "Authentication",
|
38
|
-
:message => msg("
|
38
|
+
:message => msg("List specific actions (", msg_code(":only => [..]"), ") when skipping authentication"),
|
39
39
|
:code => filter,
|
40
40
|
:confidence => :medium,
|
41
41
|
:link_path => "authentication_whitelist",
|
@@ -71,32 +71,32 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
71
71
|
def find_scope_calls
|
72
72
|
scope_calls = []
|
73
73
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
74
|
+
# Used in pre-3.1.0 versions of Rails
|
75
|
+
ar_scope_calls(:named_scope) do |model, args|
|
76
|
+
call = make_call(nil, :named_scope, args).line(args.line)
|
77
|
+
scope_calls << scope_call_hash(call, model, :named_scope)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Use in 3.1.0 and later
|
81
|
+
ar_scope_calls(:scope) do |model, args|
|
82
|
+
second_arg = args[2]
|
83
|
+
next unless sexp? second_arg
|
84
|
+
|
85
|
+
if second_arg.node_type == :iter and node_type? second_arg.block, :block, :call, :safe_call
|
86
|
+
process_scope_with_block(model, args)
|
87
|
+
elsif call? second_arg
|
88
|
+
call = second_arg
|
89
|
+
scope_calls << scope_call_hash(call, model, call.method)
|
90
|
+
else
|
91
|
+
call = make_call(nil, :scope, args).line(args.line)
|
92
|
+
scope_calls << scope_call_hash(call, model, :scope)
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
96
|
scope_calls
|
97
97
|
end
|
98
98
|
|
99
|
-
def ar_scope_calls(symbol_name
|
99
|
+
def ar_scope_calls(symbol_name, &block)
|
100
100
|
active_record_models.each do |name, model|
|
101
101
|
model_args = model.options[symbol_name]
|
102
102
|
if model_args
|
@@ -393,6 +393,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
393
393
|
nil
|
394
394
|
end
|
395
395
|
|
396
|
+
TO_STRING_METHODS = [:to_s, :squish, :strip, :strip_heredoc]
|
397
|
+
|
396
398
|
#Returns value if interpolated value is not something safe
|
397
399
|
def unsafe_string_interp? exp
|
398
400
|
if node_type? exp, :evstr
|
@@ -403,7 +405,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
403
405
|
|
404
406
|
if not sexp? value
|
405
407
|
nil
|
406
|
-
elsif call? value and value.method
|
408
|
+
elsif call? value and TO_STRING_METHODS.include? value.method
|
407
409
|
unsafe_string_interp? value.target
|
408
410
|
elsif call? value and safe_literal_target? value
|
409
411
|
nil
|
@@ -466,7 +468,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
466
468
|
unless IGNORE_METHODS_IN_SQL.include? exp.method
|
467
469
|
if has_immediate_user_input? exp
|
468
470
|
exp
|
469
|
-
elsif exp.method
|
471
|
+
elsif TO_STRING_METHODS.include? exp.method
|
470
472
|
find_dangerous_value exp.target, ignore_hash
|
471
473
|
else
|
472
474
|
check_call exp
|
@@ -523,8 +525,6 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
523
525
|
false
|
524
526
|
end
|
525
527
|
|
526
|
-
STRING_METHODS = Set[:<<, :+, :concat, :prepend]
|
527
|
-
|
528
528
|
def check_for_string_building exp
|
529
529
|
return unless call? exp
|
530
530
|
|
@@ -571,15 +571,6 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
571
571
|
end
|
572
572
|
end
|
573
573
|
|
574
|
-
def string_building? exp
|
575
|
-
return false unless call? exp and STRING_METHODS.include? exp.method
|
576
|
-
|
577
|
-
node_type? exp.target, :str, :dstr or
|
578
|
-
node_type? exp.first_arg, :str, :dstr or
|
579
|
-
string_building? exp.target or
|
580
|
-
string_building? exp.first_arg
|
581
|
-
end
|
582
|
-
|
583
574
|
IGNORE_METHODS_IN_SQL = Set[:id, :merge_conditions, :table_name, :quoted_table_name,
|
584
575
|
:quoted_primary_key, :to_i, :to_f, :sanitize_sql, :sanitize_sql_array,
|
585
576
|
:sanitize_sql_for_assignment, :sanitize_sql_for_conditions, :sanitize_sql_hash,
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'brakeman/checks/base_check'
|
2
|
+
|
3
|
+
class Brakeman::CheckTemplateInjection < Brakeman::BaseCheck
|
4
|
+
Brakeman::Checks.add self
|
5
|
+
|
6
|
+
@description = "Searches for evaluation of user input through template injection"
|
7
|
+
|
8
|
+
#Process calls
|
9
|
+
def run_check
|
10
|
+
Brakeman.debug "Finding ERB.new calls"
|
11
|
+
erb_calls = tracker.find_call :target => :ERB, :method => :new, :nested => true
|
12
|
+
|
13
|
+
Brakeman.debug "Processing ERB.new calls"
|
14
|
+
erb_calls.each do |call|
|
15
|
+
process_result call
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
#Warns if eval includes user input
|
20
|
+
def process_result result
|
21
|
+
return unless original? result
|
22
|
+
|
23
|
+
if input = include_user_input?(result[:call].arglist)
|
24
|
+
warn :result => result,
|
25
|
+
:warning_type => "Template Injection",
|
26
|
+
:warning_code => :erb_template_injection,
|
27
|
+
:message => msg(msg_input(input), " used directly in ", msg_code("ERB"), " template, which might enable remote code execution"),
|
28
|
+
:user_input => input,
|
29
|
+
:confidence => :high
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/brakeman/commandline.rb
CHANGED
@@ -102,6 +102,13 @@ module Brakeman
|
|
102
102
|
app_path = "."
|
103
103
|
end
|
104
104
|
|
105
|
+
if options[:ensure_ignore_notes] and options[:previous_results_json]
|
106
|
+
warn '[Notice] --ensure-ignore-notes may not be used at the same ' \
|
107
|
+
'time as --compare. Deactivating --ensure-ignore-notes. ' \
|
108
|
+
'Please see `brakeman --help` for valid options'
|
109
|
+
options[:ensure_ignore_notes] = false
|
110
|
+
end
|
111
|
+
|
105
112
|
return options, app_path
|
106
113
|
end
|
107
114
|
|
@@ -115,7 +122,20 @@ module Brakeman
|
|
115
122
|
|
116
123
|
# Runs a regular report based on the options provided.
|
117
124
|
def regular_report options
|
118
|
-
tracker = run_brakeman options
|
125
|
+
tracker = run_brakeman options
|
126
|
+
|
127
|
+
ensure_ignore_notes_failed = false
|
128
|
+
if tracker.options[:ensure_ignore_notes]
|
129
|
+
fingerprints = Brakeman::ignore_file_entries_with_empty_notes tracker.ignored_filter&.file
|
130
|
+
|
131
|
+
unless fingerprints.empty?
|
132
|
+
ensure_ignore_notes_failed = true
|
133
|
+
warn '[Error] Notes required for all ignored warnings when ' \
|
134
|
+
'--ensure-ignore-notes is set. No notes provided for these ' \
|
135
|
+
'warnings: '
|
136
|
+
fingerprints.each { |f| warn f }
|
137
|
+
end
|
138
|
+
end
|
119
139
|
|
120
140
|
if tracker.options[:exit_on_warn] and not tracker.filtered_warnings.empty?
|
121
141
|
quit Brakeman::Warnings_Found_Exit_Code
|
@@ -124,6 +144,10 @@ module Brakeman
|
|
124
144
|
if tracker.options[:exit_on_error] and tracker.errors.any?
|
125
145
|
quit Brakeman::Errors_Found_Exit_Code
|
126
146
|
end
|
147
|
+
|
148
|
+
if ensure_ignore_notes_failed
|
149
|
+
quit Brakeman::Empty_Ignore_Note_Exit_Code
|
150
|
+
end
|
127
151
|
end
|
128
152
|
|
129
153
|
# Actually run Brakeman.
|
data/lib/brakeman/differ.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# extracting the diff logic to it's own class for consistency. Currently handles
|
2
2
|
# an array of Brakeman::Warnings or plain hash representations.
|
3
3
|
class Brakeman::Differ
|
4
|
-
DEFAULT_HASH = {:new => [], :fixed => []}
|
5
|
-
OLD_WARNING_KEYS = [:warning_type, :location, :code, :message, :file, :link, :confidence, :user_input]
|
6
4
|
attr_reader :old_warnings, :new_warnings
|
7
5
|
|
8
6
|
def initialize new_warnings, old_warnings
|
@@ -11,9 +9,6 @@ class Brakeman::Differ
|
|
11
9
|
end
|
12
10
|
|
13
11
|
def diff
|
14
|
-
# get the type of elements
|
15
|
-
return DEFAULT_HASH if @new_warnings.empty?
|
16
|
-
|
17
12
|
warnings = {}
|
18
13
|
warnings[:new] = @new_warnings - @old_warnings
|
19
14
|
warnings[:fixed] = @old_warnings - @new_warnings
|
data/lib/brakeman/options.rb
CHANGED
@@ -67,6 +67,10 @@ module Brakeman::Options
|
|
67
67
|
options[:ensure_latest] = true
|
68
68
|
end
|
69
69
|
|
70
|
+
opts.on "--ensure-ignore-notes", "Fail when an ignored warnings does not include a note" do
|
71
|
+
options[:ensure_ignore_notes] = true
|
72
|
+
end
|
73
|
+
|
70
74
|
opts.on "-3", "--rails3", "Force Rails 3 mode" do
|
71
75
|
options[:rails3] = true
|
72
76
|
end
|
@@ -225,7 +229,7 @@ module Brakeman::Options
|
|
225
229
|
|
226
230
|
opts.on "-f",
|
227
231
|
"--format TYPE",
|
228
|
-
[:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table],
|
232
|
+
[:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit],
|
229
233
|
"Specify output formats. Default is text" do |type|
|
230
234
|
|
231
235
|
type = "s" if type == :text
|
@@ -301,6 +305,22 @@ module Brakeman::Options
|
|
301
305
|
options[:github_repo] = repo
|
302
306
|
end
|
303
307
|
|
308
|
+
opts.on "--text-fields field1,field2,etc.", Array, "Specify fields for text report format" do |format|
|
309
|
+
valid_options = [:category, :category_id, :check, :code, :confidence, :file, :fingerprint, :line, :link, :message, :render_path]
|
310
|
+
|
311
|
+
options[:text_fields] = format.map(&:to_sym)
|
312
|
+
|
313
|
+
if options[:text_fields] == [:all]
|
314
|
+
options[:text_fields] = valid_options
|
315
|
+
else
|
316
|
+
invalid_options = (options[:text_fields] - valid_options)
|
317
|
+
|
318
|
+
unless invalid_options.empty?
|
319
|
+
raise OptionParser::ParseError, "\nInvalid format options: #{invalid_options.inspect}"
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
304
324
|
opts.on "-w",
|
305
325
|
"--confidence-level LEVEL",
|
306
326
|
["1", "2", "3"],
|
data/lib/brakeman/processor.rb
CHANGED
@@ -53,7 +53,7 @@ module Brakeman
|
|
53
53
|
#Process a model source
|
54
54
|
def process_model src, file_name
|
55
55
|
result = ModelProcessor.new(@tracker).process_model src, file_name
|
56
|
-
AliasProcessor.new(@tracker).process result if result
|
56
|
+
AliasProcessor.new(@tracker, file_name).process result if result
|
57
57
|
end
|
58
58
|
|
59
59
|
#Process either an ERB or HAML template
|
@@ -82,7 +82,6 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
82
82
|
def replace exp, int = 0
|
83
83
|
return exp if int > 3
|
84
84
|
|
85
|
-
|
86
85
|
if replacement = env[exp] and not duplicate? replacement
|
87
86
|
replace(replacement.deep_clone(exp.line), int + 1)
|
88
87
|
elsif tracker and replacement = tracker.constant_lookup(exp) and not duplicate? replacement
|
@@ -731,14 +730,14 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
731
730
|
def array_include_all_literals? exp
|
732
731
|
call? exp and
|
733
732
|
exp.method == :include? and
|
734
|
-
all_literals? exp.target
|
733
|
+
(all_literals? exp.target or dir_glob? exp.target)
|
735
734
|
end
|
736
735
|
|
737
736
|
def array_detect_all_literals? exp
|
738
737
|
call? exp and
|
739
738
|
[:detect, :find].include? exp.method and
|
740
739
|
exp.first_arg.nil? and
|
741
|
-
all_literals? exp.target
|
740
|
+
(all_literals? exp.target or dir_glob? exp.target)
|
742
741
|
end
|
743
742
|
|
744
743
|
#Sets @inside_if = true
|
@@ -20,6 +20,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
20
20
|
@current_template = opts[:template]
|
21
21
|
@current_file = opts[:file]
|
22
22
|
@current_call = nil
|
23
|
+
@full_call = nil
|
23
24
|
process exp
|
24
25
|
end
|
25
26
|
|
@@ -60,7 +61,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
60
61
|
end
|
61
62
|
|
62
63
|
def process_call exp
|
63
|
-
@calls << create_call_hash(exp)
|
64
|
+
@calls << create_call_hash(exp).freeze
|
64
65
|
exp
|
65
66
|
end
|
66
67
|
|
@@ -72,6 +73,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
72
73
|
|
73
74
|
call_hash[:block] = exp.block
|
74
75
|
call_hash[:block_args] = exp.block_args
|
76
|
+
call_hash.freeze
|
75
77
|
|
76
78
|
@calls << call_hash
|
77
79
|
|
@@ -88,7 +90,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
88
90
|
#Calls to render() are converted to s(:render, ...) but we would
|
89
91
|
#like them in the call cache still for speed
|
90
92
|
def process_render exp
|
91
|
-
|
93
|
+
process_all exp
|
92
94
|
|
93
95
|
add_simple_call :render, exp
|
94
96
|
|
@@ -136,7 +138,8 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
136
138
|
:call => exp,
|
137
139
|
:nested => false,
|
138
140
|
:location => make_location,
|
139
|
-
:parent => @current_call
|
141
|
+
:parent => @current_call,
|
142
|
+
:full_call => @full_call }.freeze
|
140
143
|
end
|
141
144
|
|
142
145
|
#Gets the target of a call as a Symbol
|
@@ -213,34 +216,47 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
213
216
|
#Return info hash for a call Sexp
|
214
217
|
def create_call_hash exp
|
215
218
|
target = get_target exp.target
|
216
|
-
|
217
|
-
if call? target or node_type? target, :dxstr # need to index `` even if target of a call
|
218
|
-
already_in_target = @in_target
|
219
|
-
@in_target = true
|
220
|
-
process target
|
221
|
-
@in_target = already_in_target
|
222
|
-
|
223
|
-
target = get_target(target, :include_calls)
|
224
|
-
end
|
219
|
+
target_symbol = get_target(target, :include_calls)
|
225
220
|
|
226
221
|
method = exp.method
|
227
222
|
|
228
223
|
call_hash = {
|
229
|
-
:target =>
|
224
|
+
:target => target_symbol,
|
230
225
|
:method => method,
|
231
226
|
:call => exp,
|
232
227
|
:nested => @in_target,
|
233
228
|
:chain => get_chain(exp),
|
234
229
|
:location => make_location,
|
235
|
-
:parent => @current_call
|
230
|
+
:parent => @current_call,
|
231
|
+
:full_call => @full_call
|
236
232
|
}
|
237
233
|
|
234
|
+
unless @in_target
|
235
|
+
@full_call = call_hash
|
236
|
+
end
|
237
|
+
|
238
|
+
# Process up the call chain
|
239
|
+
if call? target or node_type? target, :dxstr # need to index `` even if target of a call
|
240
|
+
already_in_target = @in_target
|
241
|
+
@in_target = true
|
242
|
+
process target
|
243
|
+
@in_target = already_in_target
|
244
|
+
end
|
245
|
+
|
246
|
+
# Process call arguments
|
247
|
+
# but add the current call as the 'parent'
|
248
|
+
# to any calls in the arguments
|
238
249
|
old_parent = @current_call
|
239
250
|
@current_call = call_hash
|
240
251
|
|
252
|
+
# Do not set @full_call when processing arguments
|
253
|
+
old_full_call = @full_call
|
254
|
+
@full_call = nil
|
255
|
+
|
241
256
|
process_call_args exp
|
242
257
|
|
243
258
|
@current_call = old_parent
|
259
|
+
@full_call = old_full_call
|
244
260
|
|
245
261
|
call_hash
|
246
262
|
end
|