brakeman-lib 4.7.1 → 4.9.0
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.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
|