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,112 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#sanitize and sanitize_css are vulnerable:
|
|
4
|
+
#CVE-2013-1855 and CVE-2013-1857
|
|
5
|
+
class Railroader::CheckSanitizeMethods < Railroader::BaseCheck
|
|
6
|
+
Railroader::Checks.add self
|
|
7
|
+
|
|
8
|
+
@description = "Checks for versions with vulnerable sanitize and sanitize_css"
|
|
9
|
+
|
|
10
|
+
def run_check
|
|
11
|
+
@fix_version = case
|
|
12
|
+
when version_between?('2.0.0', '2.3.17')
|
|
13
|
+
'2.3.18'
|
|
14
|
+
when version_between?('3.0.0', '3.0.99')
|
|
15
|
+
'3.2.13'
|
|
16
|
+
when version_between?('3.1.0', '3.1.11')
|
|
17
|
+
'3.1.12'
|
|
18
|
+
when version_between?('3.2.0', '3.2.12')
|
|
19
|
+
'3.2.13'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if @fix_version
|
|
23
|
+
check_cve_2013_1855
|
|
24
|
+
check_cve_2013_1857
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
if tracker.config.has_gem? :'rails-html-sanitizer'
|
|
28
|
+
check_rails_html_sanitizer
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
check_cve_2018_8048
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def check_cve_2013_1855
|
|
35
|
+
check_for_cve :sanitize_css, :CVE_2013_1855, "https://groups.google.com/d/msg/rubyonrails-security/4_QHo4BqnN8/_RrdfKk12I4J"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def check_cve_2013_1857
|
|
39
|
+
check_for_cve :sanitize, :CVE_2013_1857, "https://groups.google.com/d/msg/rubyonrails-security/zAAU7vGTPvI/1vZDWXqBuXgJ"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def check_for_cve method, code, link
|
|
43
|
+
tracker.find_call(:target => false, :method => method).each do |result|
|
|
44
|
+
next if duplicate? result
|
|
45
|
+
add_result result
|
|
46
|
+
|
|
47
|
+
message = "Rails #{rails_version} has a vulnerability in #{method}: upgrade to #{@fix_version} or patch"
|
|
48
|
+
|
|
49
|
+
warn :result => result,
|
|
50
|
+
:warning_type => "Cross-Site Scripting",
|
|
51
|
+
:warning_code => code,
|
|
52
|
+
:message => message,
|
|
53
|
+
:confidence => :high,
|
|
54
|
+
:link_path => link
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def check_rails_html_sanitizer
|
|
59
|
+
rhs_version = tracker.config.gem_version(:'rails-html-sanitizer')
|
|
60
|
+
|
|
61
|
+
if version_between? "1.0.0", "1.0.2", rhs_version
|
|
62
|
+
warn_sanitizer_cve "CVE-2015-7578", "https://groups.google.com/d/msg/rubyonrails-security/uh--W4TDwmI/JbvSRpdbFQAJ", "1.0.3"
|
|
63
|
+
warn_sanitizer_cve "CVE-2015-7580", "https://groups.google.com/d/msg/rubyonrails-security/uh--W4TDwmI/m_CVZtdbFQAJ", "1.0.3"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
if version_between? "1.0.0", "1.0.3", rhs_version
|
|
67
|
+
warn_sanitizer_cve "CVE-2018-3741", "https://groups.google.com/d/msg/rubyonrails-security/tP7W3kLc5u4/uDy2Br7xBgAJ", "1.0.4"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def check_cve_2018_8048
|
|
72
|
+
if loofah_vulnerable_cve_2018_8048?
|
|
73
|
+
message = "Loofah #{tracker.config.gem_version(:loofah)} is vulnerable (CVE-2018-8048). Upgrade to 2.1.2"
|
|
74
|
+
|
|
75
|
+
if tracker.find_call(:target => false, :method => :sanitize).any?
|
|
76
|
+
confidence = :high
|
|
77
|
+
else
|
|
78
|
+
confidence = :medium
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
warn :warning_type => "Cross-Site Scripting",
|
|
82
|
+
:warning_code => :CVE_2018_8048,
|
|
83
|
+
:message => message,
|
|
84
|
+
:gem_info => gemfile_or_environment(:loofah),
|
|
85
|
+
:confidence => confidence,
|
|
86
|
+
:link_path => "https://github.com/flavorjones/loofah/issues/144"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def loofah_vulnerable_cve_2018_8048?
|
|
91
|
+
loofah_version = tracker.config.gem_version(:loofah)
|
|
92
|
+
|
|
93
|
+
loofah_version and loofah_version < "2.1.2"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def warn_sanitizer_cve cve, link, upgrade_version
|
|
97
|
+
message = "rails-html-sanitizer #{tracker.config.gem_version(:'rails-html-sanitizer')} is vulnerable (#{cve}). Upgrade to #{upgrade_version}"
|
|
98
|
+
|
|
99
|
+
if tracker.find_call(:target => false, :method => :sanitize).any?
|
|
100
|
+
confidence = :high
|
|
101
|
+
else
|
|
102
|
+
confidence = :medium
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
warn :warning_type => "Cross-Site Scripting",
|
|
106
|
+
:warning_code => cve.tr('-', '_').to_sym,
|
|
107
|
+
:message => message,
|
|
108
|
+
:gem_info => gemfile_or_environment(:'rails-html-sanitizer'),
|
|
109
|
+
:confidence => confidence,
|
|
110
|
+
:link_path => link
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Railroader::CheckSecrets < Railroader::BaseCheck
|
|
4
|
+
Railroader::Checks.add_optional self
|
|
5
|
+
|
|
6
|
+
@description = "Checks for secrets stored in source code"
|
|
7
|
+
|
|
8
|
+
def run_check
|
|
9
|
+
check_constants
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def check_constants
|
|
13
|
+
@warned = Set.new
|
|
14
|
+
|
|
15
|
+
@tracker.constants.each do |constant|
|
|
16
|
+
name = constant.name_array.last
|
|
17
|
+
value = constant.value
|
|
18
|
+
|
|
19
|
+
if string? value and not value.value.empty? and looks_like_secret? name
|
|
20
|
+
match = [name, value, value.line]
|
|
21
|
+
|
|
22
|
+
unless @warned.include? match
|
|
23
|
+
@warned << match
|
|
24
|
+
|
|
25
|
+
warn :warning_code => :secret_in_source,
|
|
26
|
+
:warning_type => "Authentication",
|
|
27
|
+
:message => "Hardcoded value for #{name} in source code",
|
|
28
|
+
:confidence => :medium,
|
|
29
|
+
:file => constant.file,
|
|
30
|
+
:line => constant.line
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def looks_like_secret? name
|
|
37
|
+
# REST_AUTH_SITE_KEY is the pepper in Devise
|
|
38
|
+
name.match /password|secret|(rest_auth_site|api)_key$/i
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#Checks for CVE-2012-3463, unescaped input in :prompt option of select_tag:
|
|
4
|
+
#https://groups.google.com/d/topic/rubyonrails-security/fV3QUToSMSw/discussion
|
|
5
|
+
class Railroader::CheckSelectTag < Railroader::BaseCheck
|
|
6
|
+
Railroader::Checks.add self
|
|
7
|
+
|
|
8
|
+
@description = "Looks for unsafe uses of select_tag() in some versions of Rails 3.x"
|
|
9
|
+
|
|
10
|
+
def run_check
|
|
11
|
+
|
|
12
|
+
if version_between? "3.0.0", "3.0.16"
|
|
13
|
+
suggested_version = "3.0.17"
|
|
14
|
+
elsif version_between? "3.1.0", "3.1.7"
|
|
15
|
+
suggested_version = "3.1.8"
|
|
16
|
+
elsif version_between? "3.2.0", "3.2.7"
|
|
17
|
+
suggested_version = "3.2.8"
|
|
18
|
+
else
|
|
19
|
+
return
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@ignore_methods = Set[:escapeHTML, :escape_once, :h].merge tracker.options[:safe_methods]
|
|
23
|
+
|
|
24
|
+
@message = "Upgrade to Rails #{suggested_version}, #{rails_version} select_tag is vulnerable (CVE-2012-3463)"
|
|
25
|
+
|
|
26
|
+
calls = tracker.find_call(:target => nil, :method => :select_tag).select do |result|
|
|
27
|
+
result[:location][:type] == :template
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
calls.each do |result|
|
|
31
|
+
process_result result
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#Check if select_tag is called with user input in :prompt option
|
|
36
|
+
def process_result result
|
|
37
|
+
return unless original? result
|
|
38
|
+
|
|
39
|
+
#Only concerned if user input is supplied for :prompt option
|
|
40
|
+
last_arg = result[:call].last_arg
|
|
41
|
+
|
|
42
|
+
if hash? last_arg
|
|
43
|
+
prompt_option = hash_access last_arg, :prompt
|
|
44
|
+
|
|
45
|
+
if call? prompt_option and @ignore_methods.include? prompt_option.method
|
|
46
|
+
return
|
|
47
|
+
elsif sexp? prompt_option and input = include_user_input?(prompt_option)
|
|
48
|
+
|
|
49
|
+
warn :warning_type => "Cross-Site Scripting",
|
|
50
|
+
:warning_code => :CVE_2012_3463,
|
|
51
|
+
:result => result,
|
|
52
|
+
:message => @message,
|
|
53
|
+
:confidence => :high,
|
|
54
|
+
:user_input => input,
|
|
55
|
+
:link_path => "https://groups.google.com/d/topic/rubyonrails-security/fV3QUToSMSw/discussion"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#Checks for select() helper vulnerability in some versions of Rails 3
|
|
4
|
+
#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/9da0c515a6c4664
|
|
5
|
+
class Railroader::CheckSelectVulnerability < Railroader::BaseCheck
|
|
6
|
+
Railroader::Checks.add self
|
|
7
|
+
|
|
8
|
+
@description = "Looks for unsafe uses of select() helper"
|
|
9
|
+
|
|
10
|
+
def run_check
|
|
11
|
+
|
|
12
|
+
if lts_version? "2.3.18.7"
|
|
13
|
+
return
|
|
14
|
+
elsif version_between? "3.0.0", "3.0.11"
|
|
15
|
+
suggested_version = "3.0.12"
|
|
16
|
+
elsif version_between? "3.1.0", "3.1.3"
|
|
17
|
+
suggested_version = "3.1.4"
|
|
18
|
+
elsif version_between? "3.2.0", "3.2.1"
|
|
19
|
+
suggested_version = "3.2.2"
|
|
20
|
+
elsif version_between? "2.0.0", "2.3.14"
|
|
21
|
+
suggested_version = "3 or use options_for_select"
|
|
22
|
+
else
|
|
23
|
+
return
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
@message = "Upgrade to Rails #{suggested_version}, #{rails_version} select() helper is vulnerable"
|
|
27
|
+
|
|
28
|
+
calls = tracker.find_call(:target => nil, :method => :select).select do |result|
|
|
29
|
+
result[:location][:type] == :template
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
calls.each do |result|
|
|
33
|
+
process_result result
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def process_result result
|
|
38
|
+
return if duplicate? result
|
|
39
|
+
|
|
40
|
+
third_arg = result[:call].third_arg
|
|
41
|
+
|
|
42
|
+
#Check for user input in options parameter
|
|
43
|
+
if sexp? third_arg and include_user_input? third_arg
|
|
44
|
+
add_result result
|
|
45
|
+
|
|
46
|
+
if string_interp? third_arg
|
|
47
|
+
confidence = :medium
|
|
48
|
+
else
|
|
49
|
+
confidence = :weak
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
warn :template => result[:location][:template],
|
|
53
|
+
:warning_type => "Cross-Site Scripting",
|
|
54
|
+
:warning_code => :select_options_vuln,
|
|
55
|
+
:result => result,
|
|
56
|
+
:message => @message,
|
|
57
|
+
:confidence => confidence
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#Checks if user supplied data is passed to send
|
|
4
|
+
class Railroader::CheckSend < Railroader::BaseCheck
|
|
5
|
+
Railroader::Checks.add self
|
|
6
|
+
|
|
7
|
+
@description = "Check for unsafe use of Object#send"
|
|
8
|
+
|
|
9
|
+
def run_check
|
|
10
|
+
@send_methods = [:send, :try, :__send__, :public_send]
|
|
11
|
+
Railroader.debug("Finding instances of #send")
|
|
12
|
+
calls = tracker.find_call :methods => @send_methods, :nested => true
|
|
13
|
+
|
|
14
|
+
calls.each do |call|
|
|
15
|
+
process_result call
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def process_result result
|
|
20
|
+
return unless original? result
|
|
21
|
+
|
|
22
|
+
send_call = get_send result[:call]
|
|
23
|
+
process_call_args send_call
|
|
24
|
+
process send_call.target
|
|
25
|
+
|
|
26
|
+
if input = has_immediate_user_input?(send_call.first_arg)
|
|
27
|
+
warn :result => result,
|
|
28
|
+
:warning_type => "Dangerous Send",
|
|
29
|
+
:warning_code => :dangerous_send,
|
|
30
|
+
:message => "User controlled method execution",
|
|
31
|
+
:code => result[:call],
|
|
32
|
+
:user_input => input,
|
|
33
|
+
:confidence => :high
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Recursively check call chain for send call
|
|
38
|
+
def get_send exp
|
|
39
|
+
if call? exp
|
|
40
|
+
if @send_methods.include? exp.method
|
|
41
|
+
return exp
|
|
42
|
+
else
|
|
43
|
+
get_send exp.target
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'railroader/checks/check_file_access'
|
|
2
|
+
require 'railroader/processors/lib/processor_helper'
|
|
3
|
+
|
|
4
|
+
#Checks for user input in send_file()
|
|
5
|
+
class Railroader::CheckSendFile < Railroader::CheckFileAccess
|
|
6
|
+
Railroader::Checks.add self
|
|
7
|
+
|
|
8
|
+
@description = "Check for user input in uses of send_file"
|
|
9
|
+
|
|
10
|
+
def run_check
|
|
11
|
+
Railroader.debug "Finding all calls to send_file()"
|
|
12
|
+
|
|
13
|
+
methods = tracker.find_call :target => false, :method => :send_file
|
|
14
|
+
|
|
15
|
+
methods.each do |call|
|
|
16
|
+
process_result call
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Railroader::CheckSessionManipulation < Railroader::BaseCheck
|
|
4
|
+
Railroader::Checks.add self
|
|
5
|
+
|
|
6
|
+
@description = "Check for user input in session keys"
|
|
7
|
+
|
|
8
|
+
def run_check
|
|
9
|
+
tracker.find_call(:method => :[]=, :target => :session).each do |result|
|
|
10
|
+
process_result result
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def process_result result
|
|
15
|
+
return unless original? result
|
|
16
|
+
|
|
17
|
+
index = result[:call].first_arg
|
|
18
|
+
|
|
19
|
+
if input = has_immediate_user_input?(index)
|
|
20
|
+
if params? index
|
|
21
|
+
confidence = :high
|
|
22
|
+
else
|
|
23
|
+
confidence = :medium
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
warn :result => result,
|
|
27
|
+
:warning_type => "Session Manipulation",
|
|
28
|
+
:warning_code => :session_key_manipulation,
|
|
29
|
+
:message => "#{friendly_type_of(input).capitalize} used as key in session hash",
|
|
30
|
+
:code => result[:call],
|
|
31
|
+
:user_input => input,
|
|
32
|
+
:confidence => confidence
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#Checks for session key length and http_only settings
|
|
4
|
+
class Railroader::CheckSessionSettings < Railroader::BaseCheck
|
|
5
|
+
Railroader::Checks.add self
|
|
6
|
+
|
|
7
|
+
@description = "Checks for session key length and http_only settings"
|
|
8
|
+
|
|
9
|
+
def initialize *args
|
|
10
|
+
super
|
|
11
|
+
|
|
12
|
+
unless tracker.options[:rails3]
|
|
13
|
+
@session_settings = Sexp.new(:colon2, Sexp.new(:const, :ActionController), :Base)
|
|
14
|
+
else
|
|
15
|
+
@session_settings = nil
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def run_check
|
|
20
|
+
settings = tracker.config.session_settings
|
|
21
|
+
|
|
22
|
+
check_for_issues settings, @app_tree.expand_path("config/environment.rb")
|
|
23
|
+
|
|
24
|
+
["session_store.rb", "secret_token.rb"].each do |file|
|
|
25
|
+
if tracker.initializers[file] and not ignored? file
|
|
26
|
+
process tracker.initializers[file]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if tracker.options[:rails4]
|
|
31
|
+
check_secrets_yaml
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#Looks for ActionController::Base.session = { ... }
|
|
36
|
+
#in Rails 2.x apps
|
|
37
|
+
#
|
|
38
|
+
#and App::Application.config.secret_token =
|
|
39
|
+
#in Rails 3.x apps
|
|
40
|
+
#
|
|
41
|
+
#and App::Application.config.secret_key_base =
|
|
42
|
+
#in Rails 4.x apps
|
|
43
|
+
def process_attrasgn exp
|
|
44
|
+
if not tracker.options[:rails3] and exp.target == @session_settings and exp.method == :session=
|
|
45
|
+
check_for_issues exp.first_arg, @app_tree.expand_path("config/initializers/session_store.rb")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if tracker.options[:rails3] and settings_target?(exp.target) and
|
|
49
|
+
(exp.method == :secret_token= or exp.method == :secret_key_base=) and string? exp.first_arg
|
|
50
|
+
|
|
51
|
+
warn_about_secret_token exp.line, @app_tree.expand_path("config/initializers/secret_token.rb")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
exp
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
#Looks for Rails3::Application.config.session_store :cookie_store, { ... }
|
|
58
|
+
#in Rails 3.x apps
|
|
59
|
+
def process_call exp
|
|
60
|
+
if tracker.options[:rails3] and settings_target?(exp.target) and exp.method == :session_store
|
|
61
|
+
check_for_rails3_issues exp.second_arg, @app_tree.expand_path("config/initializers/session_store.rb")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
exp
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def settings_target? exp
|
|
70
|
+
call? exp and
|
|
71
|
+
exp.method == :config and
|
|
72
|
+
node_type? exp.target, :colon2 and
|
|
73
|
+
exp.target.rhs == :Application
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def check_for_issues settings, file
|
|
77
|
+
if settings and hash? settings
|
|
78
|
+
if value = (hash_access(settings, :session_http_only) ||
|
|
79
|
+
hash_access(settings, :http_only) ||
|
|
80
|
+
hash_access(settings, :httponly))
|
|
81
|
+
|
|
82
|
+
if false? value
|
|
83
|
+
warn_about_http_only value.line, file
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
if value = hash_access(settings, :secret)
|
|
88
|
+
if string? value
|
|
89
|
+
warn_about_secret_token value.line, file
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def check_for_rails3_issues settings, file
|
|
96
|
+
if settings and hash? settings
|
|
97
|
+
if value = hash_access(settings, :httponly)
|
|
98
|
+
if false? value
|
|
99
|
+
warn_about_http_only value.line, file
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
if value = hash_access(settings, :secure)
|
|
104
|
+
if false? value
|
|
105
|
+
warn_about_secure_only value.line, file
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def check_secrets_yaml
|
|
112
|
+
secrets_file = "config/secrets.yml"
|
|
113
|
+
|
|
114
|
+
if @app_tree.exists? secrets_file and not ignored? "secrets.yml" and not ignored? "config/*.yml"
|
|
115
|
+
yaml = @app_tree.read secrets_file
|
|
116
|
+
require 'date' # https://github.com/dtao/safe_yaml/issues/80
|
|
117
|
+
require 'safe_yaml/load'
|
|
118
|
+
begin
|
|
119
|
+
secrets = SafeYAML.load yaml
|
|
120
|
+
rescue Psych::SyntaxError, RuntimeError => e
|
|
121
|
+
Railroader.notify "[Notice] #{self.class}: Unable to parse `#{secrets_file}`"
|
|
122
|
+
Railroader.debug "Failed to parse #{secrets_file}: #{e.inspect}"
|
|
123
|
+
return
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
if secrets["production"] and secret = secrets["production"]["secret_key_base"]
|
|
127
|
+
unless secret.include? "<%="
|
|
128
|
+
line = yaml.lines.find_index { |l| l.include? secret } + 1
|
|
129
|
+
|
|
130
|
+
warn_about_secret_token line, @app_tree.expand_path(secrets_file)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def warn_about_http_only line, file
|
|
137
|
+
warn :warning_type => "Session Setting",
|
|
138
|
+
:warning_code => :http_cookies,
|
|
139
|
+
:message => "Session cookies should be set to HTTP only",
|
|
140
|
+
:confidence => :high,
|
|
141
|
+
:line => line,
|
|
142
|
+
:file => file
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def warn_about_secret_token line, file
|
|
147
|
+
warn :warning_type => "Session Setting",
|
|
148
|
+
:warning_code => :session_secret,
|
|
149
|
+
:message => "Session secret should not be included in version control",
|
|
150
|
+
:confidence => :high,
|
|
151
|
+
:line => line,
|
|
152
|
+
:file => file
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def warn_about_secure_only line, file
|
|
156
|
+
warn :warning_type => "Session Setting",
|
|
157
|
+
:warning_code => :secure_cookies,
|
|
158
|
+
:message => "Session cookie should be set to secure only",
|
|
159
|
+
:confidence => :high,
|
|
160
|
+
:line => line,
|
|
161
|
+
:file => file
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def ignored? file
|
|
165
|
+
[".", "config", "config/initializers"].each do |dir|
|
|
166
|
+
ignore_file = "#{dir}/.gitignore"
|
|
167
|
+
if @app_tree.exists? ignore_file
|
|
168
|
+
input = @app_tree.read(ignore_file)
|
|
169
|
+
|
|
170
|
+
return true if input.include? file
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
false
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Railroader::CheckSimpleFormat < Railroader::CheckCrossSiteScripting
|
|
4
|
+
Railroader::Checks.add self
|
|
5
|
+
|
|
6
|
+
@description = "Checks for simple_format XSS vulnerability (CVE-2013-6416) in certain versions"
|
|
7
|
+
|
|
8
|
+
def run_check
|
|
9
|
+
if version_between? "4.0.0", "4.0.1"
|
|
10
|
+
@inspect_arguments = true
|
|
11
|
+
@ignore_methods = Set[:h, :escapeHTML]
|
|
12
|
+
|
|
13
|
+
check_simple_format_usage
|
|
14
|
+
generic_warning unless @found_any
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def generic_warning
|
|
19
|
+
message = "Rails #{rails_version} has a vulnerability in simple_format (CVE-2013-6416). Upgrade to Rails version 4.0.2"
|
|
20
|
+
|
|
21
|
+
warn :warning_type => "Cross-Site Scripting",
|
|
22
|
+
:warning_code => :CVE_2013_6416,
|
|
23
|
+
:message => message,
|
|
24
|
+
:confidence => :medium,
|
|
25
|
+
:gem_info => gemfile_or_environment,
|
|
26
|
+
:link_path => "https://groups.google.com/d/msg/ruby-security-ann/5ZI1-H5OoIM/ZNq4FoR2GnIJ"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def check_simple_format_usage
|
|
30
|
+
tracker.find_call(:target => false, :method => :simple_format).each do |result|
|
|
31
|
+
@matched = false
|
|
32
|
+
process_call result[:call]
|
|
33
|
+
if @matched
|
|
34
|
+
warn_on_simple_format result, @matched
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def process_call exp
|
|
40
|
+
@mark = true
|
|
41
|
+
actually_process_call exp
|
|
42
|
+
exp
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def warn_on_simple_format result, match
|
|
46
|
+
return unless original? result
|
|
47
|
+
|
|
48
|
+
@found_any = true
|
|
49
|
+
|
|
50
|
+
warn :result => result,
|
|
51
|
+
:warning_type => "Cross-Site Scripting",
|
|
52
|
+
:warning_code => :CVE_2013_6416_call,
|
|
53
|
+
:message => "Values passed to simple_format are not safe in Rails #{rails_version}",
|
|
54
|
+
:confidence => :high,
|
|
55
|
+
:link_path => "https://groups.google.com/d/msg/ruby-security-ann/5ZI1-H5OoIM/ZNq4FoR2GnIJ",
|
|
56
|
+
:user_input => match
|
|
57
|
+
end
|
|
58
|
+
end
|