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.
Files changed (165) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES.md +1091 -0
  3. data/FEATURES +16 -0
  4. data/README.md +174 -0
  5. data/bin/railroader +8 -0
  6. data/lib/railroader/app_tree.rb +191 -0
  7. data/lib/railroader/call_index.rb +219 -0
  8. data/lib/railroader/checks/base_check.rb +505 -0
  9. data/lib/railroader/checks/check_basic_auth.rb +88 -0
  10. data/lib/railroader/checks/check_basic_auth_timing_attack.rb +33 -0
  11. data/lib/railroader/checks/check_content_tag.rb +200 -0
  12. data/lib/railroader/checks/check_create_with.rb +74 -0
  13. data/lib/railroader/checks/check_cross_site_scripting.rb +381 -0
  14. data/lib/railroader/checks/check_default_routes.rb +86 -0
  15. data/lib/railroader/checks/check_deserialize.rb +56 -0
  16. data/lib/railroader/checks/check_detailed_exceptions.rb +55 -0
  17. data/lib/railroader/checks/check_digest_dos.rb +38 -0
  18. data/lib/railroader/checks/check_divide_by_zero.rb +42 -0
  19. data/lib/railroader/checks/check_dynamic_finders.rb +48 -0
  20. data/lib/railroader/checks/check_escape_function.rb +21 -0
  21. data/lib/railroader/checks/check_evaluation.rb +35 -0
  22. data/lib/railroader/checks/check_execute.rb +189 -0
  23. data/lib/railroader/checks/check_file_access.rb +71 -0
  24. data/lib/railroader/checks/check_file_disclosure.rb +35 -0
  25. data/lib/railroader/checks/check_filter_skipping.rb +31 -0
  26. data/lib/railroader/checks/check_forgery_setting.rb +81 -0
  27. data/lib/railroader/checks/check_header_dos.rb +31 -0
  28. data/lib/railroader/checks/check_i18n_xss.rb +48 -0
  29. data/lib/railroader/checks/check_jruby_xml.rb +36 -0
  30. data/lib/railroader/checks/check_json_encoding.rb +47 -0
  31. data/lib/railroader/checks/check_json_parsing.rb +107 -0
  32. data/lib/railroader/checks/check_link_to.rb +132 -0
  33. data/lib/railroader/checks/check_link_to_href.rb +146 -0
  34. data/lib/railroader/checks/check_mail_to.rb +49 -0
  35. data/lib/railroader/checks/check_mass_assignment.rb +196 -0
  36. data/lib/railroader/checks/check_mime_type_dos.rb +39 -0
  37. data/lib/railroader/checks/check_model_attr_accessible.rb +55 -0
  38. data/lib/railroader/checks/check_model_attributes.rb +119 -0
  39. data/lib/railroader/checks/check_model_serialize.rb +67 -0
  40. data/lib/railroader/checks/check_nested_attributes.rb +38 -0
  41. data/lib/railroader/checks/check_nested_attributes_bypass.rb +58 -0
  42. data/lib/railroader/checks/check_number_to_currency.rb +74 -0
  43. data/lib/railroader/checks/check_permit_attributes.rb +43 -0
  44. data/lib/railroader/checks/check_quote_table_name.rb +40 -0
  45. data/lib/railroader/checks/check_redirect.rb +256 -0
  46. data/lib/railroader/checks/check_regex_dos.rb +68 -0
  47. data/lib/railroader/checks/check_render.rb +97 -0
  48. data/lib/railroader/checks/check_render_dos.rb +37 -0
  49. data/lib/railroader/checks/check_render_inline.rb +53 -0
  50. data/lib/railroader/checks/check_response_splitting.rb +21 -0
  51. data/lib/railroader/checks/check_route_dos.rb +42 -0
  52. data/lib/railroader/checks/check_safe_buffer_manipulation.rb +31 -0
  53. data/lib/railroader/checks/check_sanitize_methods.rb +112 -0
  54. data/lib/railroader/checks/check_secrets.rb +40 -0
  55. data/lib/railroader/checks/check_select_tag.rb +59 -0
  56. data/lib/railroader/checks/check_select_vulnerability.rb +60 -0
  57. data/lib/railroader/checks/check_send.rb +47 -0
  58. data/lib/railroader/checks/check_send_file.rb +19 -0
  59. data/lib/railroader/checks/check_session_manipulation.rb +35 -0
  60. data/lib/railroader/checks/check_session_settings.rb +176 -0
  61. data/lib/railroader/checks/check_simple_format.rb +58 -0
  62. data/lib/railroader/checks/check_single_quotes.rb +101 -0
  63. data/lib/railroader/checks/check_skip_before_filter.rb +60 -0
  64. data/lib/railroader/checks/check_sql.rb +700 -0
  65. data/lib/railroader/checks/check_sql_cves.rb +106 -0
  66. data/lib/railroader/checks/check_ssl_verify.rb +48 -0
  67. data/lib/railroader/checks/check_strip_tags.rb +89 -0
  68. data/lib/railroader/checks/check_symbol_dos.rb +71 -0
  69. data/lib/railroader/checks/check_symbol_dos_cve.rb +30 -0
  70. data/lib/railroader/checks/check_translate_bug.rb +45 -0
  71. data/lib/railroader/checks/check_unsafe_reflection.rb +50 -0
  72. data/lib/railroader/checks/check_unscoped_find.rb +57 -0
  73. data/lib/railroader/checks/check_validation_regex.rb +116 -0
  74. data/lib/railroader/checks/check_weak_hash.rb +148 -0
  75. data/lib/railroader/checks/check_without_protection.rb +80 -0
  76. data/lib/railroader/checks/check_xml_dos.rb +45 -0
  77. data/lib/railroader/checks/check_yaml_parsing.rb +121 -0
  78. data/lib/railroader/checks.rb +209 -0
  79. data/lib/railroader/codeclimate/engine_configuration.rb +97 -0
  80. data/lib/railroader/commandline.rb +179 -0
  81. data/lib/railroader/differ.rb +66 -0
  82. data/lib/railroader/file_parser.rb +54 -0
  83. data/lib/railroader/format/style.css +133 -0
  84. data/lib/railroader/options.rb +339 -0
  85. data/lib/railroader/parsers/rails2_erubis.rb +6 -0
  86. data/lib/railroader/parsers/rails2_xss_plugin_erubis.rb +48 -0
  87. data/lib/railroader/parsers/rails3_erubis.rb +81 -0
  88. data/lib/railroader/parsers/template_parser.rb +108 -0
  89. data/lib/railroader/processor.rb +102 -0
  90. data/lib/railroader/processors/alias_processor.rb +1229 -0
  91. data/lib/railroader/processors/base_processor.rb +295 -0
  92. data/lib/railroader/processors/config_processor.rb +14 -0
  93. data/lib/railroader/processors/controller_alias_processor.rb +278 -0
  94. data/lib/railroader/processors/controller_processor.rb +249 -0
  95. data/lib/railroader/processors/erb_template_processor.rb +77 -0
  96. data/lib/railroader/processors/erubis_template_processor.rb +92 -0
  97. data/lib/railroader/processors/gem_processor.rb +64 -0
  98. data/lib/railroader/processors/haml_template_processor.rb +191 -0
  99. data/lib/railroader/processors/lib/basic_processor.rb +37 -0
  100. data/lib/railroader/processors/lib/call_conversion_helper.rb +90 -0
  101. data/lib/railroader/processors/lib/find_all_calls.rb +224 -0
  102. data/lib/railroader/processors/lib/find_call.rb +183 -0
  103. data/lib/railroader/processors/lib/find_return_value.rb +166 -0
  104. data/lib/railroader/processors/lib/module_helper.rb +111 -0
  105. data/lib/railroader/processors/lib/processor_helper.rb +88 -0
  106. data/lib/railroader/processors/lib/rails2_config_processor.rb +145 -0
  107. data/lib/railroader/processors/lib/rails2_route_processor.rb +313 -0
  108. data/lib/railroader/processors/lib/rails3_config_processor.rb +132 -0
  109. data/lib/railroader/processors/lib/rails3_route_processor.rb +308 -0
  110. data/lib/railroader/processors/lib/render_helper.rb +181 -0
  111. data/lib/railroader/processors/lib/render_path.rb +107 -0
  112. data/lib/railroader/processors/lib/route_helper.rb +68 -0
  113. data/lib/railroader/processors/lib/safe_call_helper.rb +16 -0
  114. data/lib/railroader/processors/library_processor.rb +74 -0
  115. data/lib/railroader/processors/model_processor.rb +91 -0
  116. data/lib/railroader/processors/output_processor.rb +144 -0
  117. data/lib/railroader/processors/route_processor.rb +17 -0
  118. data/lib/railroader/processors/slim_template_processor.rb +111 -0
  119. data/lib/railroader/processors/template_alias_processor.rb +118 -0
  120. data/lib/railroader/processors/template_processor.rb +85 -0
  121. data/lib/railroader/report/config/remediation.yml +71 -0
  122. data/lib/railroader/report/ignore/config.rb +153 -0
  123. data/lib/railroader/report/ignore/interactive.rb +362 -0
  124. data/lib/railroader/report/pager.rb +112 -0
  125. data/lib/railroader/report/renderer.rb +24 -0
  126. data/lib/railroader/report/report_base.rb +292 -0
  127. data/lib/railroader/report/report_codeclimate.rb +79 -0
  128. data/lib/railroader/report/report_csv.rb +55 -0
  129. data/lib/railroader/report/report_hash.rb +23 -0
  130. data/lib/railroader/report/report_html.rb +216 -0
  131. data/lib/railroader/report/report_json.rb +45 -0
  132. data/lib/railroader/report/report_markdown.rb +107 -0
  133. data/lib/railroader/report/report_table.rb +117 -0
  134. data/lib/railroader/report/report_tabs.rb +17 -0
  135. data/lib/railroader/report/report_text.rb +198 -0
  136. data/lib/railroader/report/templates/controller_overview.html.erb +22 -0
  137. data/lib/railroader/report/templates/controller_warnings.html.erb +21 -0
  138. data/lib/railroader/report/templates/error_overview.html.erb +29 -0
  139. data/lib/railroader/report/templates/header.html.erb +58 -0
  140. data/lib/railroader/report/templates/ignored_warnings.html.erb +25 -0
  141. data/lib/railroader/report/templates/model_warnings.html.erb +21 -0
  142. data/lib/railroader/report/templates/overview.html.erb +38 -0
  143. data/lib/railroader/report/templates/security_warnings.html.erb +23 -0
  144. data/lib/railroader/report/templates/template_overview.html.erb +21 -0
  145. data/lib/railroader/report/templates/view_warnings.html.erb +34 -0
  146. data/lib/railroader/report/templates/warning_overview.html.erb +17 -0
  147. data/lib/railroader/report.rb +88 -0
  148. data/lib/railroader/rescanner.rb +483 -0
  149. data/lib/railroader/scanner.rb +321 -0
  150. data/lib/railroader/tracker/collection.rb +93 -0
  151. data/lib/railroader/tracker/config.rb +154 -0
  152. data/lib/railroader/tracker/constants.rb +171 -0
  153. data/lib/railroader/tracker/controller.rb +161 -0
  154. data/lib/railroader/tracker/library.rb +17 -0
  155. data/lib/railroader/tracker/model.rb +90 -0
  156. data/lib/railroader/tracker/template.rb +33 -0
  157. data/lib/railroader/tracker.rb +362 -0
  158. data/lib/railroader/util.rb +503 -0
  159. data/lib/railroader/version.rb +3 -0
  160. data/lib/railroader/warning.rb +294 -0
  161. data/lib/railroader/warning_codes.rb +117 -0
  162. data/lib/railroader.rb +544 -0
  163. data/lib/ruby_parser/bm_sexp.rb +626 -0
  164. data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
  165. metadata +386 -0
