railroader 4.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGES.md +1091 -0
- data/FEATURES +16 -0
- data/README.md +174 -0
- data/bin/railroader +8 -0
- data/lib/railroader/app_tree.rb +191 -0
- data/lib/railroader/call_index.rb +219 -0
- data/lib/railroader/checks/base_check.rb +505 -0
- data/lib/railroader/checks/check_basic_auth.rb +88 -0
- data/lib/railroader/checks/check_basic_auth_timing_attack.rb +33 -0
- data/lib/railroader/checks/check_content_tag.rb +200 -0
- data/lib/railroader/checks/check_create_with.rb +74 -0
- data/lib/railroader/checks/check_cross_site_scripting.rb +381 -0
- data/lib/railroader/checks/check_default_routes.rb +86 -0
- data/lib/railroader/checks/check_deserialize.rb +56 -0
- data/lib/railroader/checks/check_detailed_exceptions.rb +55 -0
- data/lib/railroader/checks/check_digest_dos.rb +38 -0
- data/lib/railroader/checks/check_divide_by_zero.rb +42 -0
- data/lib/railroader/checks/check_dynamic_finders.rb +48 -0
- data/lib/railroader/checks/check_escape_function.rb +21 -0
- data/lib/railroader/checks/check_evaluation.rb +35 -0
- data/lib/railroader/checks/check_execute.rb +189 -0
- data/lib/railroader/checks/check_file_access.rb +71 -0
- data/lib/railroader/checks/check_file_disclosure.rb +35 -0
- data/lib/railroader/checks/check_filter_skipping.rb +31 -0
- data/lib/railroader/checks/check_forgery_setting.rb +81 -0
- data/lib/railroader/checks/check_header_dos.rb +31 -0
- data/lib/railroader/checks/check_i18n_xss.rb +48 -0
- data/lib/railroader/checks/check_jruby_xml.rb +36 -0
- data/lib/railroader/checks/check_json_encoding.rb +47 -0
- data/lib/railroader/checks/check_json_parsing.rb +107 -0
- data/lib/railroader/checks/check_link_to.rb +132 -0
- data/lib/railroader/checks/check_link_to_href.rb +146 -0
- data/lib/railroader/checks/check_mail_to.rb +49 -0
- data/lib/railroader/checks/check_mass_assignment.rb +196 -0
- data/lib/railroader/checks/check_mime_type_dos.rb +39 -0
- data/lib/railroader/checks/check_model_attr_accessible.rb +55 -0
- data/lib/railroader/checks/check_model_attributes.rb +119 -0
- data/lib/railroader/checks/check_model_serialize.rb +67 -0
- data/lib/railroader/checks/check_nested_attributes.rb +38 -0
- data/lib/railroader/checks/check_nested_attributes_bypass.rb +58 -0
- data/lib/railroader/checks/check_number_to_currency.rb +74 -0
- data/lib/railroader/checks/check_permit_attributes.rb +43 -0
- data/lib/railroader/checks/check_quote_table_name.rb +40 -0
- data/lib/railroader/checks/check_redirect.rb +256 -0
- data/lib/railroader/checks/check_regex_dos.rb +68 -0
- data/lib/railroader/checks/check_render.rb +97 -0
- data/lib/railroader/checks/check_render_dos.rb +37 -0
- data/lib/railroader/checks/check_render_inline.rb +53 -0
- data/lib/railroader/checks/check_response_splitting.rb +21 -0
- data/lib/railroader/checks/check_route_dos.rb +42 -0
- data/lib/railroader/checks/check_safe_buffer_manipulation.rb +31 -0
- data/lib/railroader/checks/check_sanitize_methods.rb +112 -0
- data/lib/railroader/checks/check_secrets.rb +40 -0
- data/lib/railroader/checks/check_select_tag.rb +59 -0
- data/lib/railroader/checks/check_select_vulnerability.rb +60 -0
- data/lib/railroader/checks/check_send.rb +47 -0
- data/lib/railroader/checks/check_send_file.rb +19 -0
- data/lib/railroader/checks/check_session_manipulation.rb +35 -0
- data/lib/railroader/checks/check_session_settings.rb +176 -0
- data/lib/railroader/checks/check_simple_format.rb +58 -0
- data/lib/railroader/checks/check_single_quotes.rb +101 -0
- data/lib/railroader/checks/check_skip_before_filter.rb +60 -0
- data/lib/railroader/checks/check_sql.rb +700 -0
- data/lib/railroader/checks/check_sql_cves.rb +106 -0
- data/lib/railroader/checks/check_ssl_verify.rb +48 -0
- data/lib/railroader/checks/check_strip_tags.rb +89 -0
- data/lib/railroader/checks/check_symbol_dos.rb +71 -0
- data/lib/railroader/checks/check_symbol_dos_cve.rb +30 -0
- data/lib/railroader/checks/check_translate_bug.rb +45 -0
- data/lib/railroader/checks/check_unsafe_reflection.rb +50 -0
- data/lib/railroader/checks/check_unscoped_find.rb +57 -0
- data/lib/railroader/checks/check_validation_regex.rb +116 -0
- data/lib/railroader/checks/check_weak_hash.rb +148 -0
- data/lib/railroader/checks/check_without_protection.rb +80 -0
- data/lib/railroader/checks/check_xml_dos.rb +45 -0
- data/lib/railroader/checks/check_yaml_parsing.rb +121 -0
- data/lib/railroader/checks.rb +209 -0
- data/lib/railroader/codeclimate/engine_configuration.rb +97 -0
- data/lib/railroader/commandline.rb +179 -0
- data/lib/railroader/differ.rb +66 -0
- data/lib/railroader/file_parser.rb +54 -0
- data/lib/railroader/format/style.css +133 -0
- data/lib/railroader/options.rb +339 -0
- data/lib/railroader/parsers/rails2_erubis.rb +6 -0
- data/lib/railroader/parsers/rails2_xss_plugin_erubis.rb +48 -0
- data/lib/railroader/parsers/rails3_erubis.rb +81 -0
- data/lib/railroader/parsers/template_parser.rb +108 -0
- data/lib/railroader/processor.rb +102 -0
- data/lib/railroader/processors/alias_processor.rb +1229 -0
- data/lib/railroader/processors/base_processor.rb +295 -0
- data/lib/railroader/processors/config_processor.rb +14 -0
- data/lib/railroader/processors/controller_alias_processor.rb +278 -0
- data/lib/railroader/processors/controller_processor.rb +249 -0
- data/lib/railroader/processors/erb_template_processor.rb +77 -0
- data/lib/railroader/processors/erubis_template_processor.rb +92 -0
- data/lib/railroader/processors/gem_processor.rb +64 -0
- data/lib/railroader/processors/haml_template_processor.rb +191 -0
- data/lib/railroader/processors/lib/basic_processor.rb +37 -0
- data/lib/railroader/processors/lib/call_conversion_helper.rb +90 -0
- data/lib/railroader/processors/lib/find_all_calls.rb +224 -0
- data/lib/railroader/processors/lib/find_call.rb +183 -0
- data/lib/railroader/processors/lib/find_return_value.rb +166 -0
- data/lib/railroader/processors/lib/module_helper.rb +111 -0
- data/lib/railroader/processors/lib/processor_helper.rb +88 -0
- data/lib/railroader/processors/lib/rails2_config_processor.rb +145 -0
- data/lib/railroader/processors/lib/rails2_route_processor.rb +313 -0
- data/lib/railroader/processors/lib/rails3_config_processor.rb +132 -0
- data/lib/railroader/processors/lib/rails3_route_processor.rb +308 -0
- data/lib/railroader/processors/lib/render_helper.rb +181 -0
- data/lib/railroader/processors/lib/render_path.rb +107 -0
- data/lib/railroader/processors/lib/route_helper.rb +68 -0
- data/lib/railroader/processors/lib/safe_call_helper.rb +16 -0
- data/lib/railroader/processors/library_processor.rb +74 -0
- data/lib/railroader/processors/model_processor.rb +91 -0
- data/lib/railroader/processors/output_processor.rb +144 -0
- data/lib/railroader/processors/route_processor.rb +17 -0
- data/lib/railroader/processors/slim_template_processor.rb +111 -0
- data/lib/railroader/processors/template_alias_processor.rb +118 -0
- data/lib/railroader/processors/template_processor.rb +85 -0
- data/lib/railroader/report/config/remediation.yml +71 -0
- data/lib/railroader/report/ignore/config.rb +153 -0
- data/lib/railroader/report/ignore/interactive.rb +362 -0
- data/lib/railroader/report/pager.rb +112 -0
- data/lib/railroader/report/renderer.rb +24 -0
- data/lib/railroader/report/report_base.rb +292 -0
- data/lib/railroader/report/report_codeclimate.rb +79 -0
- data/lib/railroader/report/report_csv.rb +55 -0
- data/lib/railroader/report/report_hash.rb +23 -0
- data/lib/railroader/report/report_html.rb +216 -0
- data/lib/railroader/report/report_json.rb +45 -0
- data/lib/railroader/report/report_markdown.rb +107 -0
- data/lib/railroader/report/report_table.rb +117 -0
- data/lib/railroader/report/report_tabs.rb +17 -0
- data/lib/railroader/report/report_text.rb +198 -0
- data/lib/railroader/report/templates/controller_overview.html.erb +22 -0
- data/lib/railroader/report/templates/controller_warnings.html.erb +21 -0
- data/lib/railroader/report/templates/error_overview.html.erb +29 -0
- data/lib/railroader/report/templates/header.html.erb +58 -0
- data/lib/railroader/report/templates/ignored_warnings.html.erb +25 -0
- data/lib/railroader/report/templates/model_warnings.html.erb +21 -0
- data/lib/railroader/report/templates/overview.html.erb +38 -0
- data/lib/railroader/report/templates/security_warnings.html.erb +23 -0
- data/lib/railroader/report/templates/template_overview.html.erb +21 -0
- data/lib/railroader/report/templates/view_warnings.html.erb +34 -0
- data/lib/railroader/report/templates/warning_overview.html.erb +17 -0
- data/lib/railroader/report.rb +88 -0
- data/lib/railroader/rescanner.rb +483 -0
- data/lib/railroader/scanner.rb +321 -0
- data/lib/railroader/tracker/collection.rb +93 -0
- data/lib/railroader/tracker/config.rb +154 -0
- data/lib/railroader/tracker/constants.rb +171 -0
- data/lib/railroader/tracker/controller.rb +161 -0
- data/lib/railroader/tracker/library.rb +17 -0
- data/lib/railroader/tracker/model.rb +90 -0
- data/lib/railroader/tracker/template.rb +33 -0
- data/lib/railroader/tracker.rb +362 -0
- data/lib/railroader/util.rb +503 -0
- data/lib/railroader/version.rb +3 -0
- data/lib/railroader/warning.rb +294 -0
- data/lib/railroader/warning_codes.rb +117 -0
- data/lib/railroader.rb +544 -0
- data/lib/ruby_parser/bm_sexp.rb +626 -0
- data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
- metadata +386 -0
|
@@ -0,0 +1,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
|