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,132 @@
|
|
|
1
|
+
|
|
2
|
+
require 'railroader/processors/lib/basic_processor'
|
|
3
|
+
|
|
4
|
+
#Processes configuration. Results are put in tracker.config.
|
|
5
|
+
#
|
|
6
|
+
#Configuration of Rails via Rails::Initializer are stored in tracker.config.rails.
|
|
7
|
+
#For example:
|
|
8
|
+
#
|
|
9
|
+
# MyApp::Application.configure do
|
|
10
|
+
# config.active_record.whitelist_attributes = true
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
#will be stored in
|
|
14
|
+
#
|
|
15
|
+
# tracker.config.rails[:active_record][:whitelist_attributes]
|
|
16
|
+
#
|
|
17
|
+
#Values for tracker.config.rails will still be Sexps.
|
|
18
|
+
class Railroader::Rails3ConfigProcessor < Railroader::BasicProcessor
|
|
19
|
+
RAILS_CONFIG = Sexp.new(:call, nil, :config)
|
|
20
|
+
|
|
21
|
+
def initialize *args
|
|
22
|
+
super
|
|
23
|
+
@inside_config = false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
#Use this method to process configuration file
|
|
27
|
+
def process_config src, file_name
|
|
28
|
+
@file_name = file_name
|
|
29
|
+
res = Railroader::AliasProcessor.new(@tracker).process_safely(src, nil, @file_name)
|
|
30
|
+
process res
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
#Look for MyApp::Application.configure do ... end
|
|
34
|
+
def process_iter exp
|
|
35
|
+
call = exp.block_call
|
|
36
|
+
|
|
37
|
+
if node_type?(call.target, :colon2) and
|
|
38
|
+
call.target.rhs == :Application and
|
|
39
|
+
call.method == :configure
|
|
40
|
+
|
|
41
|
+
@inside_config = true
|
|
42
|
+
process exp.block if sexp? exp.block
|
|
43
|
+
@inside_config = false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
exp
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#Look for class Application < Rails::Application
|
|
50
|
+
def process_class exp
|
|
51
|
+
if exp.class_name == :Application
|
|
52
|
+
@inside_config = true
|
|
53
|
+
process_all exp.body if sexp? exp.body
|
|
54
|
+
@inside_config = false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
exp
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
#Look for configuration settings
|
|
61
|
+
def process_attrasgn exp
|
|
62
|
+
return exp unless @inside_config
|
|
63
|
+
|
|
64
|
+
if exp.target == RAILS_CONFIG
|
|
65
|
+
#Get rid of '=' at end
|
|
66
|
+
attribute = exp.method.to_s[0..-2].to_sym
|
|
67
|
+
if exp.args.length > 1
|
|
68
|
+
#Multiple arguments?...not sure if this will ever happen
|
|
69
|
+
@tracker.config.rails[attribute] = exp.args
|
|
70
|
+
else
|
|
71
|
+
@tracker.config.rails[attribute] = exp.first_arg
|
|
72
|
+
end
|
|
73
|
+
elsif include_rails_config? exp
|
|
74
|
+
options = get_rails_config exp
|
|
75
|
+
level = @tracker.config.rails
|
|
76
|
+
options[0..-2].each do |o|
|
|
77
|
+
level[o] ||= {}
|
|
78
|
+
|
|
79
|
+
option = level[o]
|
|
80
|
+
|
|
81
|
+
if not option.is_a? Hash
|
|
82
|
+
Railroader.debug "[Notice] Skipping config setting: #{options.map(&:to_s).join(".")}"
|
|
83
|
+
return exp
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
level = level[o]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
level[options.last] = exp.first_arg
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
exp
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
#Check if an expression includes a call to set Rails config
|
|
96
|
+
def include_rails_config? exp
|
|
97
|
+
target = exp.target
|
|
98
|
+
if call? target
|
|
99
|
+
if target.target == RAILS_CONFIG
|
|
100
|
+
true
|
|
101
|
+
else
|
|
102
|
+
include_rails_config? target
|
|
103
|
+
end
|
|
104
|
+
elsif target == RAILS_CONFIG
|
|
105
|
+
true
|
|
106
|
+
else
|
|
107
|
+
false
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
#Returns an array of symbols for each 'level' in the config
|
|
112
|
+
#
|
|
113
|
+
# config.action_controller.session_store = :cookie
|
|
114
|
+
#
|
|
115
|
+
#becomes
|
|
116
|
+
#
|
|
117
|
+
# [:action_controller, :session_store]
|
|
118
|
+
def get_rails_config exp
|
|
119
|
+
if node_type? exp, :attrasgn
|
|
120
|
+
attribute = exp.method.to_s[0..-2].to_sym
|
|
121
|
+
get_rails_config(exp.target) << attribute
|
|
122
|
+
elsif call? exp
|
|
123
|
+
if exp.target == RAILS_CONFIG
|
|
124
|
+
[exp.method]
|
|
125
|
+
else
|
|
126
|
+
get_rails_config(exp.target) << exp.method
|
|
127
|
+
end
|
|
128
|
+
else
|
|
129
|
+
raise "WHAT"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,308 @@
|
|
|
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::Rails3RoutesProcessor < 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
|
+
@controller_block = false
|
|
20
|
+
@file_name = "config/routes.rb"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def process_routes exp
|
|
24
|
+
process Railroader::AliasProcessor.new.process_safely(exp, nil, @file_name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def process_call exp
|
|
28
|
+
case exp.method
|
|
29
|
+
when :resources
|
|
30
|
+
process_resources exp
|
|
31
|
+
when :resource
|
|
32
|
+
process_resource exp
|
|
33
|
+
when :root
|
|
34
|
+
process_root exp
|
|
35
|
+
when :member
|
|
36
|
+
process_default exp
|
|
37
|
+
when :get, :put, :post, :delete
|
|
38
|
+
process_verb exp
|
|
39
|
+
when :match
|
|
40
|
+
process_match exp
|
|
41
|
+
else
|
|
42
|
+
exp
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def process_iter exp
|
|
47
|
+
case exp.block_call.method
|
|
48
|
+
when :namespace
|
|
49
|
+
process_namespace exp
|
|
50
|
+
when :resource
|
|
51
|
+
process_resource_block exp
|
|
52
|
+
when :resources
|
|
53
|
+
process_resources_block exp
|
|
54
|
+
when :scope
|
|
55
|
+
process_scope_block exp
|
|
56
|
+
when :controller
|
|
57
|
+
process_controller_block exp
|
|
58
|
+
else
|
|
59
|
+
process_default exp
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def process_namespace exp
|
|
64
|
+
arg = exp.block_call.first_arg
|
|
65
|
+
return exp unless symbol? arg or string? arg
|
|
66
|
+
|
|
67
|
+
name = arg.value
|
|
68
|
+
block = exp.block
|
|
69
|
+
|
|
70
|
+
@prefix << camelize(name)
|
|
71
|
+
|
|
72
|
+
process block
|
|
73
|
+
|
|
74
|
+
@prefix.pop
|
|
75
|
+
|
|
76
|
+
exp
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
#TODO: Need test for this
|
|
80
|
+
def process_root exp
|
|
81
|
+
if value = hash_access(exp.first_arg, :to)
|
|
82
|
+
if string? value
|
|
83
|
+
add_route_from_string value
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
exp
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def process_match exp
|
|
91
|
+
first_arg = exp.first_arg
|
|
92
|
+
second_arg = exp.second_arg
|
|
93
|
+
last_arg = exp.last_arg
|
|
94
|
+
|
|
95
|
+
if string? first_arg
|
|
96
|
+
|
|
97
|
+
matcher = first_arg.value
|
|
98
|
+
if matcher == ':controller(/:action(/:id(.:format)))' or
|
|
99
|
+
matcher.include? ':controller' and action_route?(matcher) #Default routes
|
|
100
|
+
@tracker.routes[:allow_all_actions] = first_arg
|
|
101
|
+
return exp
|
|
102
|
+
elsif action_route?(first_arg)
|
|
103
|
+
if hash? second_arg and controller_name = hash_access(second_arg, :controller)
|
|
104
|
+
loose_action(controller_name, "matched") #TODO: Parse verbs
|
|
105
|
+
end
|
|
106
|
+
elsif second_arg.nil? and in_controller_block? and not matcher.include? ":"
|
|
107
|
+
add_route matcher
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
if hash? last_arg
|
|
112
|
+
hash_iterate last_arg do |k, v|
|
|
113
|
+
if string? k
|
|
114
|
+
if string? v
|
|
115
|
+
add_route_from_string v
|
|
116
|
+
elsif in_controller_block? and symbol? v
|
|
117
|
+
add_route v
|
|
118
|
+
end
|
|
119
|
+
elsif symbol? k
|
|
120
|
+
case k.value
|
|
121
|
+
when :action
|
|
122
|
+
if string? v
|
|
123
|
+
add_route_from_string v
|
|
124
|
+
else
|
|
125
|
+
add_route v
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
when :to
|
|
129
|
+
if string? v
|
|
130
|
+
add_route_from_string v[1]
|
|
131
|
+
elsif in_controller_block? and symbol? v
|
|
132
|
+
add_route v
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
@current_controller = nil unless in_controller_block?
|
|
140
|
+
exp
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def add_route_from_string value
|
|
144
|
+
value = value[1] if string? value
|
|
145
|
+
|
|
146
|
+
controller, action = extract_action value
|
|
147
|
+
|
|
148
|
+
if action
|
|
149
|
+
add_route action, controller
|
|
150
|
+
elsif in_controller_block?
|
|
151
|
+
add_route value
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def process_verb exp
|
|
156
|
+
first_arg = exp.first_arg
|
|
157
|
+
second_arg = exp.second_arg
|
|
158
|
+
|
|
159
|
+
if symbol? first_arg and not hash? second_arg
|
|
160
|
+
add_route first_arg
|
|
161
|
+
elsif hash? second_arg
|
|
162
|
+
hash_iterate second_arg do |k, v|
|
|
163
|
+
if symbol? k and k.value == :to
|
|
164
|
+
if string? v
|
|
165
|
+
add_route_from_string v
|
|
166
|
+
elsif in_controller_block? and symbol? v
|
|
167
|
+
add_route v
|
|
168
|
+
end
|
|
169
|
+
elsif action_route?(first_arg)
|
|
170
|
+
if hash? second_arg and controller_name = hash_access(second_arg, :controller)
|
|
171
|
+
loose_action(controller_name, exp.method)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
elsif string? first_arg
|
|
176
|
+
if first_arg.value.include? ':controller' and action_route?(first_arg) #Default routes
|
|
177
|
+
@tracker.routes[:allow_all_actions] = first_arg
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
route = first_arg.value.split "/"
|
|
181
|
+
if route.length != 2
|
|
182
|
+
add_route route[0]
|
|
183
|
+
else
|
|
184
|
+
add_route route[1], route[0]
|
|
185
|
+
end
|
|
186
|
+
elsif in_controller_block? and symbol? first_arg
|
|
187
|
+
add_route first_arg
|
|
188
|
+
else hash? first_arg
|
|
189
|
+
hash_iterate first_arg do |k, v|
|
|
190
|
+
if string? k
|
|
191
|
+
if string? v
|
|
192
|
+
add_route_from_string v
|
|
193
|
+
elsif in_controller_block?
|
|
194
|
+
add_route v
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
@current_controller = nil unless in_controller_block?
|
|
201
|
+
exp
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def process_resources exp
|
|
205
|
+
first_arg = exp.first_arg
|
|
206
|
+
second_arg = exp.second_arg
|
|
207
|
+
|
|
208
|
+
return exp unless symbol? first_arg or string? first_arg
|
|
209
|
+
|
|
210
|
+
if second_arg and second_arg.node_type == :hash
|
|
211
|
+
self.current_controller = first_arg.value
|
|
212
|
+
#handle hash
|
|
213
|
+
add_resources_routes
|
|
214
|
+
elsif exp.args.all? { |s| symbol? s }
|
|
215
|
+
exp.each_arg do |s|
|
|
216
|
+
self.current_controller = s.value
|
|
217
|
+
add_resources_routes
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
@current_controller = nil unless in_controller_block?
|
|
222
|
+
exp
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def process_resource exp
|
|
226
|
+
#Does resource even take more than one controller name?
|
|
227
|
+
exp.each_arg do |s|
|
|
228
|
+
if symbol? s
|
|
229
|
+
self.current_controller = pluralize(s.value.to_s)
|
|
230
|
+
add_resource_routes
|
|
231
|
+
else
|
|
232
|
+
#handle something else, like options
|
|
233
|
+
#or something?
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
@current_controller = nil unless in_controller_block?
|
|
238
|
+
exp
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def process_resources_block exp
|
|
242
|
+
in_controller_block do
|
|
243
|
+
process_resources exp.block_call
|
|
244
|
+
process exp.block
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
@current_controller = nil
|
|
248
|
+
exp
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def process_resource_block exp
|
|
252
|
+
in_controller_block do
|
|
253
|
+
process_resource exp.block_call
|
|
254
|
+
process exp.block
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
@current_controller = nil
|
|
258
|
+
exp
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def process_scope_block exp
|
|
262
|
+
#How to deal with options?
|
|
263
|
+
process exp.block
|
|
264
|
+
exp
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def process_controller_block exp
|
|
268
|
+
if string? exp or symbol? exp
|
|
269
|
+
self.current_controller = exp.block_call.first_arg.value
|
|
270
|
+
|
|
271
|
+
in_controller_block do
|
|
272
|
+
process exp[-1] if exp[-1]
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
@current_controller = nil
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
exp
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def extract_action str
|
|
282
|
+
str.split "#"
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def in_controller_block?
|
|
286
|
+
@controller_block
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def in_controller_block
|
|
290
|
+
prev_block = @controller_block
|
|
291
|
+
@controller_block = true
|
|
292
|
+
yield
|
|
293
|
+
@controller_block = prev_block
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def action_route? arg
|
|
297
|
+
if string? arg
|
|
298
|
+
arg = arg.value
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
arg.is_a? String and (arg.include? ":action" or arg.include? "*action")
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def loose_action controller_name, verb = "any"
|
|
305
|
+
self.current_controller = controller_name.value
|
|
306
|
+
@tracker.routes[@current_controller] = [:allow_all_actions, {:allow_verb => verb}]
|
|
307
|
+
end
|
|
308
|
+
end
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
require 'digest/sha1'
|
|
2
|
+
|
|
3
|
+
#Processes a call to render() in a controller or template
|
|
4
|
+
module Railroader::RenderHelper
|
|
5
|
+
|
|
6
|
+
#Process s(:render, TYPE, OPTION?, OPTIONS)
|
|
7
|
+
def process_render exp
|
|
8
|
+
process_default exp
|
|
9
|
+
@rendered = true
|
|
10
|
+
case exp.render_type
|
|
11
|
+
when :action, :template, :inline
|
|
12
|
+
process_action exp[2][1], exp[3], exp.line
|
|
13
|
+
when :default
|
|
14
|
+
begin
|
|
15
|
+
process_template template_name, exp[3], nil, exp.line
|
|
16
|
+
rescue ArgumentError
|
|
17
|
+
Railroader.debug "Problem processing render: #{exp}"
|
|
18
|
+
end
|
|
19
|
+
when :partial, :layout
|
|
20
|
+
process_partial exp[2], exp[3], exp.line
|
|
21
|
+
when :nothing
|
|
22
|
+
end
|
|
23
|
+
exp
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
#Processes layout
|
|
27
|
+
def process_layout name = nil
|
|
28
|
+
if name.nil? and defined? layout_name
|
|
29
|
+
name = layout_name
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
return unless name
|
|
33
|
+
|
|
34
|
+
process_template name, nil, nil, nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
#Determines file name for partial and then processes it
|
|
38
|
+
def process_partial name, args, line
|
|
39
|
+
if name == "" or !(string? name or symbol? name)
|
|
40
|
+
return
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
names = name.value.to_s.split("/")
|
|
44
|
+
names[-1] = "_" + names[-1]
|
|
45
|
+
process_template template_name(names.join("/")), args, nil, line
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
#Processes a given action
|
|
49
|
+
def process_action name, args, line
|
|
50
|
+
if name.is_a? String or name.is_a? Symbol
|
|
51
|
+
process_template template_name(name), args, nil, line
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
#Processes a template, adding any instance variables
|
|
56
|
+
#to its environment.
|
|
57
|
+
def process_template name, args, called_from = nil, *_
|
|
58
|
+
|
|
59
|
+
Railroader.debug "Rendering #{name} (#{called_from})"
|
|
60
|
+
#Get scanned source for this template
|
|
61
|
+
name = name.to_s.gsub(/^\//, "")
|
|
62
|
+
template = @tracker.templates[name.to_sym]
|
|
63
|
+
unless template
|
|
64
|
+
Railroader.debug "[Notice] No such template: #{name}"
|
|
65
|
+
return
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
template_env = only_ivars(:include_request_vars)
|
|
69
|
+
|
|
70
|
+
#Hash the environment and the source of the template to avoid
|
|
71
|
+
#pointlessly processing templates, which can become prohibitively
|
|
72
|
+
#expensive in terms of time and memory.
|
|
73
|
+
digest = Digest::SHA1.new.update(template_env.instance_variable_get(:@env).to_a.sort.to_s << name).to_s.to_sym
|
|
74
|
+
|
|
75
|
+
if @tracker.template_cache.include? digest
|
|
76
|
+
#Already processed this template with identical environment
|
|
77
|
+
return
|
|
78
|
+
else
|
|
79
|
+
@tracker.template_cache << digest
|
|
80
|
+
|
|
81
|
+
options = get_options args
|
|
82
|
+
|
|
83
|
+
#Process layout
|
|
84
|
+
if string? options[:layout]
|
|
85
|
+
process_template "layouts/#{options[:layout][1]}", nil, nil, nil
|
|
86
|
+
elsif node_type? options[:layout], :false
|
|
87
|
+
#nothing
|
|
88
|
+
elsif not template.name.to_s.match(/[^\/_][^\/]+$/)
|
|
89
|
+
#Don't do this for partials
|
|
90
|
+
|
|
91
|
+
process_layout
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
if hash? options[:locals]
|
|
95
|
+
hash_iterate options[:locals] do |key, value|
|
|
96
|
+
template_env[Sexp.new(:call, nil, key.value)] = value
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
if options[:collection]
|
|
101
|
+
|
|
102
|
+
#The collection name is the name of the partial without the leading
|
|
103
|
+
#underscore.
|
|
104
|
+
variable = template.name.to_s.match(/[^\/_][^\/]+$/)[0].to_sym
|
|
105
|
+
|
|
106
|
+
#Unless the :as => :variable_name option is used
|
|
107
|
+
if options[:as]
|
|
108
|
+
if string? options[:as] or symbol? options[:as]
|
|
109
|
+
variable = options[:as].value.to_sym
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
collection = get_class_target(options[:collection]) || Railroader::Tracker::UNKNOWN_MODEL
|
|
114
|
+
|
|
115
|
+
template_env[Sexp.new(:call, nil, variable)] = Sexp.new(:call, Sexp.new(:const, collection), :new)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
#Set original_line for values so it is clear
|
|
119
|
+
#that values came from another file
|
|
120
|
+
template_env.all.each do |_var, value|
|
|
121
|
+
unless value.original_line
|
|
122
|
+
#TODO: This has been broken for a while now and no one noticed
|
|
123
|
+
#so maybe we can skip it
|
|
124
|
+
value.original_line = value.line
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
#Run source through AliasProcessor with instance variables from the
|
|
129
|
+
#current environment.
|
|
130
|
+
#TODO: Add in :locals => { ... } to environment
|
|
131
|
+
src = Railroader::TemplateAliasProcessor.new(@tracker, template, called_from).process_safely(template.src, template_env)
|
|
132
|
+
|
|
133
|
+
digest = Digest::SHA1.new.update(name + src.to_s).to_s.to_sym
|
|
134
|
+
|
|
135
|
+
if @tracker.template_cache.include? digest
|
|
136
|
+
return
|
|
137
|
+
else
|
|
138
|
+
@tracker.template_cache << digest
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
#Run alias-processed src through the template processor to pull out
|
|
142
|
+
#information and outputs.
|
|
143
|
+
#This information will be stored in tracker.templates, but with a name
|
|
144
|
+
#specifying this particular route. The original source should remain
|
|
145
|
+
#pristine (so it can be processed within other environments).
|
|
146
|
+
@tracker.processor.process_template name, src, template.type, called_from
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
#Override to process name, such as adding the controller name.
|
|
151
|
+
def template_name name
|
|
152
|
+
raise "RenderHelper#template_name should be overridden."
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
#Turn options Sexp into hash
|
|
156
|
+
def get_options args
|
|
157
|
+
options = {}
|
|
158
|
+
return options unless hash? args
|
|
159
|
+
|
|
160
|
+
hash_iterate args do |key, value|
|
|
161
|
+
if symbol? key
|
|
162
|
+
options[key.value] = value
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
options
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def get_class_target sexp
|
|
170
|
+
if call? sexp
|
|
171
|
+
get_class_target sexp.target
|
|
172
|
+
else
|
|
173
|
+
klass = class_name sexp
|
|
174
|
+
if klass.is_a? Symbol
|
|
175
|
+
klass
|
|
176
|
+
else
|
|
177
|
+
nil
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
module Railroader
|
|
2
|
+
class RenderPath
|
|
3
|
+
attr_reader :path
|
|
4
|
+
|
|
5
|
+
def initialize
|
|
6
|
+
@path = []
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def add_controller_render controller_name, method_name, line, file
|
|
10
|
+
method_name ||= ""
|
|
11
|
+
|
|
12
|
+
@path << { :type => :controller,
|
|
13
|
+
:class => controller_name.to_sym,
|
|
14
|
+
:method => method_name.to_sym,
|
|
15
|
+
:line => line,
|
|
16
|
+
:file => file
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def add_template_render template_name, line, file
|
|
23
|
+
@path << { :type => :template,
|
|
24
|
+
:name => template_name.to_sym,
|
|
25
|
+
:line => line,
|
|
26
|
+
:file => file
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def include_template? name
|
|
33
|
+
name = name.to_sym
|
|
34
|
+
|
|
35
|
+
@path.any? do |loc|
|
|
36
|
+
loc[:type] == :template and loc[:name] == name
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def include_controller? klass
|
|
41
|
+
klass = klass.to_sym
|
|
42
|
+
|
|
43
|
+
@path.any? do |loc|
|
|
44
|
+
loc[:type] == :controller and loc[:class] == klass
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def include_any_method? method_names
|
|
49
|
+
names = method_names.map(&:to_sym)
|
|
50
|
+
|
|
51
|
+
@path.any? do |loc|
|
|
52
|
+
loc[:type] == :controller and names.include? loc[:method]
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def rendered_from_controller?
|
|
57
|
+
@path.any? do |loc|
|
|
58
|
+
loc[:type] == :controller
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def each &block
|
|
63
|
+
@path.each(&block)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def join *args
|
|
67
|
+
self.to_a.join(*args)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def length
|
|
71
|
+
@path.length
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def to_a
|
|
75
|
+
@path.map do |loc|
|
|
76
|
+
case loc[:type]
|
|
77
|
+
when :template
|
|
78
|
+
"Template:#{loc[:name]}"
|
|
79
|
+
when :controller
|
|
80
|
+
"#{loc[:class]}##{loc[:method]}"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def last
|
|
86
|
+
self.to_a.last
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def to_s
|
|
90
|
+
self.to_a.to_s
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def to_sym
|
|
94
|
+
self.to_s.to_sym
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def to_json *args
|
|
98
|
+
require 'json'
|
|
99
|
+
JSON.generate(@path)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def initialize_copy original
|
|
103
|
+
@path = original.path.dup
|
|
104
|
+
self
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|