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,339 @@
1
+ require 'optparse'
2
+ require 'set'
3
+
4
+ #Parses command line arguments for Railroader
5
+ module Railroader::Options
6
+
7
+ class << self
8
+
9
+ #Parse argument array
10
+ def parse args
11
+ get_options args
12
+ end
13
+
14
+ #Parse arguments and remove them from the array as they are matched
15
+ def parse! args
16
+ get_options args, true
17
+ end
18
+
19
+ #Return hash of options and the parser
20
+ def get_options args, destructive = false
21
+ options = {}
22
+
23
+ parser = create_option_parser options
24
+
25
+ if destructive
26
+ parser.parse! args
27
+ else
28
+ parser.parse args
29
+ end
30
+
31
+ if options[:previous_results_json] and options[:output_files]
32
+ options[:comparison_output_file] = options[:output_files].shift
33
+ end
34
+
35
+ return options, parser
36
+ end
37
+
38
+ def create_option_parser options
39
+ OptionParser.new do |opts|
40
+ opts.banner = "Usage: railroader [options] rails/root/path"
41
+
42
+ opts.on "-n", "--no-threads", "Run checks sequentially" do
43
+ options[:parallel_checks] = false
44
+ end
45
+
46
+ opts.on "--[no-]progress", "Show progress reports" do |progress|
47
+ options[:report_progress] = progress
48
+ end
49
+
50
+ opts.on "-p", "--path PATH", "Specify path to Rails application" do |path|
51
+ options[:app_path] = path
52
+ end
53
+
54
+ opts.on "-q", "--[no-]quiet", "Suppress informational messages" do |quiet|
55
+ options[:quiet] = quiet
56
+ end
57
+
58
+ opts.on( "-z", "--[no-]exit-on-warn", "Exit code is non-zero if warnings found (Default)") do |exit_on_warn|
59
+ options[:exit_on_warn] = exit_on_warn
60
+ end
61
+
62
+ opts.on "--[no-]exit-on-error", "Exit code is non-zero if errors raised (Default)" do |exit_on_error|
63
+ options[:exit_on_error] = exit_on_error
64
+ end
65
+
66
+ opts.on "--ensure-latest", "Fail when Railroader is outdated" do
67
+ options[:ensure_latest] = true
68
+ end
69
+
70
+ opts.on "-3", "--rails3", "Force Rails 3 mode" do
71
+ options[:rails3] = true
72
+ end
73
+
74
+ opts.on "-4", "--rails4", "Force Rails 4 mode" do
75
+ options[:rails3] = true
76
+ options[:rails4] = true
77
+ end
78
+
79
+ opts.on "-5", "--rails5", "Force Rails 5 mode" do
80
+ options[:rails3] = true
81
+ options[:rails4] = true
82
+ options[:rails5] = true
83
+ end
84
+
85
+ opts.separator ""
86
+ opts.separator "Scanning options:"
87
+
88
+ opts.on "-A", "--run-all-checks", "Run all default and optional checks" do
89
+ options[:run_all_checks] = true
90
+ end
91
+
92
+ opts.on "-a", "--[no-]assume-routes", "Assume all controller methods are actions (Default)" do |assume|
93
+ options[:assume_all_routes] = assume
94
+ end
95
+
96
+ opts.on "-e", "--escape-html", "Escape HTML by default" do
97
+ options[:escape_html] = true
98
+ end
99
+
100
+ opts.on "--faster", "Faster, but less accurate scan" do
101
+ options[:ignore_ifs] = true
102
+ options[:skip_libs] = true
103
+ options[:disable_constant_tracking] = true
104
+ end
105
+
106
+ opts.on "--ignore-model-output", "Consider model attributes XSS-safe" do
107
+ options[:ignore_model_output] = true
108
+ end
109
+
110
+ opts.on "--ignore-protected", "Consider models with attr_protected safe" do
111
+ options[:ignore_attr_protected] = true
112
+ end
113
+
114
+ opts.on "--[no-]index-libs", "Add libraries to call index (Default)" do |index|
115
+ options[:index_libs] = index
116
+ end
117
+
118
+ opts.on "--interprocedural", "Process method calls to known methods" do
119
+ options[:interprocedural] = true
120
+ end
121
+
122
+ opts.on "--no-branching", "Disable flow sensitivity on conditionals" do
123
+ options[:ignore_ifs] = true
124
+ end
125
+
126
+ opts.on "--branch-limit LIMIT", Integer, "Limit depth of values in branches (-1 for no limit)" do |limit|
127
+ options[:branch_limit] = limit
128
+ end
129
+
130
+ opts.on "--parser-timeout SECONDS", Integer, "Set parse timeout (Default: 10)" do |timeout|
131
+ options[:parser_timeout] = timeout
132
+ end
133
+
134
+ opts.on "-r", "--report-direct", "Only report direct use of untrusted data" do |option|
135
+ options[:check_arguments] = !option
136
+ end
137
+
138
+ opts.on "-s", "--safe-methods meth1,meth2,etc", Array, "Set methods as safe for unescaped output in views" do |methods|
139
+ options[:safe_methods] ||= Set.new
140
+ options[:safe_methods].merge methods.map {|e| e.to_sym }
141
+ end
142
+
143
+ opts.on "--url-safe-methods method1,method2,etc", Array, "Do not warn of XSS if the link_to href parameter is wrapped in a safe method" do |methods|
144
+ options[:url_safe_methods] ||= Set.new
145
+ options[:url_safe_methods].merge methods.map {|e| e.to_sym }
146
+ end
147
+
148
+ opts.on "--skip-files file1,path2,etc", Array, "Skip processing of these files/directories. Directories are application relative and must end in \"#{File::SEPARATOR}\"" do |files|
149
+ options[:skip_files] ||= Set.new
150
+ options[:skip_files].merge files
151
+ end
152
+
153
+ opts.on "--only-files file1,path2,etc", Array, "Process only these files/directories. Directories are application relative and must end in \"#{File::SEPARATOR}\"" do |files|
154
+ options[:only_files] ||= Set.new
155
+ options[:only_files].merge files
156
+ end
157
+
158
+ opts.on "--skip-libs", "Skip processing lib directory" do
159
+ options[:skip_libs] = true
160
+ end
161
+
162
+ opts.on "--add-libs-path path1,path2,etc", Array, "An application relative lib directory (ex. app/mailers) to process" do |paths|
163
+ options[:additional_libs_path] ||= Set.new
164
+ options[:additional_libs_path].merge paths
165
+ end
166
+
167
+ opts.on "--add-engines-path path1,path2,etc", Array, "Include these engines in the scan" do |paths|
168
+ options[:engine_paths] ||= Set.new
169
+ options[:engine_paths].merge paths
170
+ end
171
+
172
+ opts.on "-t", "--test Check1,Check2,etc", Array, "Only run the specified checks" do |checks|
173
+ checks.each_with_index do |s, index|
174
+ if s[0,5] != "Check"
175
+ checks[index] = "Check" << s
176
+ end
177
+ end
178
+
179
+ options[:run_checks] ||= Set.new
180
+ options[:run_checks].merge checks
181
+ end
182
+
183
+ opts.on "-x", "--except Check1,Check2,etc", Array, "Skip the specified checks" do |skip|
184
+ skip.each do |s|
185
+ if s[0,5] != "Check"
186
+ s = "Check" << s
187
+ end
188
+
189
+ options[:skip_checks] ||= Set.new
190
+ options[:skip_checks] << s
191
+ end
192
+ end
193
+
194
+ opts.on "--add-checks-path path1,path2,etc", Array, "A directory containing additional out-of-tree checks to run" do |paths|
195
+ options[:additional_checks_path] ||= Set.new
196
+ options[:additional_checks_path].merge paths.map {|p| File.expand_path p}
197
+ end
198
+
199
+ opts.separator ""
200
+ opts.separator "Output options:"
201
+
202
+ opts.on "-d", "--debug", "Lots of output" do
203
+ options[:debug] = true
204
+ end
205
+
206
+ opts.on "-f",
207
+ "--format TYPE",
208
+ [:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table],
209
+ "Specify output formats. Default is text" do |type|
210
+
211
+ type = "s" if type == :text
212
+ options[:output_format] = ("to_" << type.to_s).to_sym
213
+ end
214
+
215
+ opts.on "--css-file CSSFile", "Specify CSS to use for HTML output" do |file|
216
+ options[:html_style] = File.expand_path file
217
+ end
218
+
219
+ opts.on "-i IGNOREFILE", "--ignore-config IGNOREFILE", "Use configuration to ignore warnings" do |file|
220
+ options[:ignore_file] = file
221
+ end
222
+
223
+ opts.on "-I", "--interactive-ignore", "Interactively ignore warnings" do
224
+ options[:interactive_ignore] = true
225
+ end
226
+
227
+ opts.on "-l", "--[no-]combine-locations", "Combine warning locations (Default)" do |combine|
228
+ options[:combine_locations] = combine
229
+ end
230
+
231
+ opts.on "--[no-]highlights", "Highlight user input in report" do |highlight|
232
+ options[:highlight_user_input] = highlight
233
+ end
234
+
235
+ opts.on "--[no-]color", "Use ANSI colors in report (Default)" do |color|
236
+ if color
237
+ options[:output_color] = :force
238
+ else
239
+ options[:output_color] = color
240
+ end
241
+ end
242
+
243
+ opts.on "-m", "--routes", "Report controller information" do
244
+ options[:report_routes] = true
245
+ end
246
+
247
+ opts.on "--message-limit LENGTH", "Limit message length in HTML report" do |limit|
248
+ options[:message_limit] = limit.to_i
249
+ end
250
+
251
+ opts.on "--[no-]pager", "Use pager for output to terminal (Default)" do |pager|
252
+ options[:pager] = pager
253
+ end
254
+
255
+ opts.on "--table-width WIDTH", "Limit table width in text report" do |width|
256
+ options[:table_width] = width.to_i
257
+ end
258
+
259
+ opts.on "-o", "--output FILE", "Specify files for output. Defaults to stdout. Multiple '-o's allowed" do |file|
260
+ options[:output_files] ||= []
261
+ options[:output_files].push(file)
262
+ end
263
+
264
+ opts.on "--[no-]separate-models", "Warn on each model without attr_accessible (Default)" do |separate|
265
+ options[:collapse_mass_assignment] = !separate
266
+ end
267
+
268
+ opts.on "--[no-]summary", "Only output summary of warnings" do |summary_only|
269
+ if summary_only
270
+ options[:summary_only] = :summary_only
271
+ else
272
+ options[:summary_only] = :no_summary
273
+ end
274
+ end
275
+
276
+ opts.on "--absolute-paths", "Output absolute file paths in reports" do
277
+ options[:absolute_paths] = true
278
+ end
279
+
280
+ opts.on "--github-repo USER/REPO[/PATH][@REF]", "Output links to GitHub in markdown and HTML reports using specified repo" do |repo|
281
+ options[:github_repo] = repo
282
+ end
283
+
284
+ opts.on "-w",
285
+ "--confidence-level LEVEL",
286
+ ["1", "2", "3"],
287
+ "Set minimal confidence level (1 - 3)" do |level|
288
+
289
+ options[:min_confidence] = 3 - level.to_i
290
+ end
291
+
292
+ opts.on "--compare FILE", "Compare the results of a previous Railroader scan (only JSON is supported)" do |file|
293
+ options[:previous_results_json] = File.expand_path(file)
294
+ end
295
+
296
+ opts.separator ""
297
+ opts.separator "Configuration files:"
298
+
299
+ opts.on "-c", "--config-file FILE", "Use specified configuration file" do |file|
300
+ options[:config_file] = File.expand_path(file)
301
+ end
302
+
303
+ opts.on "-C", "--create-config [FILE]", "Output configuration file based on options" do |file|
304
+ if file
305
+ options[:create_config] = file
306
+ else
307
+ options[:create_config] = true
308
+ end
309
+ end
310
+
311
+ opts.on "--allow-check-paths-in-config", "Allow loading checks from configuration file (Unsafe)" do
312
+ options[:allow_check_paths_in_config] = true
313
+ end
314
+
315
+ opts.separator ""
316
+
317
+ opts.on "-k", "--checks", "List all available vulnerability checks" do
318
+ options[:list_checks] = true
319
+ end
320
+
321
+ opts.on "--optional-checks", "List optional checks" do
322
+ options[:list_optional_checks] = true
323
+ end
324
+
325
+ opts.on "-v", "--version", "Show Railroader version" do
326
+ options[:show_version] = true
327
+ end
328
+
329
+ opts.on "--force-scan", "Scan application even if rails is not detected" do
330
+ options[:force_scan] = true
331
+ end
332
+
333
+ opts.on_tail "-h", "--help", "Display this message" do
334
+ options[:show_help] = true
335
+ end
336
+ end
337
+ end
338
+ end
339
+ end
@@ -0,0 +1,6 @@
1
+ Railroader.load_railroader_dependency 'erubis'
2
+
3
+ #Erubis processor which ignores any output which is plain text.
4
+ class Railroader::ScannerErubis < Erubis::Eruby
5
+ include Erubis::NoTextEnhancer
6
+ end
@@ -0,0 +1,48 @@
1
+ Railroader.load_railroader_dependency 'erubis'
2
+
3
+ #This is from the rails_xss plugin for Rails 2
4
+ class Railroader::Rails2XSSPluginErubis < ::Erubis::Eruby
5
+ def add_preamble(src)
6
+ #src << "@output_buffer = ActiveSupport::SafeBuffer.new;"
7
+ end
8
+
9
+ #This is different from rails_xss - fixes some line number issues
10
+ def add_text(src, text)
11
+ if text == "\n"
12
+ src << "\n"
13
+ elsif text.include? "\n"
14
+ lines = text.split("\n")
15
+ if text.match(/\n\z/)
16
+ lines.each do |line|
17
+ src << "@output_buffer.safe_concat('" << escape_text(line) << "');\n"
18
+ end
19
+ else
20
+ lines[0..-2].each do |line|
21
+ src << "@output_buffer.safe_concat('" << escape_text(line) << "');\n"
22
+ end
23
+
24
+ src << "@output_buffer.safe_concat('" << escape_text(lines.last) << "');"
25
+ end
26
+ else
27
+ src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
28
+ end
29
+ end
30
+
31
+ BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
32
+
33
+ def add_expr_literal(src, code)
34
+ if code =~ BLOCK_EXPR
35
+ src << "@output_buffer.safe_concat((" << $1 << ").to_s);"
36
+ else
37
+ src << '@output_buffer << ((' << code << ').to_s);'
38
+ end
39
+ end
40
+
41
+ def add_expr_escaped(src, code)
42
+ src << '@output_buffer << ' << escaped_expr(code) << ';'
43
+ end
44
+
45
+ def add_postamble(src)
46
+ #src << '@output_buffer.to_s'
47
+ end
48
+ end
@@ -0,0 +1,81 @@
1
+ Railroader.load_railroader_dependency 'erubis'
2
+
3
+ # This is from Rails 5 version of the Erubis handler
4
+ # https://github.com/rails/rails/blob/ec608107801b1e505db03ba76bae4a326a5804ca/actionview/lib/action_view/template/handlers/erb.rb#L7-L73
5
+ class Railroader::Rails3Erubis < ::Erubis::Eruby
6
+
7
+ def add_preamble(src)
8
+ @newline_pending = 0
9
+ src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
10
+ end
11
+
12
+ def add_text(src, text)
13
+ return if text.empty?
14
+
15
+ if text == "\n"
16
+ @newline_pending += 1
17
+ else
18
+ src << "@output_buffer.safe_append='"
19
+ src << "\n" * @newline_pending if @newline_pending > 0
20
+ src << escape_text(text)
21
+ src << "'.freeze;"
22
+
23
+ @newline_pending = 0
24
+ end
25
+ end
26
+
27
+ # Erubis toggles <%= and <%== behavior when escaping is enabled.
28
+ # We override to always treat <%== as escaped.
29
+ def add_expr(src, code, indicator)
30
+ case indicator
31
+ when '=='
32
+ add_expr_escaped(src, code)
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ BLOCK_EXPR = /\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
39
+
40
+ def add_expr_literal(src, code)
41
+ flush_newline_if_pending(src)
42
+ if code =~ BLOCK_EXPR
43
+ src << '@output_buffer.append= ' << code
44
+ else
45
+ src << '@output_buffer.append=(' << code << ');'
46
+ end
47
+ end
48
+
49
+ def add_expr_escaped(src, code)
50
+ flush_newline_if_pending(src)
51
+ if code =~ BLOCK_EXPR
52
+ src << "@output_buffer.safe_expr_append= " << code
53
+ else
54
+ src << "@output_buffer.safe_expr_append=(" << code << ");"
55
+ end
56
+ end
57
+
58
+ def add_stmt(src, code)
59
+ flush_newline_if_pending(src)
60
+ super
61
+ end
62
+
63
+ def add_postamble(src)
64
+ flush_newline_if_pending(src)
65
+ src << '@output_buffer.to_s'
66
+ end
67
+
68
+ def flush_newline_if_pending(src)
69
+ if @newline_pending > 0
70
+ src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;"
71
+ @newline_pending = 0
72
+ end
73
+ end
74
+
75
+ # This is borrowed from graphql's erb plugin:
76
+ # https://github.com/github/graphql-client/blob/51e76bd8d8b2ac0021d8fef7468b9a294e4bd6e8/lib/graphql/client/erubis.rb#L33-L38
77
+ def convert_input(src, input)
78
+ input = input.gsub(/<%graphql/, "<%#")
79
+ super(src, input)
80
+ end
81
+ end
@@ -0,0 +1,108 @@
1
+ module Railroader
2
+ class TemplateParser
3
+ include Railroader::Util
4
+ attr_reader :tracker
5
+ KNOWN_TEMPLATE_EXTENSIONS = /.*\.(erb|haml|rhtml|slim)$/
6
+
7
+ TemplateFile = Struct.new(:path, :ast, :name, :type)
8
+
9
+ def initialize tracker, file_parser
10
+ @tracker = tracker
11
+ @file_parser = file_parser
12
+ @file_parser.file_list[:templates] ||= []
13
+ end
14
+
15
+ def parse_template path, text
16
+ type = path.match(KNOWN_TEMPLATE_EXTENSIONS)[1].to_sym
17
+ type = :erb if type == :rhtml
18
+ name = template_path_to_name path
19
+ Railroader.debug "Parsing #{path}"
20
+
21
+ begin
22
+ src = case type
23
+ when :erb
24
+ type = :erubis if erubis?
25
+ parse_erb path, text
26
+ when :haml
27
+ parse_haml path, text
28
+ when :slim
29
+ parse_slim path, text
30
+ else
31
+ tracker.error "Unknown template type in #{path}"
32
+ nil
33
+ end
34
+
35
+ if src and ast = @file_parser.parse_ruby(src, path)
36
+ @file_parser.file_list[:templates] << TemplateFile.new(path, ast, name, type)
37
+ end
38
+ rescue Racc::ParseError => e
39
+ tracker.error e, "Could not parse #{path}"
40
+ rescue StandardError, LoadError => e
41
+ tracker.error e.exception(e.message + "\nWhile processing #{path}"), e.backtrace
42
+ end
43
+
44
+ nil
45
+ end
46
+
47
+ def parse_erb path, text
48
+ if tracker.config.escape_html?
49
+ if tracker.options[:rails3]
50
+ require 'railroader/parsers/rails3_erubis'
51
+ Railroader::Rails3Erubis.new(text, :filename => path).src
52
+ else
53
+ require 'railroader/parsers/rails2_xss_plugin_erubis'
54
+ Railroader::Rails2XSSPluginErubis.new(text, :filename => path).src
55
+ end
56
+ elsif tracker.config.erubis?
57
+ require 'railroader/parsers/rails2_erubis'
58
+ Railroader::ScannerErubis.new(text, :filename => path).src
59
+ else
60
+ require 'erb'
61
+ src = if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
62
+ ERB.new(text, trim_mode: path).src
63
+ else
64
+ ERB.new(text, nil, path).src
65
+ end
66
+ src.sub!(/^#.*\n/, '') if Railroader::Scanner::RUBY_1_9
67
+ src
68
+ end
69
+ end
70
+
71
+ def erubis?
72
+ tracker.config.escape_html? or
73
+ tracker.config.erubis?
74
+ end
75
+
76
+ def parse_haml path, text
77
+ Railroader.load_railroader_dependency 'haml'
78
+ Railroader.load_railroader_dependency 'sass'
79
+
80
+ Haml::Engine.new(text,
81
+ :filename => path,
82
+ :escape_html => tracker.config.escape_html?).precompiled.gsub(/([^\\])\\n/, '\1')
83
+ rescue Haml::Error => e
84
+ tracker.error e, ["While compiling HAML in #{path}"] << e.backtrace
85
+ nil
86
+ rescue Sass::SyntaxError => e
87
+ tracker.error e, "While processing #{path}"
88
+ nil
89
+ end
90
+
91
+ def parse_slim path, text
92
+ Railroader.load_railroader_dependency 'slim'
93
+
94
+ Slim::Template.new(path,
95
+ :disable_capture => true,
96
+ :generator => Temple::Generators::RailsOutputBuffer) { text }.precompiled_template
97
+ end
98
+
99
+ def self.parse_inline_erb tracker, text
100
+ fp = Railroader::FileParser.new(tracker, nil)
101
+ tp = self.new(tracker, fp)
102
+ src = tp.parse_erb '_inline_', text
103
+ type = tp.erubis? ? :erubis : :erb
104
+
105
+ return type, fp.parse_ruby(src, "_inline_")
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,102 @@
1
+ #Load all files in processors/
2
+ Dir.glob("#{File.expand_path(File.dirname(__FILE__))}/processors/*.rb").each { |f| require f.match(/railroader\/processors.*/)[0] }
3
+ require 'railroader/tracker'
4
+ require 'set'
5
+ require 'pathname'
6
+
7
+ module Railroader
8
+ #Makes calls to the appropriate processor.
9
+ #
10
+ #The ControllerProcessor, TemplateProcessor, and ModelProcessor will
11
+ #update the Tracker with information about what is parsed.
12
+ class Processor
13
+ include Util
14
+
15
+ def initialize(app_tree, options)
16
+ @app_tree = app_tree
17
+ @tracker = Tracker.new(@app_tree, self, options)
18
+ end
19
+
20
+ def tracked_events
21
+ @tracker
22
+ end
23
+
24
+ #Process configuration file source
25
+ def process_config src, file_name
26
+ ConfigProcessor.new(@tracker).process_config src, file_name
27
+ end
28
+
29
+ #Process Gemfile
30
+ def process_gems gem_files
31
+ GemProcessor.new(@tracker).process_gems gem_files
32
+ end
33
+
34
+ #Process route file source
35
+ def process_routes src
36
+ RoutesProcessor.new(@tracker).process_routes src
37
+ end
38
+
39
+ #Process controller source. +file_name+ is used for reporting
40
+ def process_controller src, file_name
41
+ if contains_class? src
42
+ ControllerProcessor.new(@app_tree, @tracker).process_controller src, file_name
43
+ else
44
+ LibraryProcessor.new(@tracker).process_library src, file_name
45
+ end
46
+ end
47
+
48
+ #Process variable aliasing in controller source and save it in the
49
+ #tracker.
50
+ def process_controller_alias name, src, only_method = nil, file = nil
51
+ ControllerAliasProcessor.new(@app_tree, @tracker, only_method).process_controller name, src, file
52
+ end
53
+
54
+ #Process a model source
55
+ def process_model src, file_name
56
+ result = ModelProcessor.new(@tracker).process_model src, file_name
57
+ AliasProcessor.new(@tracker).process result if result
58
+ end
59
+
60
+ #Process either an ERB or HAML template
61
+ def process_template name, src, type, called_from = nil, file_name = nil
62
+ case type
63
+ when :erb
64
+ result = ErbTemplateProcessor.new(@tracker, name, called_from, file_name).process src
65
+ when :haml
66
+ result = HamlTemplateProcessor.new(@tracker, name, called_from, file_name).process src
67
+ when :erubis
68
+ result = ErubisTemplateProcessor.new(@tracker, name, called_from, file_name).process src
69
+ when :slim
70
+ result = SlimTemplateProcessor.new(@tracker, name, called_from, file_name).process src
71
+ else
72
+ abort "Unknown template type: #{type} (#{name})"
73
+ end
74
+
75
+ #Each template which is rendered is stored separately
76
+ #with a new name.
77
+ if called_from
78
+ name = ("#{name}.#{called_from}").to_sym
79
+ end
80
+
81
+ @tracker.templates[name].src = result
82
+ @tracker.templates[name].type = type
83
+ end
84
+
85
+ #Process any calls to render() within a template
86
+ def process_template_alias template
87
+ TemplateAliasProcessor.new(@tracker, template).process_safely template.src
88
+ end
89
+
90
+ #Process source for initializing files
91
+ def process_initializer file_name, src
92
+ res = BaseProcessor.new(@tracker).process_file src, file_name
93
+ res = AliasProcessor.new(@tracker).process_safely res, nil, file_name
94
+ @tracker.initializers[Pathname.new(file_name).basename.to_s] = res
95
+ end
96
+
97
+ #Process source for a library file
98
+ def process_lib src, file_name
99
+ LibraryProcessor.new(@tracker).process_library src, file_name
100
+ end
101
+ end
102
+ end