railroader 4.3.4
Sign up to get free protection for your applications and to get access to all the features.
- 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,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,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
|