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,249 @@
|
|
|
1
|
+
require 'railroader/processors/base_processor'
|
|
2
|
+
require 'railroader/processors/lib/module_helper'
|
|
3
|
+
require 'railroader/tracker/controller'
|
|
4
|
+
|
|
5
|
+
#Processes controller. Results are put in tracker.controllers
|
|
6
|
+
class Railroader::ControllerProcessor < Railroader::BaseProcessor
|
|
7
|
+
include Railroader::ModuleHelper
|
|
8
|
+
|
|
9
|
+
FORMAT_HTML = Sexp.new(:call, Sexp.new(:lvar, :format), :html)
|
|
10
|
+
|
|
11
|
+
def initialize app_tree, tracker
|
|
12
|
+
super(tracker)
|
|
13
|
+
@app_tree = app_tree
|
|
14
|
+
@current_class = nil
|
|
15
|
+
@current_method = nil
|
|
16
|
+
@current_module = nil
|
|
17
|
+
@visibility = :public
|
|
18
|
+
@file_name = nil
|
|
19
|
+
@concerns = Set.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#Use this method to process a Controller
|
|
23
|
+
def process_controller src, file_name = nil
|
|
24
|
+
@file_name = file_name
|
|
25
|
+
process src
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
#s(:class, NAME, PARENT, s(:scope ...))
|
|
29
|
+
def process_class exp
|
|
30
|
+
name = class_name(exp.class_name)
|
|
31
|
+
parent = class_name(exp.parent_name)
|
|
32
|
+
|
|
33
|
+
#If inside a real controller, treat any other classes as libraries.
|
|
34
|
+
#But if not inside a controller already, then the class may include
|
|
35
|
+
#a real controller, so we can't take this shortcut.
|
|
36
|
+
if @current_class and @current_class.name.to_s.end_with? "Controller"
|
|
37
|
+
Railroader.debug "[Notice] Treating inner class as library: #{name}"
|
|
38
|
+
Railroader::LibraryProcessor.new(@tracker).process_library exp, @file_name
|
|
39
|
+
return exp
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if not name.to_s.end_with? "Controller"
|
|
43
|
+
Railroader.debug "[Notice] Adding noncontroller as library: #{name}"
|
|
44
|
+
#Set the class to be a module in order to get the right namespacing.
|
|
45
|
+
#Add class to libraries, in case it is needed later (e.g. it's used
|
|
46
|
+
#as a parent class for a controller.)
|
|
47
|
+
#However, still want to process it in this class, so have to set
|
|
48
|
+
#@current_class to this not-really-a-controller thing.
|
|
49
|
+
process_module exp, parent
|
|
50
|
+
|
|
51
|
+
return exp
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
handle_class(exp, @tracker.controllers, Railroader::Controller) do
|
|
55
|
+
set_layout_name
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
exp
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def process_module exp, parent = nil
|
|
62
|
+
handle_module exp, Railroader::Controller, parent
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def process_concern concern_name
|
|
66
|
+
return unless @current_class
|
|
67
|
+
|
|
68
|
+
if mod = @tracker.find_class(concern_name)
|
|
69
|
+
if mod.options[:included] and not @concerns.include? concern_name
|
|
70
|
+
@concerns << concern_name
|
|
71
|
+
process mod.options[:included].deep_clone
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
#Look for specific calls inside the controller
|
|
77
|
+
def process_call exp
|
|
78
|
+
return exp if process_call_defn? exp
|
|
79
|
+
|
|
80
|
+
target = exp.target
|
|
81
|
+
if sexp? target
|
|
82
|
+
target = process target
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
method = exp.method
|
|
86
|
+
first_arg = exp.first_arg
|
|
87
|
+
last_arg = exp.last_arg
|
|
88
|
+
|
|
89
|
+
#Methods called inside class definition
|
|
90
|
+
#like attr_* and other settings
|
|
91
|
+
if @current_method.nil? and target.nil? and @current_class
|
|
92
|
+
if first_arg.nil? #No args
|
|
93
|
+
case method
|
|
94
|
+
when :private, :protected, :public
|
|
95
|
+
@visibility = method
|
|
96
|
+
when :protect_from_forgery
|
|
97
|
+
@current_class.options[:protect_from_forgery] = true
|
|
98
|
+
else
|
|
99
|
+
#??
|
|
100
|
+
end
|
|
101
|
+
else
|
|
102
|
+
case method
|
|
103
|
+
when :include
|
|
104
|
+
if @current_class
|
|
105
|
+
concern = class_name(first_arg)
|
|
106
|
+
@current_class.add_include concern
|
|
107
|
+
process_concern concern
|
|
108
|
+
end
|
|
109
|
+
when :before_filter, :append_before_filter, :before_action, :append_before_action
|
|
110
|
+
if node_type? exp.first_arg, :iter
|
|
111
|
+
add_lambda_filter exp
|
|
112
|
+
else
|
|
113
|
+
@current_class.add_before_filter exp
|
|
114
|
+
end
|
|
115
|
+
when :prepend_before_filter, :prepend_before_action
|
|
116
|
+
if node_type? exp.first_arg, :iter
|
|
117
|
+
add_lambda_filter exp
|
|
118
|
+
else
|
|
119
|
+
@current_class.prepend_before_filter exp
|
|
120
|
+
end
|
|
121
|
+
when :skip_before_filter, :skip_filter, :skip_before_action, :skip_action_callback
|
|
122
|
+
@current_class.skip_filter exp
|
|
123
|
+
when :layout
|
|
124
|
+
if string? last_arg
|
|
125
|
+
#layout "some_layout"
|
|
126
|
+
|
|
127
|
+
name = last_arg.value.to_s
|
|
128
|
+
if @app_tree.layout_exists?(name)
|
|
129
|
+
@current_class.layout = "layouts/#{name}"
|
|
130
|
+
else
|
|
131
|
+
Railroader.debug "[Notice] Layout not found: #{name}"
|
|
132
|
+
end
|
|
133
|
+
elsif node_type? last_arg, :nil, :false
|
|
134
|
+
#layout :false or layout nil
|
|
135
|
+
@current_class.layout = false
|
|
136
|
+
end
|
|
137
|
+
else
|
|
138
|
+
@current_class.add_option method, exp
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
exp
|
|
143
|
+
elsif target == nil and method == :render
|
|
144
|
+
make_render exp
|
|
145
|
+
elsif exp == FORMAT_HTML and context[1] != :iter
|
|
146
|
+
#This is an empty call to
|
|
147
|
+
# format.html
|
|
148
|
+
#Which renders the default template if no arguments
|
|
149
|
+
#Need to make more generic, though.
|
|
150
|
+
call = Sexp.new :render, :default, @current_method
|
|
151
|
+
call.line(exp.line)
|
|
152
|
+
call
|
|
153
|
+
else
|
|
154
|
+
call = make_call target, method, process_all!(exp.args)
|
|
155
|
+
call.line(exp.line)
|
|
156
|
+
call
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
#Look for before_filters and add fake ones if necessary
|
|
161
|
+
def process_iter exp
|
|
162
|
+
if @current_method.nil? and call? exp.block_call
|
|
163
|
+
block_call_name = exp.block_call.method
|
|
164
|
+
|
|
165
|
+
if block_call_name == :before_filter or block_call_name == :before_action
|
|
166
|
+
add_fake_filter exp
|
|
167
|
+
else
|
|
168
|
+
super
|
|
169
|
+
end
|
|
170
|
+
else
|
|
171
|
+
super
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
#Sets default layout for renders inside Controller
|
|
176
|
+
def set_layout_name
|
|
177
|
+
return if @current_class.layout
|
|
178
|
+
|
|
179
|
+
name = underscore(@current_class.name.to_s.split("::")[-1].gsub("Controller", ''))
|
|
180
|
+
|
|
181
|
+
#There is a layout for this Controller
|
|
182
|
+
if @app_tree.layout_exists?(name)
|
|
183
|
+
@current_class.layout = "layouts/#{name}"
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
#This is to handle before_filter do |controller| ... end
|
|
188
|
+
#
|
|
189
|
+
#We build a new method and process that the same way as usual
|
|
190
|
+
#methods and filters.
|
|
191
|
+
def add_fake_filter exp
|
|
192
|
+
unless @current_class
|
|
193
|
+
Railroader.debug "Skipping before_filter outside controller: #{exp}"
|
|
194
|
+
return exp
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
filter_name = ("fake_filter" + rand.to_s[/\d+$/]).to_sym
|
|
198
|
+
args = exp.block_call.arglist
|
|
199
|
+
args.insert(1, Sexp.new(:lit, filter_name))
|
|
200
|
+
before_filter_call = make_call(nil, :before_filter, args)
|
|
201
|
+
|
|
202
|
+
if exp.block_args.length > 1
|
|
203
|
+
block_variable = exp.block_args[1]
|
|
204
|
+
else
|
|
205
|
+
block_variable = :temp
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
if node_type? exp.block, :block
|
|
209
|
+
block_inner = exp.block[1..-1]
|
|
210
|
+
else
|
|
211
|
+
block_inner = [exp.block]
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
#Build Sexp for filter method
|
|
215
|
+
body = Sexp.new(:lasgn,
|
|
216
|
+
block_variable,
|
|
217
|
+
Sexp.new(:call, Sexp.new(:const, @current_class.name), :new))
|
|
218
|
+
|
|
219
|
+
filter_method = Sexp.new(:defn, filter_name, Sexp.new(:args), body).concat(block_inner).line(exp.line)
|
|
220
|
+
|
|
221
|
+
vis = @visibility
|
|
222
|
+
@visibility = :private
|
|
223
|
+
process_defn filter_method
|
|
224
|
+
@visibility = vis
|
|
225
|
+
process before_filter_call
|
|
226
|
+
exp
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def add_lambda_filter exp
|
|
230
|
+
# Convert into regular block call
|
|
231
|
+
e = exp.dup
|
|
232
|
+
lambda_node = e.delete_at(3)
|
|
233
|
+
result = Sexp.new(:iter, e).line(e.line)
|
|
234
|
+
|
|
235
|
+
# Add block arguments
|
|
236
|
+
if node_type? lambda_node[2], :args
|
|
237
|
+
result << lambda_node[2].last
|
|
238
|
+
else
|
|
239
|
+
result << s(:args)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Add block contents
|
|
243
|
+
if sexp? lambda_node[3]
|
|
244
|
+
result << lambda_node[3]
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
add_fake_filter result
|
|
248
|
+
end
|
|
249
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require 'railroader/processors/template_processor'
|
|
2
|
+
|
|
3
|
+
#Processes ERB templates
|
|
4
|
+
#(those ending in .html.erb or .rthml).
|
|
5
|
+
class Railroader::ErbTemplateProcessor < Railroader::TemplateProcessor
|
|
6
|
+
|
|
7
|
+
#s(:call, TARGET, :method, ARGS)
|
|
8
|
+
def process_call exp
|
|
9
|
+
target = exp.target
|
|
10
|
+
if sexp? target
|
|
11
|
+
target = process target
|
|
12
|
+
end
|
|
13
|
+
method = exp.method
|
|
14
|
+
|
|
15
|
+
#_erbout is the default output variable for erb
|
|
16
|
+
if node_type? target, :lvar and target.value == :_erbout
|
|
17
|
+
if method == :concat or method == :<<
|
|
18
|
+
@inside_concat = true
|
|
19
|
+
exp.arglist = process(exp.arglist)
|
|
20
|
+
@inside_concat = false
|
|
21
|
+
|
|
22
|
+
if exp.second_arg
|
|
23
|
+
raise "Did not expect more than a single argument to _erbout.concat"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
arg = normalize_output(exp.first_arg)
|
|
27
|
+
|
|
28
|
+
if arg.node_type == :str #ignore plain strings
|
|
29
|
+
ignore
|
|
30
|
+
else
|
|
31
|
+
add_output arg
|
|
32
|
+
end
|
|
33
|
+
elsif method == :force_encoding
|
|
34
|
+
ignore
|
|
35
|
+
else
|
|
36
|
+
abort "Unrecognized action on _erbout: #{method}"
|
|
37
|
+
end
|
|
38
|
+
elsif target == nil and method == :render
|
|
39
|
+
exp.arglist = process(exp.arglist)
|
|
40
|
+
make_render_in_view exp
|
|
41
|
+
else
|
|
42
|
+
exp.target = target
|
|
43
|
+
exp.arglist = process(exp.arglist)
|
|
44
|
+
exp
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
#Process block, removing irrelevant expressions
|
|
49
|
+
def process_block exp
|
|
50
|
+
exp = exp.dup
|
|
51
|
+
exp.shift
|
|
52
|
+
if @inside_concat
|
|
53
|
+
@inside_concat = false
|
|
54
|
+
exp[0..-2].each do |e|
|
|
55
|
+
process e
|
|
56
|
+
end
|
|
57
|
+
@inside_concat = true
|
|
58
|
+
process exp.last
|
|
59
|
+
else
|
|
60
|
+
exp.map! do |e|
|
|
61
|
+
res = process e
|
|
62
|
+
if res.empty? or res == ignore
|
|
63
|
+
nil
|
|
64
|
+
elsif node_type?(res, :lvar) and res.value == :_erbout
|
|
65
|
+
nil
|
|
66
|
+
|
|
67
|
+
else
|
|
68
|
+
res
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
block = Sexp.new(:rlist).concat(exp).compact
|
|
72
|
+
block.line(exp.line)
|
|
73
|
+
block
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require 'railroader/processors/template_processor'
|
|
2
|
+
|
|
3
|
+
#Processes ERB templates using Erubis instead of erb.
|
|
4
|
+
class Railroader::ErubisTemplateProcessor < Railroader::TemplateProcessor
|
|
5
|
+
|
|
6
|
+
#s(:call, TARGET, :method, ARGS)
|
|
7
|
+
def process_call exp
|
|
8
|
+
target = exp.target
|
|
9
|
+
if sexp? target
|
|
10
|
+
target = process target
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
exp.target = target
|
|
14
|
+
exp.arglist = process exp.arglist
|
|
15
|
+
method = exp.method
|
|
16
|
+
|
|
17
|
+
#_buf is the default output variable for Erubis
|
|
18
|
+
if node_type?(target, :lvar, :ivar) and (target.value == :_buf or target.value == :@output_buffer)
|
|
19
|
+
if method == :<< or method == :safe_concat
|
|
20
|
+
|
|
21
|
+
arg = normalize_output(exp.first_arg)
|
|
22
|
+
|
|
23
|
+
if arg.node_type == :str #ignore plain strings
|
|
24
|
+
ignore
|
|
25
|
+
elsif node_type? target, :ivar and target.value == :@output_buffer
|
|
26
|
+
add_escaped_output arg
|
|
27
|
+
else
|
|
28
|
+
add_output arg
|
|
29
|
+
end
|
|
30
|
+
elsif method == :to_s
|
|
31
|
+
ignore
|
|
32
|
+
else
|
|
33
|
+
abort "Unrecognized action on buffer: #{method}"
|
|
34
|
+
end
|
|
35
|
+
elsif target == nil and method == :render
|
|
36
|
+
make_render_in_view exp
|
|
37
|
+
else
|
|
38
|
+
exp
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
#Process blocks, ignoring :ignore exps
|
|
43
|
+
def process_block exp
|
|
44
|
+
exp = exp.dup
|
|
45
|
+
exp.shift
|
|
46
|
+
exp.map! do |e|
|
|
47
|
+
res = process e
|
|
48
|
+
if res.empty? or res == ignore
|
|
49
|
+
nil
|
|
50
|
+
else
|
|
51
|
+
res
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
block = Sexp.new(:rlist).concat(exp).compact
|
|
55
|
+
block.line(exp.line)
|
|
56
|
+
block
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
#Look for assignments to output buffer that look like this:
|
|
60
|
+
# @output_buffer.append = some_output
|
|
61
|
+
# @output_buffer.safe_append = some_output
|
|
62
|
+
# @output_buffer.safe_expr_append = some_output
|
|
63
|
+
def process_attrasgn exp
|
|
64
|
+
if exp.target.node_type == :ivar and exp.target.value == :@output_buffer
|
|
65
|
+
if append_method?(exp.method)
|
|
66
|
+
exp.first_arg = process(exp.first_arg)
|
|
67
|
+
arg = normalize_output(exp.first_arg)
|
|
68
|
+
|
|
69
|
+
if arg.node_type == :str
|
|
70
|
+
ignore
|
|
71
|
+
elsif safe_append_method?(exp.method)
|
|
72
|
+
add_output arg
|
|
73
|
+
else
|
|
74
|
+
add_escaped_output arg
|
|
75
|
+
end
|
|
76
|
+
else
|
|
77
|
+
super
|
|
78
|
+
end
|
|
79
|
+
else
|
|
80
|
+
super
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
def append_method?(method)
|
|
86
|
+
method == :append= || safe_append_method?(method)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def safe_append_method?(method)
|
|
90
|
+
method == :safe_append= || method == :safe_expr_append=
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'railroader/processors/lib/basic_processor'
|
|
2
|
+
|
|
3
|
+
#Processes Gemfile and Gemfile.lock
|
|
4
|
+
class Railroader::GemProcessor < Railroader::BasicProcessor
|
|
5
|
+
|
|
6
|
+
def initialize *args
|
|
7
|
+
super
|
|
8
|
+
@gem_name_version = /^\s*([-_+.A-Za-z0-9]+) \((\w(\.\w+)*)\)/
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def process_gems gem_files
|
|
12
|
+
@gem_files = gem_files
|
|
13
|
+
@gemfile = gem_files[:gemfile][:file]
|
|
14
|
+
process gem_files[:gemfile][:src]
|
|
15
|
+
|
|
16
|
+
if gem_files[:gemlock]
|
|
17
|
+
process_gem_lock
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
@tracker.config.set_rails_version
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def process_call exp
|
|
24
|
+
if exp.target == nil
|
|
25
|
+
if exp.method == :gem
|
|
26
|
+
gem_name = exp.first_arg
|
|
27
|
+
return exp unless string? gem_name
|
|
28
|
+
|
|
29
|
+
gem_version = exp.second_arg
|
|
30
|
+
|
|
31
|
+
version = if string? gem_version
|
|
32
|
+
gem_version.value
|
|
33
|
+
else
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
@tracker.config.add_gem gem_name.value, version, @gemfile, exp.line
|
|
38
|
+
elsif exp.method == :ruby
|
|
39
|
+
version = exp.first_arg
|
|
40
|
+
if string? version
|
|
41
|
+
@tracker.config.set_ruby_version version.value
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
exp
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def process_gem_lock
|
|
50
|
+
line_num = 1
|
|
51
|
+
file = @gem_files[:gemlock][:file]
|
|
52
|
+
@gem_files[:gemlock][:src].each_line do |line|
|
|
53
|
+
set_gem_version_and_file line, file, line_num
|
|
54
|
+
line_num += 1
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Supports .rc2 but not ~>, >=, or <=
|
|
59
|
+
def set_gem_version_and_file line, file, line_num
|
|
60
|
+
if line =~ @gem_name_version
|
|
61
|
+
@tracker.config.add_gem $1, $2, file, line_num
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
require 'railroader/processors/template_processor'
|
|
2
|
+
|
|
3
|
+
#Processes HAML templates.
|
|
4
|
+
class Railroader::HamlTemplateProcessor < Railroader::TemplateProcessor
|
|
5
|
+
HAML_FORMAT_METHOD = /format_script_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)_(true|false)/
|
|
6
|
+
HAML_HELPERS = s(:colon2, s(:const, :Haml), :Helpers)
|
|
7
|
+
JAVASCRIPT_FILTER = s(:colon2, s(:colon2, s(:const, :Haml), :Filters), :Javascript)
|
|
8
|
+
COFFEE_FILTER = s(:colon2, s(:colon2, s(:const, :Haml), :Filters), :Coffee)
|
|
9
|
+
|
|
10
|
+
#Processes call, looking for template output
|
|
11
|
+
def process_call exp
|
|
12
|
+
target = exp.target
|
|
13
|
+
if sexp? target
|
|
14
|
+
target = process target
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
method = exp.method
|
|
18
|
+
|
|
19
|
+
if (call? target and target.method == :_hamlout)
|
|
20
|
+
res = case method
|
|
21
|
+
when :adjust_tabs, :rstrip!, :attributes #Check attributes, maybe?
|
|
22
|
+
ignore
|
|
23
|
+
when :options, :buffer
|
|
24
|
+
exp
|
|
25
|
+
when :open_tag
|
|
26
|
+
process_call_args exp
|
|
27
|
+
else
|
|
28
|
+
arg = exp.first_arg
|
|
29
|
+
|
|
30
|
+
if arg
|
|
31
|
+
@inside_concat = true
|
|
32
|
+
exp.first_arg = process(arg)
|
|
33
|
+
out = normalize_output(exp.first_arg)
|
|
34
|
+
@inside_concat = false
|
|
35
|
+
else
|
|
36
|
+
raise "Empty _hamlout.#{method}()?"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if string? out
|
|
40
|
+
ignore
|
|
41
|
+
else
|
|
42
|
+
r = case method.to_s
|
|
43
|
+
when "push_text"
|
|
44
|
+
build_output_from_push_text(out)
|
|
45
|
+
when HAML_FORMAT_METHOD
|
|
46
|
+
if $4 == "true"
|
|
47
|
+
if string_interp? out
|
|
48
|
+
build_output_from_push_text(out, :escaped_output)
|
|
49
|
+
else
|
|
50
|
+
Sexp.new :format_escaped, out
|
|
51
|
+
end
|
|
52
|
+
else
|
|
53
|
+
if string_interp? out
|
|
54
|
+
build_output_from_push_text(out)
|
|
55
|
+
else
|
|
56
|
+
Sexp.new :format, out
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
else
|
|
61
|
+
raise "Unrecognized action on _hamlout: #{method}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
@javascript = false
|
|
65
|
+
r
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
res.line(exp.line)
|
|
70
|
+
res
|
|
71
|
+
|
|
72
|
+
#_hamlout.buffer <<
|
|
73
|
+
#This seems to be used rarely, but directly appends args to output buffer.
|
|
74
|
+
#Has something to do with values of blocks?
|
|
75
|
+
elsif sexp? target and method == :<< and is_buffer_target? target
|
|
76
|
+
@inside_concat = true
|
|
77
|
+
exp.first_arg = process(exp.first_arg)
|
|
78
|
+
out = normalize_output(exp.first_arg)
|
|
79
|
+
@inside_concat = false
|
|
80
|
+
|
|
81
|
+
if out.node_type == :str #ignore plain strings
|
|
82
|
+
ignore
|
|
83
|
+
else
|
|
84
|
+
add_output out
|
|
85
|
+
end
|
|
86
|
+
elsif target == nil and method == :render
|
|
87
|
+
#Process call to render()
|
|
88
|
+
exp.arglist = process exp.arglist
|
|
89
|
+
make_render_in_view exp
|
|
90
|
+
elsif target == nil and method == :find_and_preserve and exp.first_arg
|
|
91
|
+
process exp.first_arg
|
|
92
|
+
elsif method == :render_with_options
|
|
93
|
+
if target == JAVASCRIPT_FILTER or target == COFFEE_FILTER
|
|
94
|
+
@javascript = true
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
process exp.first_arg
|
|
98
|
+
else
|
|
99
|
+
exp.target = target
|
|
100
|
+
exp.arglist = process exp.arglist
|
|
101
|
+
exp
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
#If inside an output stream, only return the final expression
|
|
106
|
+
def process_block exp
|
|
107
|
+
exp = exp.dup
|
|
108
|
+
exp.shift
|
|
109
|
+
if @inside_concat
|
|
110
|
+
@inside_concat = false
|
|
111
|
+
exp[0..-2].each do |e|
|
|
112
|
+
process e
|
|
113
|
+
end
|
|
114
|
+
@inside_concat = true
|
|
115
|
+
process exp[-1]
|
|
116
|
+
else
|
|
117
|
+
exp.map! do |e|
|
|
118
|
+
res = process e
|
|
119
|
+
if res.empty?
|
|
120
|
+
nil
|
|
121
|
+
else
|
|
122
|
+
res
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
Sexp.new(:rlist).concat(exp).compact
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
#Checks if the buffer is the target in a method call Sexp.
|
|
130
|
+
#TODO: Test this
|
|
131
|
+
def is_buffer_target? exp
|
|
132
|
+
exp.node_type == :call and
|
|
133
|
+
node_type? exp.target, :lvar and
|
|
134
|
+
exp.target.value == :_hamlout and
|
|
135
|
+
exp.method == :buffer
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
#HAML likes to put interpolated values into _hamlout.push_text
|
|
139
|
+
#but we want to handle those individually
|
|
140
|
+
def build_output_from_push_text exp, default = :output
|
|
141
|
+
if string_interp? exp
|
|
142
|
+
exp.map! do |e|
|
|
143
|
+
if sexp? e
|
|
144
|
+
if node_type? e, :evstr and e[1]
|
|
145
|
+
e = e.value
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
get_pushed_value e, default
|
|
149
|
+
else
|
|
150
|
+
e
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
#Gets outputs from values interpolated into _hamlout.push_text
|
|
157
|
+
def get_pushed_value exp, default = :output
|
|
158
|
+
return exp unless sexp? exp
|
|
159
|
+
|
|
160
|
+
case exp.node_type
|
|
161
|
+
when :format
|
|
162
|
+
exp.node_type = :output
|
|
163
|
+
@current_template.add_output exp
|
|
164
|
+
exp
|
|
165
|
+
when :format_escaped
|
|
166
|
+
exp.node_type = :escaped_output
|
|
167
|
+
@current_template.add_output exp
|
|
168
|
+
exp
|
|
169
|
+
when :str, :ignore, :output, :escaped_output
|
|
170
|
+
exp
|
|
171
|
+
when :block, :rlist, :dstr
|
|
172
|
+
exp.map! { |e| get_pushed_value e }
|
|
173
|
+
when :if
|
|
174
|
+
clauses = [get_pushed_value(exp.then_clause), get_pushed_value(exp.else_clause)].compact
|
|
175
|
+
|
|
176
|
+
if clauses.length > 1
|
|
177
|
+
s(:or, *clauses)
|
|
178
|
+
else
|
|
179
|
+
clauses.first
|
|
180
|
+
end
|
|
181
|
+
else
|
|
182
|
+
if call? exp and exp.target == HAML_HELPERS and exp.method == :html_escape
|
|
183
|
+
add_escaped_output exp.first_arg
|
|
184
|
+
elsif @javascript and call? exp and (exp.method == :j or exp.method == :escape_javascript)
|
|
185
|
+
add_escaped_output exp.first_arg
|
|
186
|
+
else
|
|
187
|
+
add_output exp, default
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'railroader/processors/lib/processor_helper'
|
|
2
|
+
require 'railroader/processors/lib/safe_call_helper'
|
|
3
|
+
require 'railroader/util'
|
|
4
|
+
|
|
5
|
+
class Railroader::BasicProcessor < Railroader::SexpProcessor
|
|
6
|
+
include Railroader::ProcessorHelper
|
|
7
|
+
include Railroader::SafeCallHelper
|
|
8
|
+
include Railroader::Util
|
|
9
|
+
|
|
10
|
+
def initialize tracker
|
|
11
|
+
super()
|
|
12
|
+
@tracker = tracker
|
|
13
|
+
@current_template = @current_module = @current_class = @current_method = nil
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def process_default exp
|
|
17
|
+
process_all exp
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def process_if exp
|
|
21
|
+
condition = exp.condition
|
|
22
|
+
|
|
23
|
+
process condition
|
|
24
|
+
|
|
25
|
+
if true? condition
|
|
26
|
+
process exp.then_clause
|
|
27
|
+
elsif false? condition
|
|
28
|
+
process exp.else_clause
|
|
29
|
+
else
|
|
30
|
+
[exp.then_clause, exp.else_clause].compact.map do |e|
|
|
31
|
+
process e
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
exp
|
|
36
|
+
end
|
|
37
|
+
end
|