@@ -0,0 +1,68 @@
1
+ module Railroader::RouteHelper
2
+ #Manage Controller prefixes
3
+ #@prefix is an Array, but this method returns a string
4
+ #suitable for prefixing onto a controller name.
5
+ def prefix
6
+ if @prefix.length > 0
7
+ @prefix.join("::") << "::"
8
+ else
9
+ ''
10
+ end
11
+ end
12
+
13
+ #Sets the controller name to a proper class name.
14
+ #For example
15
+ # self.current_controller = :session
16
+ # @controller == :SessionController #true
17
+ #
18
+ #Also prepends the prefix if there is one set.
19
+ def current_controller= name
20
+ @current_controller = (prefix + camelize(name) + "Controller").to_sym
21
+ @tracker.routes[@current_controller] ||= Set.new
22
+ end
23
+
24
+ #Add route to controller. If a controller is specified,
25
+ #the current controller will be set to that controller.
26
+ #If no controller is specified, uses current controller value.
27
+ def add_route route, controller = nil
28
+ if node_type? route, :str, :lit
29
+ route = route.value
30
+ end
31
+
32
+ return unless route.is_a? String or route.is_a? Symbol
33
+
34
+ if route.is_a? String and controller.nil? and route.include? ":controller"
35
+ controller = ":controller"
36
+ end
37
+
38
+ route = route.to_sym
39
+
40
+ if controller
41
+ self.current_controller = controller
42
+ end
43
+
44
+ routes = @tracker.routes[@current_controller]
45
+
46
+ if routes and not routes.include? :allow_all_actions
47
+ routes << route
48
+ end
49
+ end
50
+
51
+ #Add default routes
52
+ def add_resources_routes
53
+ existing_routes = @tracker.routes[@current_controller]
54
+
55
+ unless existing_routes.is_a? Array and existing_routes.first == :allow_all_actions
56
+ existing_routes.merge [:index, :new, :create, :show, :edit, :update, :destroy]
57
+ end
58
+ end
59
+
60
+ #Add default routes minus :index
61
+ def add_resource_routes
62
+ existing_routes = @tracker.routes[@current_controller]
63
+
64
+ unless existing_routes.is_a? Array and existing_routes.first == :allow_all_actions
65
+ existing_routes.merge [:new, :create, :show, :edit, :update, :destroy]
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,16 @@
1
+ module Railroader
2
+ module SafeCallHelper
3
+ [[:process_safe_call, :process_call],
4
+ [:process_safe_attrasgn, :process_attrasgn],
5
+ [:process_safe_op_asgn, :process_op_asgn],
6
+ [:process_safe_op_asgn2, :process_op_asgn2]].each do |call, replacement|
7
+ define_method(call) do |exp|
8
+ if self.respond_to? replacement
9
+ self.send(replacement, exp)
10
+ else
11
+ process_default exp
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,74 @@
1
+ require 'railroader/processors/base_processor'
2
+ require 'railroader/processors/alias_processor'
3
+ require 'railroader/processors/lib/module_helper'
4
+ require 'railroader/tracker/library'
5
+
6
+ #Process generic library and stores it in Tracker.libs
7
+ class Railroader::LibraryProcessor < Railroader::BaseProcessor
8
+ include Railroader::ModuleHelper
9
+
10
+ def initialize tracker
11
+ super
12
+ @file_name = nil
13
+ @alias_processor = Railroader::AliasProcessor.new tracker
14
+ @current_module = nil
15
+ @current_class = nil
16
+ @intializer_env = nil
17
+ end
18
+
19
+ def process_library src, file_name = nil
20
+ @file_name = file_name
21
+ process src
22
+ end
23
+
24
+ def process_class exp
25
+ handle_class exp, @tracker.libs, Railroader::Library
26
+ end
27
+
28
+ def process_module exp
29
+ handle_module exp, Railroader::Library
30
+ end
31
+
32
+ def process_defn exp
33
+ if exp.method_name == :initialize
34
+ @alias_processor.process_safely exp.body_list
35
+ @initializer_env = @alias_processor.only_ivars
36
+ elsif node_type? exp, :defn
37
+ exp = @alias_processor.process_safely exp, @initializer_env
38
+ else
39
+ exp = @alias_processor.process exp
40
+ end
41
+
42
+ if @current_class
43
+ exp.body = process_all! exp.body
44
+ @current_class.add_method :public, exp.method_name, exp, @file_name
45
+ elsif @current_module
46
+ exp.body = process_all! exp.body
47
+ @current_module.add_method :public, exp.method_name, exp, @file_name
48
+ end
49
+
50
+ exp
51
+ end
52
+
53
+ alias process_defs process_defn
54
+
55
+ def process_call exp
56
+ if process_call_defn? exp
57
+ exp
58
+ else
59
+ process_default exp
60
+ end
61
+ end
62
+
63
+ def process_iter exp
64
+ res = process_default exp
65
+
66
+ if node_type? res, :iter and call? exp.block_call # sometimes this changes after processing
67
+ if exp.block_call.method == :included and (@current_module or @current_class)
68
+ (@current_module || @current_class).options[:included] = res.block
69
+ end
70
+ end
71
+
72
+ res
73
+ end
74
+ end
@@ -0,0 +1,91 @@
1
+ require 'railroader/processors/base_processor'
2
+ require 'railroader/processors/lib/module_helper'
3
+ require 'railroader/tracker/model'
4
+
5
+ #Processes models. Puts results in tracker.models
6
+ class Railroader::ModelProcessor < Railroader::BaseProcessor
7
+ include Railroader::ModuleHelper
8
+
9
+ def initialize tracker
10
+ super
11
+ @current_class = nil
12
+ @current_method = nil
13
+ @current_module = nil
14
+ @visibility = :public
15
+ @file_name = nil
16
+ end
17
+
18
+ #Process model source
19
+ def process_model src, file_name = nil
20
+ @file_name = file_name
21
+ process src
22
+ end
23
+
24
+ #s(:class, NAME, PARENT, BODY)
25
+ def process_class exp
26
+ name = class_name(exp.class_name)
27
+ parent = class_name(exp.parent_name)
28
+
29
+ #If inside an inner class we treat it as a library.
30
+ if @current_class
31
+ Railroader.debug "[Notice] Treating inner class as library: #{name}"
32
+ Railroader::LibraryProcessor.new(@tracker).process_library exp, @file_name
33
+ return exp
34
+ end
35
+
36
+ handle_class exp, @tracker.models, Railroader::Model
37
+ end
38
+
39
+ def process_module exp
40
+ handle_module exp, Railroader::Model
41
+ end
42
+
43
+ #Handle calls outside of methods,
44
+ #such as include, attr_accessible, private, etc.
45
+ def process_call exp
46
+ return exp unless @current_class
47
+ return exp if process_call_defn? exp
48
+
49
+ target = exp.target
50
+ if sexp? target
51
+ target = process target
52
+ end
53
+
54
+ method = exp.method
55
+ first_arg = exp.first_arg
56
+
57
+ #Methods called inside class definition
58
+ #like attr_* and other settings
59
+ if @current_method.nil? and target.nil?
60
+ if first_arg.nil?
61
+ case method
62
+ when :private, :protected, :public
63
+ @visibility = method
64
+ when :attr_accessible
65
+ @current_class.set_attr_accessible
66
+ else
67
+ #??
68
+ end
69
+ else
70
+ case method
71
+ when :include
72
+ @current_class.add_include class_name(first_arg) if @current_class
73
+ when :attr_accessible
74
+ @current_class.set_attr_accessible exp
75
+ when :attr_protected
76
+ @current_class.set_attr_protected exp
77
+ else
78
+ if @current_class
79
+ @current_class.add_option method, exp
80
+ end
81
+ end
82
+ end
83
+
84
+ exp
85
+ else
86
+ call = make_call target, method, process_all!(exp.args)
87
+ call.line(exp.line)
88
+ call
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,144 @@
1
+ Railroader.load_railroader_dependency 'ruby2ruby'
2
+ require 'railroader/util'
3
+
4
+ #Produces formatted output strings from Sexps.
5
+ #Recommended usage is
6
+ #
7
+ # OutputProcessor.new.format(Sexp.new(:str, "hello"))
8
+ class Railroader::OutputProcessor < Ruby2Ruby
9
+ include Railroader::Util
10
+
11
+ #Copies +exp+ and then formats it.
12
+ def format exp, user_input = nil, &block
13
+ @user_input = user_input
14
+ @user_input_block = block
15
+ process(exp.deep_clone) || "[Format Error]"
16
+ end
17
+
18
+ alias process_safely format
19
+
20
+ def process exp
21
+ begin
22
+ if @user_input and @user_input == exp
23
+ @user_input_block.call(exp, super(exp))
24
+ else
25
+ super exp if sexp? exp and not exp.empty?
26
+ end
27
+ rescue => e
28
+ Railroader.debug "While formatting #{exp}: #{e}\n#{e.backtrace.join("\n")}"
29
+ end
30
+ end
31
+
32
+ def process_ignore exp
33
+ "[ignored]"
34
+ end
35
+
36
+ def process_params exp
37
+ "params"
38
+ end
39
+
40
+ def process_session exp
41
+ "session"
42
+ end
43
+
44
+ def process_cookies exp
45
+ "cookies"
46
+ end
47
+
48
+ def process_rlist exp
49
+ out = exp.map do |e|
50
+ res = process e
51
+ if res == ""
52
+ nil
53
+ else
54
+ res
55
+ end
56
+ end.compact.join("\n")
57
+
58
+ out
59
+ end
60
+
61
+ def process_defn exp
62
+ # Copied from Ruby2Ruby except without the whole
63
+ # "convert methods to attr_*" stuff
64
+ exp = exp.deep_clone
65
+ exp.shift
66
+ name = exp.shift
67
+ args = process exp.shift
68
+ args = "" if args == "()"
69
+
70
+ exp.shift if exp == s(s(:nil)) # empty it out of a default nil expression
71
+
72
+ body = []
73
+ until exp.empty? do
74
+ body << indent(process(exp.shift))
75
+ end
76
+
77
+ body << indent("# do nothing") if body.empty?
78
+
79
+ body = body.join("\n")
80
+
81
+ return "def #{name}#{args}\n#{body}\nend".gsub(/\n\s*\n+/, "\n")
82
+ end
83
+
84
+ def process_iter exp
85
+ call = process exp[1]
86
+ block = process_rlist exp[3..-1]
87
+ out = "#{call} do\n #{block}\n end"
88
+
89
+ out
90
+ end
91
+
92
+ def process_output exp
93
+ output_format exp, "Output"
94
+ end
95
+
96
+ def process_escaped_output exp
97
+ output_format exp, "Escaped Output"
98
+ end
99
+
100
+
101
+ def process_format exp
102
+ output_format exp, "Format"
103
+ end
104
+
105
+ def process_format_escaped exp
106
+ output_format exp, "Escaped"
107
+ end
108
+
109
+ def output_format exp, tag
110
+ out = if exp[1].node_type == :str or exp[1].node_type == :ignore
111
+ ""
112
+ else
113
+ res = process exp[1]
114
+
115
+ if res == ""
116
+ ""
117
+ else
118
+ "[#{tag}] #{res}"
119
+ end
120
+ end
121
+
122
+ out
123
+ end
124
+
125
+ def process_const exp
126
+ if exp[1] == Railroader::Tracker::UNKNOWN_MODEL
127
+ "(Unresolved Model)"
128
+ else
129
+ out = exp[1].to_s
130
+ out
131
+ end
132
+ end
133
+
134
+ def process_render exp
135
+ exp = exp.deep_clone
136
+ exp.shift
137
+
138
+ exp[1] = process exp[1] if sexp? exp[1]
139
+ exp[2] = process exp[2] if sexp? exp[2]
140
+ out = "render(#{exp[0]} => #{exp[1]}, #{exp[2]})"
141
+
142
+ out
143
+ end
144
+ end
@@ -0,0 +1,17 @@
1
+ require 'railroader/processors/base_processor'
2
+ require 'railroader/processors/alias_processor'
3
+ require 'railroader/processors/lib/route_helper'
4
+ require 'railroader/util'
5
+ require 'railroader/processors/lib/rails3_route_processor.rb'
6
+ require 'railroader/processors/lib/rails2_route_processor.rb'
7
+ require 'set'
8
+
9
+ class Railroader::RoutesProcessor
10
+ def self.new tracker
11
+ if tracker.options[:rails3]
12
+ Railroader::Rails3RoutesProcessor.new tracker
13
+ else
14
+ Railroader::Rails2RoutesProcessor.new tracker
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,111 @@
1
+ require 'railroader/processors/template_processor'
2
+ require 'railroader/processors/lib/render_helper'
3
+
4
+ class Railroader::SlimTemplateProcessor < Railroader::TemplateProcessor
5
+ include Railroader::RenderHelper
6
+
7
+ SAFE_BUFFER = s(:call, s(:colon2, s(:const, :ActiveSupport), :SafeBuffer), :new)
8
+ OUTPUT_BUFFER = s(:ivar, :@output_buffer)
9
+ TEMPLE_UTILS = s(:colon2, s(:colon3, :Temple), :Utils)
10
+ ATTR_MERGE = s(:call, s(:call, s(:array), :reject, s(:block_pass, s(:lit, :empty?))), :join, s(:str, " "))
11
+
12
+ def process_call exp
13
+ target = exp.target
14
+ method = exp.method
15
+
16
+ if method == :safe_concat and (target == SAFE_BUFFER or target == OUTPUT_BUFFER)
17
+ arg = normalize_output(exp.first_arg)
18
+
19
+ if is_escaped? arg
20
+ add_escaped_output arg.first_arg
21
+ elsif string? arg
22
+ ignore
23
+ elsif render? arg
24
+ add_output make_render_in_view arg
25
+ elsif string_interp? arg
26
+ process_inside_interp arg
27
+ elsif node_type? arg, :ignore
28
+ ignore
29
+ elsif internal_variable? arg
30
+ ignore
31
+ elsif arg == ATTR_MERGE
32
+ ignore
33
+ else
34
+ add_output arg
35
+ end
36
+ elsif is_escaped? exp
37
+ add_escaped_output arg
38
+ elsif target == nil and method == :render
39
+ exp.arglist = process exp.arglist
40
+ make_render_in_view exp
41
+ else
42
+ exp.arglist = process exp.arglist
43
+ exp
44
+ end
45
+ end
46
+
47
+ #Slim likes to interpolate output into strings then pass them to safe_concat.
48
+ #Better to pull those values out directly.
49
+ def process_inside_interp exp
50
+ exp.map! do |e|
51
+ if node_type? e, :evstr
52
+ e.value = process_interp_output e.value
53
+ e
54
+ else
55
+ e
56
+ end
57
+ end
58
+
59
+ exp
60
+ end
61
+
62
+ def process_interp_output exp
63
+ if sexp? exp
64
+ if node_type? exp, :if
65
+ process_interp_output exp.then_clause
66
+ process_interp_output exp.else_clause
67
+ elsif exp == SAFE_BUFFER
68
+ ignore
69
+ elsif render? exp
70
+ add_output make_render_in_view exp
71
+ elsif node_type? :output, :escaped_output
72
+ exp
73
+ elsif is_escaped? exp
74
+ add_escaped_output exp
75
+ else
76
+ add_output exp
77
+ end
78
+ end
79
+ end
80
+
81
+ def add_escaped_output exp
82
+ exp = normalize_output(exp)
83
+
84
+ return exp if string? exp or internal_variable? exp
85
+
86
+ super exp
87
+ end
88
+
89
+ def is_escaped? exp
90
+ call? exp and
91
+ exp.target == TEMPLE_UTILS and
92
+ (exp.method == :escape_html or exp.method == :escape_html_safe)
93
+ end
94
+
95
+ def internal_variable? exp
96
+ node_type? exp, :lvar and
97
+ exp.value =~ /^_(temple_|slim_)/
98
+ end
99
+
100
+ def render? exp
101
+ call? exp and
102
+ exp.target.nil? and
103
+ exp.method == :render
104
+ end
105
+
106
+ def process_render exp
107
+ #Still confused as to why this is not needed in other template processors
108
+ #but is needed here
109
+ exp
110
+ end
111
+ end
@@ -0,0 +1,118 @@
1
+ require 'set'
2
+ require 'railroader/processors/alias_processor'
3
+ require 'railroader/processors/lib/render_helper'
4
+ require 'railroader/processors/lib/render_path'
5
+ require 'railroader/tracker'
6
+
7
+ #Processes aliasing in templates.
8
+ #Handles calls to +render+.
9
+ class Railroader::TemplateAliasProcessor < Railroader::AliasProcessor
10
+ include Railroader::RenderHelper
11
+
12
+ FORM_METHODS = Set[:form_for, :remote_form_for, :form_remote_for]
13
+
14
+ def initialize tracker, template, called_from = nil
15
+ super tracker
16
+ @template = template
17
+ @called_from = called_from
18
+ end
19
+
20
+ #Process template
21
+ def process_template name, args, _, line = nil, file_name = nil
22
+ @file_name = file_name || relative_path(@template.file || @tracker.templates[@template.name])
23
+
24
+ if @called_from
25
+ if @called_from.include_template? name
26
+ Railroader.debug "Skipping circular render from #{@template.name} to #{name}"
27
+ return
28
+ end
29
+
30
+ super name, args, @called_from.dup.add_template_render(@template.name, line, @file_name)
31
+ else
32
+ super name, args, Railroader::RenderPath.new.add_template_render(@template.name, line, @file_name)
33
+ end
34
+ end
35
+
36
+ #Determine template name
37
+ def template_name name
38
+ if !name.to_s.include?('/') && @template.name.to_s.include?('/')
39
+ name = "#{@template.name.to_s.match(/^(.*\/).*$/)[1]}#{name}"
40
+ end
41
+ name
42
+ end
43
+
44
+ UNKNOWN_MODEL_CALL = Sexp.new(:call, Sexp.new(:const, Railroader::Tracker::UNKNOWN_MODEL), :new)
45
+ FORM_BUILDER_CALL = Sexp.new(:call, Sexp.new(:const, :FormBuilder), :new)
46
+
47
+ #Looks for form methods and iterating over collections of Models
48
+ def process_iter exp
49
+ process_default exp
50
+
51
+ call = exp.block_call
52
+
53
+ if call? call
54
+ target = call.target
55
+ method = call.method
56
+ arg = exp.block_args.first_param
57
+ block = exp.block
58
+
59
+ #Check for e.g. Model.find.each do ... end
60
+ if method == :each and arg and block and model = get_model_target(target)
61
+ if arg.is_a? Symbol
62
+ if model == target.target
63
+ env[Sexp.new(:lvar, arg)] = Sexp.new(:call, model, :new)
64
+ else
65
+ env[Sexp.new(:lvar, arg)] = UNKNOWN_MODEL_CALL
66
+ end
67
+
68
+ process block if sexp? block
69
+ end
70
+ elsif FORM_METHODS.include? method
71
+ if arg.is_a? Symbol
72
+ env[Sexp.new(:lvar, arg)] = FORM_BUILDER_CALL
73
+
74
+ process block if sexp? block
75
+ end
76
+ end
77
+ end
78
+
79
+ exp
80
+ end
81
+
82
+ COLLECTION_METHODS = [:all, :find, :select, :where]
83
+
84
+ #Checks if +exp+ is a call to Model.all or Model.find*
85
+ def get_model_target exp
86
+ if call? exp
87
+ target = exp.target
88
+
89
+ if COLLECTION_METHODS.include? exp.method or exp.method.to_s[0,4] == "find"
90
+ models = Set.new @tracker.models.keys
91
+ name = class_name target
92
+ return target if models.include?(name)
93
+ end
94
+
95
+ return get_model_target(target)
96
+ end
97
+
98
+ false
99
+ end
100
+
101
+ #Ignore `<<` calls on template variables which are used by the templating
102
+ #library (HAML, ERB, etc.)
103
+ def find_push_target exp
104
+ if sexp? exp
105
+ if exp.node_type == :lvar and (exp.value == :_buf or exp.value == :_erbout)
106
+ return nil
107
+ elsif exp.node_type == :ivar and exp.value == :@output_buffer
108
+ return nil
109
+ elsif exp.node_type == :call and call? exp.target and
110
+ exp.target.method == :_hamlout and exp.method == :buffer
111
+
112
+ return nil
113
+ end
114
+ end
115
+
116
+ super
117
+ end
118
+ end