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,111 @@
|
|
|
1
|
+
module Railroader::ModuleHelper
|
|
2
|
+
def handle_module exp, tracker_class, parent = nil
|
|
3
|
+
name = class_name(exp.module_name)
|
|
4
|
+
|
|
5
|
+
if @current_module
|
|
6
|
+
outer_module = @current_module
|
|
7
|
+
name = (outer_module.name.to_s + "::" + name.to_s).to_sym
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
if @current_class
|
|
11
|
+
name = (@current_class.name.to_s + "::" + name.to_s).to_sym
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
if @tracker.libs[name]
|
|
15
|
+
@current_module = @tracker.libs[name]
|
|
16
|
+
@current_module.add_file @file_name, exp
|
|
17
|
+
else
|
|
18
|
+
@current_module = tracker_class.new name, parent, @file_name, exp, @tracker
|
|
19
|
+
@tracker.libs[name] = @current_module
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
exp.body = process_all! exp.body
|
|
23
|
+
|
|
24
|
+
if outer_module
|
|
25
|
+
@current_module = outer_module
|
|
26
|
+
else
|
|
27
|
+
@current_module = nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
exp
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def handle_class exp, collection, tracker_class
|
|
34
|
+
name = class_name(exp.class_name)
|
|
35
|
+
parent = class_name exp.parent_name
|
|
36
|
+
|
|
37
|
+
if @current_class
|
|
38
|
+
outer_class = @current_class
|
|
39
|
+
name = (outer_class.name.to_s + "::" + name.to_s).to_sym
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if @current_module
|
|
43
|
+
name = (@current_module.name.to_s + "::" + name.to_s).to_sym
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if collection[name]
|
|
47
|
+
@current_class = collection[name]
|
|
48
|
+
@current_class.add_file @file_name, exp
|
|
49
|
+
else
|
|
50
|
+
@current_class = tracker_class.new name, parent, @file_name, exp, @tracker
|
|
51
|
+
collection[name] = @current_class
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
exp.body = process_all! exp.body
|
|
55
|
+
|
|
56
|
+
yield if block_given?
|
|
57
|
+
|
|
58
|
+
if outer_class
|
|
59
|
+
@current_class = outer_class
|
|
60
|
+
else
|
|
61
|
+
@current_class = nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
exp
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def process_defs exp
|
|
68
|
+
name = exp.method_name
|
|
69
|
+
|
|
70
|
+
if node_type? exp[1], :self
|
|
71
|
+
if @current_class
|
|
72
|
+
target = @current_class.name
|
|
73
|
+
elsif @current_module
|
|
74
|
+
target = @current_module.name
|
|
75
|
+
else
|
|
76
|
+
target = nil
|
|
77
|
+
end
|
|
78
|
+
else
|
|
79
|
+
target = class_name exp[1]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
@current_method = name
|
|
83
|
+
res = Sexp.new :defs, target, name, exp.formal_args, *process_all!(exp.body)
|
|
84
|
+
res.line(exp.line)
|
|
85
|
+
@current_method = nil
|
|
86
|
+
|
|
87
|
+
if @current_class
|
|
88
|
+
@current_class.add_method @visibility, name, res, @file_name
|
|
89
|
+
elsif @current_module
|
|
90
|
+
@current_module.add_method @visibility, name, res, @file_name
|
|
91
|
+
end
|
|
92
|
+
res
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def process_defn exp
|
|
96
|
+
name = exp.method_name
|
|
97
|
+
|
|
98
|
+
@current_method = name
|
|
99
|
+
res = Sexp.new :defn, name, exp.formal_args, *process_all!(exp.body)
|
|
100
|
+
res.line(exp.line)
|
|
101
|
+
@current_method = nil
|
|
102
|
+
|
|
103
|
+
if @current_class
|
|
104
|
+
@current_class.add_method @visibility, name, res, @file_name
|
|
105
|
+
elsif @current_module
|
|
106
|
+
@current_module.add_method @visibility, name, res, @file_name
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
res
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#Contains a couple shared methods for Processors.
|
|
2
|
+
module Railroader::ProcessorHelper
|
|
3
|
+
def process_all exp
|
|
4
|
+
exp.each_sexp do |e|
|
|
5
|
+
process e
|
|
6
|
+
end
|
|
7
|
+
exp
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def process_all! exp
|
|
11
|
+
exp.map! do |e|
|
|
12
|
+
if sexp? e
|
|
13
|
+
process e
|
|
14
|
+
else
|
|
15
|
+
e
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
exp
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#Process the arguments of a method call. Does not store results.
|
|
23
|
+
#
|
|
24
|
+
#This method is used because Sexp#args and Sexp#arglist create new objects.
|
|
25
|
+
def process_call_args exp
|
|
26
|
+
exp.each_arg do |a|
|
|
27
|
+
process a if sexp? a
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
exp
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def process_class exp
|
|
34
|
+
current_class = @current_class
|
|
35
|
+
@current_class = class_name exp[1]
|
|
36
|
+
process_all exp.body
|
|
37
|
+
@current_class = current_class
|
|
38
|
+
exp
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
#Sets the current module.
|
|
42
|
+
def process_module exp
|
|
43
|
+
module_name = class_name(exp.class_name).to_s
|
|
44
|
+
prev_module = @current_module
|
|
45
|
+
|
|
46
|
+
if prev_module
|
|
47
|
+
@current_module = "#{prev_module}::#{module_name}"
|
|
48
|
+
else
|
|
49
|
+
@current_module = module_name
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
if block_given?
|
|
53
|
+
yield
|
|
54
|
+
else
|
|
55
|
+
process_all exp.body
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
@current_module = prev_module
|
|
59
|
+
|
|
60
|
+
exp
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# e.g. private defn
|
|
64
|
+
def process_call_defn? exp
|
|
65
|
+
if call? exp and exp.target.nil? and node_type? exp.first_arg, :defn, :defs and [:private, :public, :protected].include? exp.method
|
|
66
|
+
prev_visibility = @visibility
|
|
67
|
+
@visibility = exp.method
|
|
68
|
+
process exp.first_arg
|
|
69
|
+
@visibility = prev_visibility
|
|
70
|
+
exp
|
|
71
|
+
else
|
|
72
|
+
false
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def current_file_name
|
|
77
|
+
case
|
|
78
|
+
when @file_name
|
|
79
|
+
@file_name
|
|
80
|
+
when @current_class.is_a?(Railroader::Collection)
|
|
81
|
+
@current_class.file
|
|
82
|
+
when @current_module.is_a?(Railroader::Collection)
|
|
83
|
+
@current_module.file
|
|
84
|
+
else
|
|
85
|
+
nil
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
require 'railroader/processors/lib/basic_processor'
|
|
2
|
+
|
|
3
|
+
#Processes configuration. Results are put in tracker.config.
|
|
4
|
+
#
|
|
5
|
+
#Configuration of Rails via Rails::Initializer are stored in tracker.config.rails.
|
|
6
|
+
#For example:
|
|
7
|
+
#
|
|
8
|
+
# Rails::Initializer.run |config|
|
|
9
|
+
# config.action_controller.session_store = :cookie_store
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
#will be stored in
|
|
13
|
+
#
|
|
14
|
+
# tracker.config[:rails][:action_controller][:session_store]
|
|
15
|
+
#
|
|
16
|
+
#Values for tracker.config.rails will still be Sexps.
|
|
17
|
+
class Railroader::Rails2ConfigProcessor < Railroader::BasicProcessor
|
|
18
|
+
#Replace block variable in
|
|
19
|
+
#
|
|
20
|
+
# Rails::Initializer.run |config|
|
|
21
|
+
#
|
|
22
|
+
#with this value so we can keep track of it.
|
|
23
|
+
RAILS_CONFIG = Sexp.new(:const, :"!BRAKEMAN_RAILS_CONFIG")
|
|
24
|
+
|
|
25
|
+
def initialize *args
|
|
26
|
+
super
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
#Use this method to process configuration file
|
|
30
|
+
def process_config src, file_name
|
|
31
|
+
@file_name = file_name
|
|
32
|
+
res = Railroader::ConfigAliasProcessor.new.process_safely(src, nil, file_name)
|
|
33
|
+
process res
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
#Check if config is set to use Erubis
|
|
37
|
+
def process_call exp
|
|
38
|
+
target = exp.target
|
|
39
|
+
target = process target if sexp? target
|
|
40
|
+
|
|
41
|
+
if exp.method == :gem and exp.first_arg.value == "erubis"
|
|
42
|
+
Railroader.notify "[Notice] Using Erubis for ERB templates"
|
|
43
|
+
@tracker.config.erubis = true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
exp
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#Look for configuration settings
|
|
50
|
+
def process_attrasgn exp
|
|
51
|
+
if exp.target == RAILS_CONFIG
|
|
52
|
+
#Get rid of '=' at end
|
|
53
|
+
attribute = exp.method.to_s[0..-2].to_sym
|
|
54
|
+
if exp.args.length > 1
|
|
55
|
+
#Multiple arguments?...not sure if this will ever happen
|
|
56
|
+
@tracker.config.rails[attribute] = exp.args
|
|
57
|
+
else
|
|
58
|
+
@tracker.config.rails[attribute] = exp.first_arg
|
|
59
|
+
end
|
|
60
|
+
elsif include_rails_config? exp
|
|
61
|
+
options = get_rails_config exp
|
|
62
|
+
level = @tracker.config.rails
|
|
63
|
+
options[0..-2].each do |o|
|
|
64
|
+
level[o] ||= {}
|
|
65
|
+
level = level[o]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
level[options.last] = exp.first_arg
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
exp
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
#Check for Rails version
|
|
75
|
+
def process_cdecl exp
|
|
76
|
+
#Set Rails version required
|
|
77
|
+
if exp.lhs == :RAILS_GEM_VERSION
|
|
78
|
+
@tracker.config.rails_version = exp.rhs.value
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
exp
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
#Check if an expression includes a call to set Rails config
|
|
85
|
+
def include_rails_config? exp
|
|
86
|
+
target = exp.target
|
|
87
|
+
if call? target
|
|
88
|
+
if target.target == RAILS_CONFIG
|
|
89
|
+
true
|
|
90
|
+
else
|
|
91
|
+
include_rails_config? target
|
|
92
|
+
end
|
|
93
|
+
elsif target == RAILS_CONFIG
|
|
94
|
+
true
|
|
95
|
+
else
|
|
96
|
+
false
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
#Returns an array of symbols for each 'level' in the config
|
|
101
|
+
#
|
|
102
|
+
# config.action_controller.session_store = :cookie
|
|
103
|
+
#
|
|
104
|
+
#becomes
|
|
105
|
+
#
|
|
106
|
+
# [:action_controller, :session_store]
|
|
107
|
+
def get_rails_config exp
|
|
108
|
+
if node_type? exp, :attrasgn
|
|
109
|
+
attribute = exp.method.to_s[0..-2].to_sym
|
|
110
|
+
get_rails_config(exp.target) << attribute
|
|
111
|
+
elsif call? exp
|
|
112
|
+
if exp.target == RAILS_CONFIG
|
|
113
|
+
[exp.method]
|
|
114
|
+
else
|
|
115
|
+
get_rails_config(exp.target) << exp.method
|
|
116
|
+
end
|
|
117
|
+
else
|
|
118
|
+
raise "WHAT"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
#This is necessary to replace block variable so we can track config settings
|
|
124
|
+
class Railroader::ConfigAliasProcessor < Railroader::AliasProcessor
|
|
125
|
+
|
|
126
|
+
RAILS_INIT = Sexp.new(:colon2, Sexp.new(:const, :Rails), :Initializer)
|
|
127
|
+
|
|
128
|
+
#Look for a call to
|
|
129
|
+
#
|
|
130
|
+
# Rails::Initializer.run do |config|
|
|
131
|
+
# ...
|
|
132
|
+
# end
|
|
133
|
+
#
|
|
134
|
+
#and replace config with RAILS_CONFIG
|
|
135
|
+
def process_iter exp
|
|
136
|
+
target = exp.block_call.target
|
|
137
|
+
method = exp.block_call.method
|
|
138
|
+
|
|
139
|
+
if sexp? target and target == RAILS_INIT and method == :run
|
|
140
|
+
env[Sexp.new(:lvar, exp.block_args.value)] = Railroader::Rails2ConfigProcessor::RAILS_CONFIG
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
process_default exp
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
require 'railroader/processors/lib/basic_processor'
|
|
2
|
+
|
|
3
|
+
#Processes the Sexp from routes.rb. Stores results in tracker.routes.
|
|
4
|
+
#
|
|
5
|
+
#Note that it is only interested in determining what methods on which
|
|
6
|
+
#controllers are used as routes, not the generated URLs for routes.
|
|
7
|
+
class Railroader::Rails2RoutesProcessor < Railroader::BasicProcessor
|
|
8
|
+
include Railroader::RouteHelper
|
|
9
|
+
|
|
10
|
+
attr_reader :map, :nested, :current_controller
|
|
11
|
+
|
|
12
|
+
def initialize tracker
|
|
13
|
+
super
|
|
14
|
+
@map = Sexp.new(:lvar, :map)
|
|
15
|
+
@nested = nil #used for identifying nested targets
|
|
16
|
+
@prefix = [] #Controller name prefix (a module name, usually)
|
|
17
|
+
@current_controller = nil
|
|
18
|
+
@with_options = nil #For use inside map.with_options
|
|
19
|
+
@file_name = "config/routes.rb"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#Call this with parsed route file information.
|
|
23
|
+
#
|
|
24
|
+
#This method first calls RouteAliasProcessor#process_safely on the +exp+,
|
|
25
|
+
#so it does not modify the +exp+.
|
|
26
|
+
def process_routes exp
|
|
27
|
+
process Railroader::RouteAliasProcessor.new.process_safely(exp, nil, @file_name)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
#Looking for mapping of routes
|
|
31
|
+
def process_call exp
|
|
32
|
+
target = exp.target
|
|
33
|
+
|
|
34
|
+
if target == map or (not target.nil? and target == nested)
|
|
35
|
+
process_map exp
|
|
36
|
+
else
|
|
37
|
+
process_default exp
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
exp
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
#Process a map.something call
|
|
44
|
+
#based on the method used
|
|
45
|
+
def process_map exp
|
|
46
|
+
args = exp.args
|
|
47
|
+
|
|
48
|
+
case exp.method
|
|
49
|
+
when :resource
|
|
50
|
+
process_resource args
|
|
51
|
+
when :resources
|
|
52
|
+
process_resources args
|
|
53
|
+
when :connect, :root
|
|
54
|
+
process_connect args
|
|
55
|
+
else
|
|
56
|
+
process_named_route args
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
exp
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
#Look for map calls that take a block.
|
|
63
|
+
#Otherwise, just do the default processing.
|
|
64
|
+
def process_iter exp
|
|
65
|
+
target = exp.block_call.target
|
|
66
|
+
|
|
67
|
+
if target == map or target == nested
|
|
68
|
+
method = exp.block_call.method
|
|
69
|
+
case method
|
|
70
|
+
when :namespace
|
|
71
|
+
process_namespace exp
|
|
72
|
+
when :resources, :resource
|
|
73
|
+
process_resources exp.block_call.args
|
|
74
|
+
process_default exp.block if exp.block
|
|
75
|
+
when :with_options
|
|
76
|
+
process_with_options exp
|
|
77
|
+
end
|
|
78
|
+
exp
|
|
79
|
+
else
|
|
80
|
+
process_default exp
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
#Process
|
|
85
|
+
# map.resources :x, :controller => :y, :member => ...
|
|
86
|
+
#etc.
|
|
87
|
+
def process_resources exp
|
|
88
|
+
controller = check_for_controller_name exp
|
|
89
|
+
if controller
|
|
90
|
+
self.current_controller = controller
|
|
91
|
+
process_resource_options exp[-1]
|
|
92
|
+
else
|
|
93
|
+
exp.each do |argument|
|
|
94
|
+
if node_type? argument, :lit
|
|
95
|
+
self.current_controller = exp.first.value
|
|
96
|
+
add_resources_routes
|
|
97
|
+
process_resource_options exp.last
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
#Process all the options that might be in the hash passed to
|
|
104
|
+
#map.resource, et al.
|
|
105
|
+
def process_resource_options exp
|
|
106
|
+
if exp.nil? and @with_options
|
|
107
|
+
exp = @with_options
|
|
108
|
+
elsif @with_options
|
|
109
|
+
exp = exp.concat @with_options[1..-1]
|
|
110
|
+
end
|
|
111
|
+
return unless exp.node_type == :hash
|
|
112
|
+
|
|
113
|
+
hash_iterate(exp) do |option, value|
|
|
114
|
+
case option[1]
|
|
115
|
+
when :controller, :requirements, :singular, :path_prefix, :as,
|
|
116
|
+
:path_names, :shallow, :name_prefix, :member_path, :nested_member_path,
|
|
117
|
+
:belongs_to, :conditions, :active_scaffold
|
|
118
|
+
#should be able to skip
|
|
119
|
+
when :collection, :member, :new
|
|
120
|
+
process_collection value
|
|
121
|
+
when :has_one
|
|
122
|
+
save_controller = current_controller
|
|
123
|
+
process_resource value[1..-1] #Verify this is proper behavior
|
|
124
|
+
self.current_controller = save_controller
|
|
125
|
+
when :has_many
|
|
126
|
+
save_controller = current_controller
|
|
127
|
+
process_resources value[1..-1]
|
|
128
|
+
self.current_controller = save_controller
|
|
129
|
+
when :only
|
|
130
|
+
process_option_only value
|
|
131
|
+
when :except
|
|
132
|
+
process_option_except value
|
|
133
|
+
else
|
|
134
|
+
Railroader.notify "[Notice] Unhandled resource option, please report: #{option}"
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
#Process route option :only => ...
|
|
140
|
+
def process_option_only exp
|
|
141
|
+
routes = @tracker.routes[@current_controller]
|
|
142
|
+
[:index, :new, :create, :show, :edit, :update, :destroy].each do |r|
|
|
143
|
+
routes.delete r
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
if exp.node_type == :array
|
|
147
|
+
exp[1..-1].each do |e|
|
|
148
|
+
routes << e.value
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
#Process route option :except => ...
|
|
154
|
+
def process_option_except exp
|
|
155
|
+
return unless exp.node_type == :array
|
|
156
|
+
routes = @tracker.routes[@current_controller]
|
|
157
|
+
|
|
158
|
+
exp[1..-1].each do |e|
|
|
159
|
+
routes.delete e.value
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# map.resource :x, ..
|
|
164
|
+
def process_resource exp
|
|
165
|
+
controller = check_for_controller_name exp
|
|
166
|
+
if controller
|
|
167
|
+
self.current_controller = controller
|
|
168
|
+
process_resource_options exp.last
|
|
169
|
+
else
|
|
170
|
+
exp.each do |argument|
|
|
171
|
+
if node_type? argument, :lit
|
|
172
|
+
self.current_controller = pluralize(exp.first.value.to_s)
|
|
173
|
+
add_resource_routes
|
|
174
|
+
process_resource_options exp.last
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
#Process
|
|
181
|
+
# map.connect '/something', :controller => 'blah', :action => 'whatever'
|
|
182
|
+
def process_connect exp
|
|
183
|
+
return if exp.empty?
|
|
184
|
+
|
|
185
|
+
controller = check_for_controller_name exp
|
|
186
|
+
self.current_controller = controller if controller
|
|
187
|
+
|
|
188
|
+
#Check for default route
|
|
189
|
+
if string? exp.first
|
|
190
|
+
if exp.first.value == ":controller/:action/:id"
|
|
191
|
+
@tracker.routes[:allow_all_actions] = exp.first
|
|
192
|
+
elsif exp.first.value.include? ":action"
|
|
193
|
+
@tracker.routes[@current_controller] = [:allow_all_actions, exp.line]
|
|
194
|
+
return
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
#This -seems- redundant, but people might connect actions
|
|
199
|
+
#to a controller which already allows them all
|
|
200
|
+
return if @tracker.routes[@current_controller].is_a? Array and @tracker.routes[@current_controller][0] == :allow_all_actions
|
|
201
|
+
|
|
202
|
+
exp.last.each_with_index do |e,i|
|
|
203
|
+
if symbol? e and e.value == :action
|
|
204
|
+
action = exp.last[i + 1]
|
|
205
|
+
|
|
206
|
+
if node_type? action, :lit
|
|
207
|
+
@tracker.routes[@current_controller] << action.value.to_sym
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
return
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# map.with_options :controller => 'something' do |something|
|
|
216
|
+
# something.resources :blah
|
|
217
|
+
# end
|
|
218
|
+
def process_with_options exp
|
|
219
|
+
@with_options = exp.block_call.last_arg
|
|
220
|
+
@nested = Sexp.new(:lvar, exp.block_args.value)
|
|
221
|
+
|
|
222
|
+
self.current_controller = check_for_controller_name exp.block_call.args
|
|
223
|
+
|
|
224
|
+
#process block
|
|
225
|
+
process exp.block
|
|
226
|
+
|
|
227
|
+
@with_options = nil
|
|
228
|
+
@nested = nil
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# map.namespace :something do |something|
|
|
232
|
+
# something.resources :blah
|
|
233
|
+
# end
|
|
234
|
+
def process_namespace exp
|
|
235
|
+
call = exp.block_call
|
|
236
|
+
formal_args = exp.block_args
|
|
237
|
+
block = exp.block
|
|
238
|
+
|
|
239
|
+
@prefix << camelize(call.first_arg.value)
|
|
240
|
+
|
|
241
|
+
if formal_args
|
|
242
|
+
@nested = Sexp.new(:lvar, formal_args.value)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
process block
|
|
246
|
+
|
|
247
|
+
@prefix.pop
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# map.something_abnormal '/blah', :controller => 'something', :action => 'wohoo'
|
|
251
|
+
def process_named_route exp
|
|
252
|
+
process_connect exp
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
#Process collection option
|
|
256
|
+
# :collection => { :some_action => :http_actions }
|
|
257
|
+
def process_collection exp
|
|
258
|
+
return unless exp.node_type == :hash
|
|
259
|
+
routes = @tracker.routes[@current_controller]
|
|
260
|
+
|
|
261
|
+
hash_iterate(exp) do |action, _type|
|
|
262
|
+
routes << action.value
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
private
|
|
267
|
+
|
|
268
|
+
#Checks an argument list for a hash that has a key :controller.
|
|
269
|
+
#If it does, returns the value.
|
|
270
|
+
#
|
|
271
|
+
#Otherwise, returns nil.
|
|
272
|
+
def check_for_controller_name args
|
|
273
|
+
args.each do |a|
|
|
274
|
+
if hash? a and value = hash_access(a, :controller)
|
|
275
|
+
return value.value if string? value or symbol? value
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
nil
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
#This is for a really specific case where a hash is used as arguments
|
|
284
|
+
#to one of the map methods.
|
|
285
|
+
class Railroader::RouteAliasProcessor < Railroader::AliasProcessor
|
|
286
|
+
|
|
287
|
+
#This replaces
|
|
288
|
+
# { :some => :hash }.keys
|
|
289
|
+
#with
|
|
290
|
+
# [:some]
|
|
291
|
+
def process_call exp
|
|
292
|
+
process_default exp
|
|
293
|
+
|
|
294
|
+
if hash? exp.target and exp.method == :keys
|
|
295
|
+
keys = get_keys exp.target
|
|
296
|
+
exp.clear
|
|
297
|
+
keys.each_with_index do |e,i|
|
|
298
|
+
exp[i] = e
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
exp
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
#Returns an array Sexp containing the keys from the hash
|
|
305
|
+
def get_keys hash
|
|
306
|
+
keys = Sexp.new(:array)
|
|
307
|
+
hash_iterate(hash) do |key, _value|
|
|
308
|
+
keys << key
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
keys
|
|
312
|
+
end
|
|
313
|
+
end
|