railroader 4.3.4
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 +7 -0
- data/CHANGES.md +1091 -0
- data/FEATURES +16 -0
- data/README.md +174 -0
- data/bin/railroader +8 -0
- data/lib/railroader/app_tree.rb +191 -0
- data/lib/railroader/call_index.rb +219 -0
- data/lib/railroader/checks/base_check.rb +505 -0
- data/lib/railroader/checks/check_basic_auth.rb +88 -0
- data/lib/railroader/checks/check_basic_auth_timing_attack.rb +33 -0
- data/lib/railroader/checks/check_content_tag.rb +200 -0
- data/lib/railroader/checks/check_create_with.rb +74 -0
- data/lib/railroader/checks/check_cross_site_scripting.rb +381 -0
- data/lib/railroader/checks/check_default_routes.rb +86 -0
- data/lib/railroader/checks/check_deserialize.rb +56 -0
- data/lib/railroader/checks/check_detailed_exceptions.rb +55 -0
- data/lib/railroader/checks/check_digest_dos.rb +38 -0
- data/lib/railroader/checks/check_divide_by_zero.rb +42 -0
- data/lib/railroader/checks/check_dynamic_finders.rb +48 -0
- data/lib/railroader/checks/check_escape_function.rb +21 -0
- data/lib/railroader/checks/check_evaluation.rb +35 -0
- data/lib/railroader/checks/check_execute.rb +189 -0
- data/lib/railroader/checks/check_file_access.rb +71 -0
- data/lib/railroader/checks/check_file_disclosure.rb +35 -0
- data/lib/railroader/checks/check_filter_skipping.rb +31 -0
- data/lib/railroader/checks/check_forgery_setting.rb +81 -0
- data/lib/railroader/checks/check_header_dos.rb +31 -0
- data/lib/railroader/checks/check_i18n_xss.rb +48 -0
- data/lib/railroader/checks/check_jruby_xml.rb +36 -0
- data/lib/railroader/checks/check_json_encoding.rb +47 -0
- data/lib/railroader/checks/check_json_parsing.rb +107 -0
- data/lib/railroader/checks/check_link_to.rb +132 -0
- data/lib/railroader/checks/check_link_to_href.rb +146 -0
- data/lib/railroader/checks/check_mail_to.rb +49 -0
- data/lib/railroader/checks/check_mass_assignment.rb +196 -0
- data/lib/railroader/checks/check_mime_type_dos.rb +39 -0
- data/lib/railroader/checks/check_model_attr_accessible.rb +55 -0
- data/lib/railroader/checks/check_model_attributes.rb +119 -0
- data/lib/railroader/checks/check_model_serialize.rb +67 -0
- data/lib/railroader/checks/check_nested_attributes.rb +38 -0
- data/lib/railroader/checks/check_nested_attributes_bypass.rb +58 -0
- data/lib/railroader/checks/check_number_to_currency.rb +74 -0
- data/lib/railroader/checks/check_permit_attributes.rb +43 -0
- data/lib/railroader/checks/check_quote_table_name.rb +40 -0
- data/lib/railroader/checks/check_redirect.rb +256 -0
- data/lib/railroader/checks/check_regex_dos.rb +68 -0
- data/lib/railroader/checks/check_render.rb +97 -0
- data/lib/railroader/checks/check_render_dos.rb +37 -0
- data/lib/railroader/checks/check_render_inline.rb +53 -0
- data/lib/railroader/checks/check_response_splitting.rb +21 -0
- data/lib/railroader/checks/check_route_dos.rb +42 -0
- data/lib/railroader/checks/check_safe_buffer_manipulation.rb +31 -0
- data/lib/railroader/checks/check_sanitize_methods.rb +112 -0
- data/lib/railroader/checks/check_secrets.rb +40 -0
- data/lib/railroader/checks/check_select_tag.rb +59 -0
- data/lib/railroader/checks/check_select_vulnerability.rb +60 -0
- data/lib/railroader/checks/check_send.rb +47 -0
- data/lib/railroader/checks/check_send_file.rb +19 -0
- data/lib/railroader/checks/check_session_manipulation.rb +35 -0
- data/lib/railroader/checks/check_session_settings.rb +176 -0
- data/lib/railroader/checks/check_simple_format.rb +58 -0
- data/lib/railroader/checks/check_single_quotes.rb +101 -0
- data/lib/railroader/checks/check_skip_before_filter.rb +60 -0
- data/lib/railroader/checks/check_sql.rb +700 -0
- data/lib/railroader/checks/check_sql_cves.rb +106 -0
- data/lib/railroader/checks/check_ssl_verify.rb +48 -0
- data/lib/railroader/checks/check_strip_tags.rb +89 -0
- data/lib/railroader/checks/check_symbol_dos.rb +71 -0
- data/lib/railroader/checks/check_symbol_dos_cve.rb +30 -0
- data/lib/railroader/checks/check_translate_bug.rb +45 -0
- data/lib/railroader/checks/check_unsafe_reflection.rb +50 -0
- data/lib/railroader/checks/check_unscoped_find.rb +57 -0
- data/lib/railroader/checks/check_validation_regex.rb +116 -0
- data/lib/railroader/checks/check_weak_hash.rb +148 -0
- data/lib/railroader/checks/check_without_protection.rb +80 -0
- data/lib/railroader/checks/check_xml_dos.rb +45 -0
- data/lib/railroader/checks/check_yaml_parsing.rb +121 -0
- data/lib/railroader/checks.rb +209 -0
- data/lib/railroader/codeclimate/engine_configuration.rb +97 -0
- data/lib/railroader/commandline.rb +179 -0
- data/lib/railroader/differ.rb +66 -0
- data/lib/railroader/file_parser.rb +54 -0
- data/lib/railroader/format/style.css +133 -0
- data/lib/railroader/options.rb +339 -0
- data/lib/railroader/parsers/rails2_erubis.rb +6 -0
- data/lib/railroader/parsers/rails2_xss_plugin_erubis.rb +48 -0
- data/lib/railroader/parsers/rails3_erubis.rb +81 -0
- data/lib/railroader/parsers/template_parser.rb +108 -0
- data/lib/railroader/processor.rb +102 -0
- data/lib/railroader/processors/alias_processor.rb +1229 -0
- data/lib/railroader/processors/base_processor.rb +295 -0
- data/lib/railroader/processors/config_processor.rb +14 -0
- data/lib/railroader/processors/controller_alias_processor.rb +278 -0
- data/lib/railroader/processors/controller_processor.rb +249 -0
- data/lib/railroader/processors/erb_template_processor.rb +77 -0
- data/lib/railroader/processors/erubis_template_processor.rb +92 -0
- data/lib/railroader/processors/gem_processor.rb +64 -0
- data/lib/railroader/processors/haml_template_processor.rb +191 -0
- data/lib/railroader/processors/lib/basic_processor.rb +37 -0
- data/lib/railroader/processors/lib/call_conversion_helper.rb +90 -0
- data/lib/railroader/processors/lib/find_all_calls.rb +224 -0
- data/lib/railroader/processors/lib/find_call.rb +183 -0
- data/lib/railroader/processors/lib/find_return_value.rb +166 -0
- data/lib/railroader/processors/lib/module_helper.rb +111 -0
- data/lib/railroader/processors/lib/processor_helper.rb +88 -0
- data/lib/railroader/processors/lib/rails2_config_processor.rb +145 -0
- data/lib/railroader/processors/lib/rails2_route_processor.rb +313 -0
- data/lib/railroader/processors/lib/rails3_config_processor.rb +132 -0
- data/lib/railroader/processors/lib/rails3_route_processor.rb +308 -0
- data/lib/railroader/processors/lib/render_helper.rb +181 -0
- data/lib/railroader/processors/lib/render_path.rb +107 -0
- data/lib/railroader/processors/lib/route_helper.rb +68 -0
- data/lib/railroader/processors/lib/safe_call_helper.rb +16 -0
- data/lib/railroader/processors/library_processor.rb +74 -0
- data/lib/railroader/processors/model_processor.rb +91 -0
- data/lib/railroader/processors/output_processor.rb +144 -0
- data/lib/railroader/processors/route_processor.rb +17 -0
- data/lib/railroader/processors/slim_template_processor.rb +111 -0
- data/lib/railroader/processors/template_alias_processor.rb +118 -0
- data/lib/railroader/processors/template_processor.rb +85 -0
- data/lib/railroader/report/config/remediation.yml +71 -0
- data/lib/railroader/report/ignore/config.rb +153 -0
- data/lib/railroader/report/ignore/interactive.rb +362 -0
- data/lib/railroader/report/pager.rb +112 -0
- data/lib/railroader/report/renderer.rb +24 -0
- data/lib/railroader/report/report_base.rb +292 -0
- data/lib/railroader/report/report_codeclimate.rb +79 -0
- data/lib/railroader/report/report_csv.rb +55 -0
- data/lib/railroader/report/report_hash.rb +23 -0
- data/lib/railroader/report/report_html.rb +216 -0
- data/lib/railroader/report/report_json.rb +45 -0
- data/lib/railroader/report/report_markdown.rb +107 -0
- data/lib/railroader/report/report_table.rb +117 -0
- data/lib/railroader/report/report_tabs.rb +17 -0
- data/lib/railroader/report/report_text.rb +198 -0
- data/lib/railroader/report/templates/controller_overview.html.erb +22 -0
- data/lib/railroader/report/templates/controller_warnings.html.erb +21 -0
- data/lib/railroader/report/templates/error_overview.html.erb +29 -0
- data/lib/railroader/report/templates/header.html.erb +58 -0
- data/lib/railroader/report/templates/ignored_warnings.html.erb +25 -0
- data/lib/railroader/report/templates/model_warnings.html.erb +21 -0
- data/lib/railroader/report/templates/overview.html.erb +38 -0
- data/lib/railroader/report/templates/security_warnings.html.erb +23 -0
- data/lib/railroader/report/templates/template_overview.html.erb +21 -0
- data/lib/railroader/report/templates/view_warnings.html.erb +34 -0
- data/lib/railroader/report/templates/warning_overview.html.erb +17 -0
- data/lib/railroader/report.rb +88 -0
- data/lib/railroader/rescanner.rb +483 -0
- data/lib/railroader/scanner.rb +321 -0
- data/lib/railroader/tracker/collection.rb +93 -0
- data/lib/railroader/tracker/config.rb +154 -0
- data/lib/railroader/tracker/constants.rb +171 -0
- data/lib/railroader/tracker/controller.rb +161 -0
- data/lib/railroader/tracker/library.rb +17 -0
- data/lib/railroader/tracker/model.rb +90 -0
- data/lib/railroader/tracker/template.rb +33 -0
- data/lib/railroader/tracker.rb +362 -0
- data/lib/railroader/util.rb +503 -0
- data/lib/railroader/version.rb +3 -0
- data/lib/railroader/warning.rb +294 -0
- data/lib/railroader/warning_codes.rb +117 -0
- data/lib/railroader.rb +544 -0
- data/lib/ruby_parser/bm_sexp.rb +626 -0
- data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
- metadata +386 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module Railroader
|
|
2
|
+
module CallConversionHelper
|
|
3
|
+
def all_literals? exp, expected_type = :array
|
|
4
|
+
node_type? exp, expected_type and
|
|
5
|
+
exp.length > 1 and
|
|
6
|
+
exp.all? { |e| e.is_a? Symbol or node_type? e, :lit, :str }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Join two array literals into one.
|
|
10
|
+
def join_arrays lhs, rhs, original_exp = nil
|
|
11
|
+
if array? lhs and array? rhs
|
|
12
|
+
result = Sexp.new(:array).line(lhs.line)
|
|
13
|
+
result.concat lhs[1..-1]
|
|
14
|
+
result.concat rhs[1..-1]
|
|
15
|
+
result
|
|
16
|
+
else
|
|
17
|
+
original_exp
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Join two string literals into one.
|
|
22
|
+
def join_strings lhs, rhs, original_exp = nil
|
|
23
|
+
if string? lhs and string? rhs
|
|
24
|
+
result = Sexp.new(:str).line(lhs.line)
|
|
25
|
+
result.value = lhs.value + rhs.value
|
|
26
|
+
|
|
27
|
+
if result.value.length > 50
|
|
28
|
+
# Avoid gigantic strings
|
|
29
|
+
lhs
|
|
30
|
+
else
|
|
31
|
+
result
|
|
32
|
+
end
|
|
33
|
+
elsif call? lhs and lhs.method == :+ and string? lhs.first_arg and string? rhs
|
|
34
|
+
joined = join_strings lhs.first_arg, rhs
|
|
35
|
+
lhs.first_arg = joined
|
|
36
|
+
lhs
|
|
37
|
+
elsif safe_literal? lhs or safe_literal? rhs
|
|
38
|
+
safe_literal(lhs.line)
|
|
39
|
+
else
|
|
40
|
+
original_exp
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def math_op op, lhs, rhs, original_exp = nil
|
|
45
|
+
if number? lhs and number? rhs
|
|
46
|
+
if op == :/ and rhs.value == 0 and not lhs.value.is_a? Float
|
|
47
|
+
# Avoid division by zero
|
|
48
|
+
return original_exp
|
|
49
|
+
else
|
|
50
|
+
value = lhs.value.send(op, rhs.value)
|
|
51
|
+
Sexp.new(:lit, value).line(lhs.line)
|
|
52
|
+
end
|
|
53
|
+
elsif call? lhs and lhs.method == :+ and number? lhs.first_arg and number? rhs
|
|
54
|
+
# (x + 1) + 2 -> (x + 3)
|
|
55
|
+
lhs.first_arg = Sexp.new(:lit, lhs.first_arg.value + rhs.value).line(lhs.first_arg.line)
|
|
56
|
+
lhs
|
|
57
|
+
elsif safe_literal? lhs or safe_literal? rhs
|
|
58
|
+
safe_literal(lhs.line)
|
|
59
|
+
else
|
|
60
|
+
original_exp
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Process single integer access to an array.
|
|
65
|
+
#
|
|
66
|
+
# Returns the value inside the array, if possible.
|
|
67
|
+
def process_array_access array, args, original_exp = nil
|
|
68
|
+
if args.length == 1 and integer? args.first
|
|
69
|
+
index = args.first.value
|
|
70
|
+
|
|
71
|
+
#Have to do this because first element is :array and we have to skip it
|
|
72
|
+
array[1..-1][index] or original_exp
|
|
73
|
+
else
|
|
74
|
+
original_exp
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Process hash access by returning the value associated
|
|
79
|
+
# with the given argument.
|
|
80
|
+
def process_hash_access hash, index, original_exp = nil
|
|
81
|
+
if value = hash_access(hash, index)
|
|
82
|
+
value # deep_clone?
|
|
83
|
+
elsif all_literals? hash, :hash
|
|
84
|
+
safe_literal(hash.line)
|
|
85
|
+
else
|
|
86
|
+
original_exp
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
require 'railroader/processors/lib/basic_processor'
|
|
2
|
+
|
|
3
|
+
class Railroader::FindAllCalls < Railroader::BasicProcessor
|
|
4
|
+
attr_reader :calls
|
|
5
|
+
|
|
6
|
+
def initialize tracker
|
|
7
|
+
super
|
|
8
|
+
@current_class = nil
|
|
9
|
+
@current_method = nil
|
|
10
|
+
@in_target = false
|
|
11
|
+
@calls = []
|
|
12
|
+
@cache = {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
#Process the given source. Provide either class and method being searched
|
|
16
|
+
#or the template. These names are used when reporting results.
|
|
17
|
+
def process_source exp, opts
|
|
18
|
+
@current_class = opts[:class]
|
|
19
|
+
@current_method = opts[:method]
|
|
20
|
+
@current_template = opts[:template]
|
|
21
|
+
@current_file = opts[:file]
|
|
22
|
+
@current_call = nil
|
|
23
|
+
process exp
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
#Process body of method
|
|
27
|
+
def process_defn exp
|
|
28
|
+
return exp unless @current_method
|
|
29
|
+
process_all exp.body
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
alias process_defs process_defn
|
|
33
|
+
|
|
34
|
+
#Process body of block
|
|
35
|
+
def process_rlist exp
|
|
36
|
+
process_all exp
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def process_call exp
|
|
40
|
+
@calls << create_call_hash(exp)
|
|
41
|
+
exp
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def process_iter exp
|
|
45
|
+
call = exp.block_call
|
|
46
|
+
|
|
47
|
+
if call.node_type == :call
|
|
48
|
+
call_hash = create_call_hash(call)
|
|
49
|
+
|
|
50
|
+
call_hash[:block] = exp.block
|
|
51
|
+
call_hash[:block_args] = exp.block_args
|
|
52
|
+
|
|
53
|
+
@calls << call_hash
|
|
54
|
+
|
|
55
|
+
process exp.block
|
|
56
|
+
else
|
|
57
|
+
#Probably a :render call with block
|
|
58
|
+
process call
|
|
59
|
+
process exp.block
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
exp
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
#Calls to render() are converted to s(:render, ...) but we would
|
|
66
|
+
#like them in the call cache still for speed
|
|
67
|
+
def process_render exp
|
|
68
|
+
process exp.last if sexp? exp.last
|
|
69
|
+
|
|
70
|
+
add_simple_call :render, exp
|
|
71
|
+
|
|
72
|
+
exp
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
#Technically, `` is call to Kernel#`
|
|
76
|
+
#But we just need them in the call cache for speed
|
|
77
|
+
def process_dxstr exp
|
|
78
|
+
process exp.last if sexp? exp.last
|
|
79
|
+
|
|
80
|
+
add_simple_call :`, exp
|
|
81
|
+
|
|
82
|
+
exp
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
#:"string" is equivalent to "string".to_sym
|
|
86
|
+
def process_dsym exp
|
|
87
|
+
exp.each { |arg| process arg if sexp? arg }
|
|
88
|
+
|
|
89
|
+
add_simple_call :literal_to_sym, exp
|
|
90
|
+
|
|
91
|
+
exp
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Process a dynamic regex like a call
|
|
95
|
+
def process_dregx exp
|
|
96
|
+
exp.each { |arg| process arg if sexp? arg }
|
|
97
|
+
|
|
98
|
+
add_simple_call :railroader_regex_interp, exp
|
|
99
|
+
|
|
100
|
+
exp
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
#Process an assignment like a call
|
|
104
|
+
def process_attrasgn exp
|
|
105
|
+
process_call exp
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
def add_simple_call method_name, exp
|
|
111
|
+
@calls << { :target => nil,
|
|
112
|
+
:method => method_name,
|
|
113
|
+
:call => exp,
|
|
114
|
+
:nested => false,
|
|
115
|
+
:location => make_location,
|
|
116
|
+
:parent => @current_call }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
#Gets the target of a call as a Symbol
|
|
120
|
+
#if possible
|
|
121
|
+
def get_target exp, include_calls = false
|
|
122
|
+
if sexp? exp
|
|
123
|
+
case exp.node_type
|
|
124
|
+
when :ivar, :lvar, :const, :lit
|
|
125
|
+
exp.value
|
|
126
|
+
when :true, :false
|
|
127
|
+
exp[0]
|
|
128
|
+
when :colon2
|
|
129
|
+
class_name exp
|
|
130
|
+
when :self
|
|
131
|
+
@current_class || @current_module || nil
|
|
132
|
+
when :params, :session, :cookies
|
|
133
|
+
exp.node_type
|
|
134
|
+
when :call, :safe_call
|
|
135
|
+
if include_calls
|
|
136
|
+
if exp.target.nil?
|
|
137
|
+
exp.method
|
|
138
|
+
else
|
|
139
|
+
t = get_target(exp.target, :include_calls)
|
|
140
|
+
if t.is_a? Symbol
|
|
141
|
+
:"#{t}.#{exp.method}"
|
|
142
|
+
else
|
|
143
|
+
exp
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
else
|
|
147
|
+
exp
|
|
148
|
+
end
|
|
149
|
+
else
|
|
150
|
+
exp
|
|
151
|
+
end
|
|
152
|
+
else
|
|
153
|
+
exp
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
#Returns method chain as an array
|
|
158
|
+
#For example, User.human.alive.all would return [:User, :human, :alive, :all]
|
|
159
|
+
def get_chain call
|
|
160
|
+
if node_type? call, :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
161
|
+
get_chain(call.target) + [call.method]
|
|
162
|
+
elsif call.nil?
|
|
163
|
+
[]
|
|
164
|
+
else
|
|
165
|
+
[get_target(call)]
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def make_location
|
|
170
|
+
if @current_template
|
|
171
|
+
key = [@current_template, @current_file]
|
|
172
|
+
cached = @cache[key]
|
|
173
|
+
return cached if cached
|
|
174
|
+
|
|
175
|
+
@cache[key] = { :type => :template,
|
|
176
|
+
:template => @current_template,
|
|
177
|
+
:file => @current_file }
|
|
178
|
+
else
|
|
179
|
+
key = [@current_class, @current_method, @current_file]
|
|
180
|
+
cached = @cache[key]
|
|
181
|
+
return cached if cached
|
|
182
|
+
@cache[key] = { :type => :class,
|
|
183
|
+
:class => @current_class,
|
|
184
|
+
:method => @current_method,
|
|
185
|
+
:file => @current_file }
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
#Return info hash for a call Sexp
|
|
191
|
+
def create_call_hash exp
|
|
192
|
+
target = get_target exp.target
|
|
193
|
+
|
|
194
|
+
if call? target or node_type? target, :dxstr # need to index `` even if target of a call
|
|
195
|
+
already_in_target = @in_target
|
|
196
|
+
@in_target = true
|
|
197
|
+
process target
|
|
198
|
+
@in_target = already_in_target
|
|
199
|
+
|
|
200
|
+
target = get_target(target, :include_calls)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
method = exp.method
|
|
204
|
+
|
|
205
|
+
call_hash = {
|
|
206
|
+
:target => target,
|
|
207
|
+
:method => method,
|
|
208
|
+
:call => exp,
|
|
209
|
+
:nested => @in_target,
|
|
210
|
+
:chain => get_chain(exp),
|
|
211
|
+
:location => make_location,
|
|
212
|
+
:parent => @current_call
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
old_parent = @current_call
|
|
216
|
+
@current_call = call_hash
|
|
217
|
+
|
|
218
|
+
process_call_args exp
|
|
219
|
+
|
|
220
|
+
@current_call = old_parent
|
|
221
|
+
|
|
222
|
+
call_hash
|
|
223
|
+
end
|
|
224
|
+
end
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
require 'railroader/processors/lib/basic_processor'
|
|
2
|
+
|
|
3
|
+
#Finds method calls matching the given target(s).
|
|
4
|
+
# #-- This should be deprecated --#
|
|
5
|
+
# #-- Do not use for new code --#
|
|
6
|
+
#
|
|
7
|
+
#Targets/methods can be:
|
|
8
|
+
#
|
|
9
|
+
# - nil: matches anything, including nothing
|
|
10
|
+
# - Empty array: matches nothing
|
|
11
|
+
# - Symbol: matches single target/method exactly
|
|
12
|
+
# - Array of symbols: matches against any of the symbols
|
|
13
|
+
# - Regular expression: matches the expression
|
|
14
|
+
# - Array of regular expressions: matches any of the expressions
|
|
15
|
+
#
|
|
16
|
+
#If a target is also the name of a class, methods called on instances
|
|
17
|
+
#of that class will also be matched, in a very limited way.
|
|
18
|
+
#(Any methods called on Klass.new, basically. More useful when used
|
|
19
|
+
#in conjunction with AliasProcessor.)
|
|
20
|
+
#
|
|
21
|
+
#Examples:
|
|
22
|
+
#
|
|
23
|
+
# #To find any uses of this class:
|
|
24
|
+
# FindCall.new :FindCall, nil
|
|
25
|
+
#
|
|
26
|
+
# #Find system calls without a target
|
|
27
|
+
# FindCall.new [], [:system, :exec, :syscall]
|
|
28
|
+
#
|
|
29
|
+
# #Find all calls to length(), no matter the target
|
|
30
|
+
# FindCall.new nil, :length
|
|
31
|
+
#
|
|
32
|
+
# #Find all calls to sub, sub!, gsub, or gsub!
|
|
33
|
+
# FindCall.new nil, /^g?sub!?$/
|
|
34
|
+
class Railroader::FindCall < Railroader::BasicProcessor
|
|
35
|
+
|
|
36
|
+
def initialize targets, methods, tracker, in_depth = false
|
|
37
|
+
super tracker
|
|
38
|
+
@calls = []
|
|
39
|
+
@find_targets = targets
|
|
40
|
+
@find_methods = methods
|
|
41
|
+
@current_class = nil
|
|
42
|
+
@current_method = nil
|
|
43
|
+
@in_depth = in_depth
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
#Returns a list of results.
|
|
47
|
+
#
|
|
48
|
+
#A result looks like:
|
|
49
|
+
#
|
|
50
|
+
# s(:result, :ClassName, :method_name, s(:call, ...))
|
|
51
|
+
#
|
|
52
|
+
#or
|
|
53
|
+
#
|
|
54
|
+
# s(:result, :template_name, s(:call, ...))
|
|
55
|
+
def matches
|
|
56
|
+
@calls
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
#Process the given source. Provide either class and method being searched
|
|
60
|
+
#or the template. These names are used when reporting results.
|
|
61
|
+
#
|
|
62
|
+
#Use FindCall#matches to retrieve results.
|
|
63
|
+
def process_source exp, klass = nil, method = nil, template = nil
|
|
64
|
+
@current_class = klass
|
|
65
|
+
@current_method = method
|
|
66
|
+
@current_template = template
|
|
67
|
+
process exp
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
#Process body of method
|
|
71
|
+
def process_defn exp
|
|
72
|
+
process_all exp.body
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
alias :process_defs :process_defn
|
|
76
|
+
|
|
77
|
+
#Process body of block
|
|
78
|
+
def process_rlist exp
|
|
79
|
+
process_all exp
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
#Look for matching calls and add them to results
|
|
83
|
+
def process_call exp
|
|
84
|
+
target = get_target exp.target
|
|
85
|
+
method = exp.method
|
|
86
|
+
|
|
87
|
+
process_call_args exp
|
|
88
|
+
|
|
89
|
+
if match(@find_targets, target) and match(@find_methods, method)
|
|
90
|
+
|
|
91
|
+
if @current_template
|
|
92
|
+
@calls << Sexp.new(:result, @current_template, exp).line(exp.line)
|
|
93
|
+
else
|
|
94
|
+
@calls << Sexp.new(:result, @current_module, @current_class, @current_method, exp).line(exp.line)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
#Normally FindCall won't match a method invocation that is the target of
|
|
100
|
+
#another call, such as:
|
|
101
|
+
#
|
|
102
|
+
# User.find(:first, :conditions => "user = '#{params['user']}').name
|
|
103
|
+
#
|
|
104
|
+
#A search for User.find will not match this unless @in_depth is true.
|
|
105
|
+
if @in_depth and call? exp.target
|
|
106
|
+
process exp.target
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
exp
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
#Process an assignment like a call
|
|
113
|
+
def process_attrasgn exp
|
|
114
|
+
process_call exp
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
#Gets the target of a call as a Symbol
|
|
120
|
+
#if possible
|
|
121
|
+
def get_target exp
|
|
122
|
+
if sexp? exp
|
|
123
|
+
case exp.node_type
|
|
124
|
+
when :ivar, :lvar, :const, :lit
|
|
125
|
+
exp.value
|
|
126
|
+
when :true, :false
|
|
127
|
+
exp.node_type
|
|
128
|
+
when :colon2
|
|
129
|
+
class_name exp
|
|
130
|
+
else
|
|
131
|
+
exp
|
|
132
|
+
end
|
|
133
|
+
else
|
|
134
|
+
exp
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
#Checks if the search terms match the given item
|
|
139
|
+
def match search_terms, item
|
|
140
|
+
case search_terms
|
|
141
|
+
when Symbol
|
|
142
|
+
if search_terms == item
|
|
143
|
+
true
|
|
144
|
+
elsif sexp? item
|
|
145
|
+
is_instance_of? item, search_terms
|
|
146
|
+
else
|
|
147
|
+
false
|
|
148
|
+
end
|
|
149
|
+
when Sexp
|
|
150
|
+
search_terms == item
|
|
151
|
+
when Enumerable
|
|
152
|
+
if search_terms.empty?
|
|
153
|
+
item == nil
|
|
154
|
+
else
|
|
155
|
+
search_terms.each do|term|
|
|
156
|
+
if match(term, item)
|
|
157
|
+
return true
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
false
|
|
161
|
+
end
|
|
162
|
+
when Regexp
|
|
163
|
+
search_terms.match item.to_s
|
|
164
|
+
when nil
|
|
165
|
+
true
|
|
166
|
+
else
|
|
167
|
+
raise "Cannot match #{search_terms} and #{item}"
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
#Checks if +item+ is an instance of +klass+ by looking for Klass.new
|
|
172
|
+
def is_instance_of? item, klass
|
|
173
|
+
if call? item
|
|
174
|
+
if sexp? item.target
|
|
175
|
+
item.method == :new and item.target.node_type == :const and item.target.value == klass
|
|
176
|
+
else
|
|
177
|
+
item.method == :new and item.target == klass
|
|
178
|
+
end
|
|
179
|
+
else
|
|
180
|
+
false
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
require 'railroader/processors/alias_processor'
|
|
2
|
+
|
|
3
|
+
#Attempts to determine the return value of a method.
|
|
4
|
+
#
|
|
5
|
+
#Preferred usage:
|
|
6
|
+
#
|
|
7
|
+
# Railroader::FindReturnValue.return_value exp
|
|
8
|
+
class Railroader::FindReturnValue
|
|
9
|
+
include Railroader::Util
|
|
10
|
+
|
|
11
|
+
#Returns a guess at the return value of a given method or other block of code.
|
|
12
|
+
#
|
|
13
|
+
#If multiple return values are possible, returns all values in an :or Sexp.
|
|
14
|
+
def self.return_value exp, env = nil
|
|
15
|
+
self.new.get_return_value exp, env
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize
|
|
19
|
+
@uses_ivars = false
|
|
20
|
+
@return_values = []
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def uses_ivars?
|
|
24
|
+
@uses_ivars
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
#Find return value of Sexp. Takes an optional starting environment.
|
|
28
|
+
def get_return_value exp, env = nil
|
|
29
|
+
process_method exp, env
|
|
30
|
+
value = make_return_value
|
|
31
|
+
value.original_line = exp.line
|
|
32
|
+
value
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#Process method (or, actually, any Sexp) for return value.
|
|
36
|
+
def process_method exp, env = nil
|
|
37
|
+
exp = Railroader::AliasProcessor.new.process_safely exp, env
|
|
38
|
+
|
|
39
|
+
find_explicit_return_values exp
|
|
40
|
+
|
|
41
|
+
if node_type? exp, :defn, :defs
|
|
42
|
+
body = exp.body
|
|
43
|
+
|
|
44
|
+
unless body.empty?
|
|
45
|
+
@return_values << last_value(body)
|
|
46
|
+
else
|
|
47
|
+
Railroader.debug "FindReturnValue: Empty method? #{exp.inspect}"
|
|
48
|
+
end
|
|
49
|
+
elsif exp
|
|
50
|
+
@return_values << last_value(exp)
|
|
51
|
+
else
|
|
52
|
+
Railroader.debug "FindReturnValue: Given something strange? #{exp.inspect}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
exp
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
#Searches expression for return statements.
|
|
59
|
+
def find_explicit_return_values exp
|
|
60
|
+
todo = [exp]
|
|
61
|
+
|
|
62
|
+
until todo.empty?
|
|
63
|
+
current = todo.shift
|
|
64
|
+
|
|
65
|
+
@uses_ivars = true if node_type? current, :ivar
|
|
66
|
+
|
|
67
|
+
if node_type? current, :return
|
|
68
|
+
@return_values << last_value(current.value) if current.value
|
|
69
|
+
elsif sexp? current
|
|
70
|
+
todo = current[1..-1].concat todo
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
#Determines the "last value" of an expression.
|
|
76
|
+
def last_value exp
|
|
77
|
+
case exp.node_type
|
|
78
|
+
when :rlist, :block, :scope, Sexp
|
|
79
|
+
last_value exp.last
|
|
80
|
+
when :if
|
|
81
|
+
then_clause = exp.then_clause
|
|
82
|
+
else_clause = exp.else_clause
|
|
83
|
+
|
|
84
|
+
if then_clause.nil? and else_clause.nil?
|
|
85
|
+
nil
|
|
86
|
+
elsif then_clause.nil?
|
|
87
|
+
last_value else_clause
|
|
88
|
+
elsif else_clause.nil?
|
|
89
|
+
last_value then_clause
|
|
90
|
+
else
|
|
91
|
+
true_branch = last_value then_clause
|
|
92
|
+
false_branch = last_value else_clause
|
|
93
|
+
|
|
94
|
+
if true_branch and false_branch
|
|
95
|
+
value = make_or(true_branch, false_branch)
|
|
96
|
+
value.original_line = value.rhs.line
|
|
97
|
+
value
|
|
98
|
+
else #Unlikely?
|
|
99
|
+
true_branch or false_branch
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
when :lasgn, :iasgn, :op_asgn_or, :attrasgn
|
|
103
|
+
last_value exp.rhs
|
|
104
|
+
when :rescue
|
|
105
|
+
values = []
|
|
106
|
+
|
|
107
|
+
exp.each_sexp do |e|
|
|
108
|
+
if node_type? e, :resbody
|
|
109
|
+
if e.last
|
|
110
|
+
values << last_value(e.last)
|
|
111
|
+
end
|
|
112
|
+
elsif sexp? e
|
|
113
|
+
values << last_value(e)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
values.reject! do |v|
|
|
118
|
+
v.nil? or node_type? v, :nil
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
if values.length > 1
|
|
122
|
+
values.inject do |m, v|
|
|
123
|
+
make_or(m, v)
|
|
124
|
+
end
|
|
125
|
+
else
|
|
126
|
+
values.first
|
|
127
|
+
end
|
|
128
|
+
when :return
|
|
129
|
+
if exp.value
|
|
130
|
+
last_value exp.value
|
|
131
|
+
else
|
|
132
|
+
nil
|
|
133
|
+
end
|
|
134
|
+
when :nil
|
|
135
|
+
nil
|
|
136
|
+
else
|
|
137
|
+
exp.original_line = exp.line unless exp.original_line
|
|
138
|
+
exp
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def make_or lhs, rhs
|
|
143
|
+
#Better checks in future
|
|
144
|
+
if lhs == rhs
|
|
145
|
+
lhs
|
|
146
|
+
else
|
|
147
|
+
Sexp.new(:or, lhs, rhs)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
#Turns the array of return values into an :or Sexp
|
|
152
|
+
def make_return_value
|
|
153
|
+
@return_values.compact!
|
|
154
|
+
@return_values.uniq!
|
|
155
|
+
|
|
156
|
+
if @return_values.empty?
|
|
157
|
+
Sexp.new(:nil)
|
|
158
|
+
elsif @return_values.length == 1
|
|
159
|
+
@return_values.first
|
|
160
|
+
else
|
|
161
|
+
@return_values.reduce do |value, sexp|
|
|
162
|
+
make_or value, sexp
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|