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,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