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,106 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Railroader::CheckSQLCVEs < Railroader::BaseCheck
|
|
4
|
+
Railroader::Checks.add self
|
|
5
|
+
|
|
6
|
+
@description = "Checks for several SQL CVEs"
|
|
7
|
+
|
|
8
|
+
def run_check
|
|
9
|
+
check_rails_versions_against_cve_issues
|
|
10
|
+
check_cve_2014_0080
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def check_rails_versions_against_cve_issues
|
|
14
|
+
issues = [
|
|
15
|
+
{
|
|
16
|
+
:cve => "CVE-2012-2660",
|
|
17
|
+
:versions => [%w[2.0.0 2.3.14 2.3.17], %w[3.0.0 3.0.12 3.0.13], %w[3.1.0 3.1.4 3.1.5], %w[3.2.0 3.2.3 3.2.4]],
|
|
18
|
+
:url => "https://groups.google.com/d/topic/rubyonrails-security/8SA-M3as7A8/discussion"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
:cve => "CVE-2012-2661",
|
|
22
|
+
:versions => [%w[3.0.0 3.0.12 3.0.13], %w[3.1.0 3.1.4 3.1.5], %w[3.2.0 3.2.3 3.2.5]],
|
|
23
|
+
:url => "https://groups.google.com/d/topic/rubyonrails-security/dUaiOOGWL1k/discussion"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
:cve => "CVE-2012-2695",
|
|
27
|
+
:versions => [%w[2.0.0 2.3.14 2.3.15], %w[3.0.0 3.0.13 3.0.14], %w[3.1.0 3.1.5 3.1.6], %w[3.2.0 3.2.5 3.2.6]],
|
|
28
|
+
:url => "https://groups.google.com/d/topic/rubyonrails-security/l4L0TEVAz1k/discussion"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
:cve => "CVE-2012-5664",
|
|
32
|
+
:versions => [%w[2.0.0 2.3.14 2.3.15], %w[3.0.0 3.0.17 3.0.18], %w[3.1.0 3.1.8 3.1.9], %w[3.2.0 3.2.9 3.2.18]],
|
|
33
|
+
:url => "https://groups.google.com/d/topic/rubyonrails-security/DCNTNp_qjFM/discussion"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
:cve => "CVE-2013-0155",
|
|
37
|
+
:versions => [%w[2.0.0 2.3.15 2.3.16], %w[3.0.0 3.0.18 3.0.19], %w[3.1.0 3.1.9 3.1.10], %w[3.2.0 3.2.10 3.2.11]],
|
|
38
|
+
:url => "https://groups.google.com/d/topic/rubyonrails-security/c7jT-EeN9eI/discussion"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
:cve => "CVE-2016-6317",
|
|
42
|
+
:versions => [%w[4.2.0 4.2.7.0 4.2.7.1]],
|
|
43
|
+
:url => "https://groups.google.com/d/msg/ruby-security-ann/WccgKSKiPZA/9DrsDVSoCgAJ"
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
unless lts_version? '2.3.18.6'
|
|
49
|
+
issues << {
|
|
50
|
+
:cve => "CVE-2013-6417",
|
|
51
|
+
:versions => [%w[2.0.0 3.2.15 3.2.16], %w[4.0.0 4.0.1 4.0.2]],
|
|
52
|
+
:url => "https://groups.google.com/d/msg/ruby-security-ann/niK4drpSHT4/g8JW8ZsayRkJ"
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
if tracker.config.has_gem? :pg
|
|
57
|
+
issues << {
|
|
58
|
+
:cve => "CVE-2014-3482",
|
|
59
|
+
:versions => [%w[2.0.0 2.9.9 3.2.19], %w[3.0.0 3.2.18 3.2.19], %w[4.0.0 4.0.6 4.0.7], %w[4.1.0 4.1.2 4.1.3]],
|
|
60
|
+
:url => "https://groups.google.com/d/msg/rubyonrails-security/wDxePLJGZdI/WP7EasCJTA4J"
|
|
61
|
+
} <<
|
|
62
|
+
{
|
|
63
|
+
:cve => "CVE-2014-3483",
|
|
64
|
+
:versions => [%w[2.0.0 2.9.9 3.2.19], %w[3.0.0 3.2.18 3.2.19], %w[4.0.0 4.0.6 4.0.7], %w[4.1.0 4.1.2 4.1.3]],
|
|
65
|
+
:url => "https://groups.google.com/d/msg/rubyonrails-security/wDxePLJGZdI/WP7EasCJTA4J" }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
issues.each do |cve_issue|
|
|
69
|
+
cve_warning_for cve_issue[:versions], cve_issue[:cve], cve_issue[:url]
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def cve_warning_for versions, cve, link
|
|
74
|
+
upgrade_version = upgrade_version? versions
|
|
75
|
+
return unless upgrade_version
|
|
76
|
+
|
|
77
|
+
code = cve.tr('-', '_').to_sym
|
|
78
|
+
|
|
79
|
+
warn :warning_type => 'SQL Injection',
|
|
80
|
+
:warning_code => code,
|
|
81
|
+
:message => "Rails #{rails_version} contains a SQL injection vulnerability (#{cve}). Upgrade to #{upgrade_version}",
|
|
82
|
+
:confidence => :high,
|
|
83
|
+
:gem_info => gemfile_or_environment,
|
|
84
|
+
:link_path => link
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def upgrade_version? versions
|
|
88
|
+
versions.each do |low, high, upgrade|
|
|
89
|
+
return upgrade if version_between? low, high
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
false
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def check_cve_2014_0080
|
|
96
|
+
return unless version_between? "4.0.0", "4.0.2" and
|
|
97
|
+
@tracker.config.has_gem? :pg
|
|
98
|
+
|
|
99
|
+
warn :warning_type => 'SQL Injection',
|
|
100
|
+
:warning_code => :CVE_2014_0080,
|
|
101
|
+
:message => "Rails #{rails_version} contains a SQL injection vulnerability (CVE-2014-0080) with PostgreSQL. Upgrade to 4.0.3",
|
|
102
|
+
:confidence => :high,
|
|
103
|
+
:gem_info => gemfile_or_environment(:pg),
|
|
104
|
+
:link_path => "https://groups.google.com/d/msg/rubyonrails-security/Wu96YkTUR6s/pPLBMZrlwvYJ"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
# Checks if verify_mode= is called with OpenSSL::SSL::VERIFY_NONE
|
|
4
|
+
|
|
5
|
+
class Railroader::CheckSSLVerify < Railroader::BaseCheck
|
|
6
|
+
Railroader::Checks.add self
|
|
7
|
+
|
|
8
|
+
SSL_VERIFY_NONE = s(:colon2, s(:colon2, s(:const, :OpenSSL), :SSL), :VERIFY_NONE)
|
|
9
|
+
|
|
10
|
+
@description = "Checks for OpenSSL::SSL::VERIFY_NONE"
|
|
11
|
+
|
|
12
|
+
def run_check
|
|
13
|
+
check_open_ssl_verify_none
|
|
14
|
+
check_http_start
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def check_open_ssl_verify_none
|
|
18
|
+
tracker.find_call(:method => :verify_mode=).each {|call| process_verify_mode_result(call) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def process_verify_mode_result result
|
|
22
|
+
if result[:call].last_arg == SSL_VERIFY_NONE
|
|
23
|
+
warn_about_ssl_verification_bypass result
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def check_http_start
|
|
28
|
+
tracker.find_call(:target => :'Net::HTTP', :method => :start).each { |call| process_http_start_result call }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def process_http_start_result result
|
|
32
|
+
arg = result[:call].last_arg
|
|
33
|
+
|
|
34
|
+
if hash? arg and hash_access(arg, :verify_mode) == SSL_VERIFY_NONE
|
|
35
|
+
warn_about_ssl_verification_bypass result
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def warn_about_ssl_verification_bypass result
|
|
40
|
+
return unless original? result
|
|
41
|
+
|
|
42
|
+
warn :result => result,
|
|
43
|
+
:warning_type => "SSL Verification Bypass",
|
|
44
|
+
:warning_code => :ssl_verification_bypass,
|
|
45
|
+
:message => "SSL certificate verification was bypassed",
|
|
46
|
+
:confidence => :high
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#Check for uses of strip_tags in Rails versions before 3.0.17, 3.1.8, 3.2.8 (including 2.3.x):
|
|
4
|
+
#https://groups.google.com/d/topic/rubyonrails-security/FgVEtBajcTY/discussion
|
|
5
|
+
#
|
|
6
|
+
#Check for uses of strip_tags in Rails versions before 2.3.13 and 3.0.10:
|
|
7
|
+
#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2b9130749b74ea12
|
|
8
|
+
#
|
|
9
|
+
#Check for user of strip_tags with rails-html-sanitizer 1.0.2:
|
|
10
|
+
#https://groups.google.com/d/msg/rubyonrails-security/OU9ugTZcbjc/PjEP46pbFQAJ
|
|
11
|
+
class Railroader::CheckStripTags < Railroader::BaseCheck
|
|
12
|
+
Railroader::Checks.add self
|
|
13
|
+
|
|
14
|
+
@description = "Report strip_tags vulnerabilities"
|
|
15
|
+
|
|
16
|
+
def run_check
|
|
17
|
+
if uses_strip_tags?
|
|
18
|
+
cve_2011_2931
|
|
19
|
+
cve_2012_3465
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
cve_2015_7579
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def cve_2011_2931
|
|
26
|
+
if version_between?('2.0.0', '2.3.12') or version_between?('3.0.0', '3.0.9')
|
|
27
|
+
if rails_version =~ /^3/
|
|
28
|
+
message = "Versions before 3.0.10 have a vulnerability in strip_tags (CVE-2011-2931)"
|
|
29
|
+
else
|
|
30
|
+
message = "Versions before 2.3.13 have a vulnerability in strip_tags (CVE-2011-2931)"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
warn :warning_type => "Cross-Site Scripting",
|
|
34
|
+
:warning_code => :CVE_2011_2931,
|
|
35
|
+
:message => message,
|
|
36
|
+
:gem_info => gemfile_or_environment,
|
|
37
|
+
:confidence => :high,
|
|
38
|
+
:link_path => "https://groups.google.com/d/topic/rubyonrails-security/K5EwdJt06hI/discussion"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def cve_2012_3465
|
|
43
|
+
case
|
|
44
|
+
when (version_between?('2.0.0', '2.3.14') and tracker.config.escape_html?)
|
|
45
|
+
message = "All Rails 2.x versions have a vulnerability in strip_tags (CVE-2012-3465)"
|
|
46
|
+
when version_between?('3.0.10', '3.0.16')
|
|
47
|
+
message = "Rails #{rails_version} has a vulnerability in strip_tags (CVE-2012-3465). Upgrade to 3.0.17"
|
|
48
|
+
when version_between?('3.1.0', '3.1.7')
|
|
49
|
+
message = "Rails #{rails_version} has a vulnerability in strip_tags (CVE-2012-3465). Upgrade to 3.1.8"
|
|
50
|
+
when version_between?('3.2.0', '3.2.7')
|
|
51
|
+
message = "Rails #{rails_version} has a vulnerability in strip_tags (CVE-2012-3465). Upgrade to 3.2.8"
|
|
52
|
+
else
|
|
53
|
+
return
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
warn :warning_type => "Cross-Site Scripting",
|
|
57
|
+
:warning_code => :CVE_2012_3465,
|
|
58
|
+
:message => message,
|
|
59
|
+
:confidence => :high,
|
|
60
|
+
:gem_info => gemfile_or_environment,
|
|
61
|
+
:link_path => "https://groups.google.com/d/topic/rubyonrails-security/FgVEtBajcTY/discussion"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def cve_2015_7579
|
|
65
|
+
if tracker.config.gem_version(:'rails-html-sanitizer') == '1.0.2'
|
|
66
|
+
if uses_strip_tags?
|
|
67
|
+
confidence = :high
|
|
68
|
+
else
|
|
69
|
+
confidence = :medium
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
message = "rails-html-sanitizer 1.0.2 is vulnerable (CVE-2015-7579). Upgrade to 1.0.3"
|
|
73
|
+
|
|
74
|
+
warn :warning_type => "Cross-Site Scripting",
|
|
75
|
+
:warning_code => :CVE_2015_7579,
|
|
76
|
+
:message => message,
|
|
77
|
+
:confidence => confidence,
|
|
78
|
+
:gem_info => gemfile_or_environment,
|
|
79
|
+
:link_path => "https://groups.google.com/d/msg/rubyonrails-security/OU9ugTZcbjc/PjEP46pbFQAJ"
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def uses_strip_tags?
|
|
85
|
+
Railroader.debug "Finding calls to strip_tags()"
|
|
86
|
+
|
|
87
|
+
not tracker.find_call(:target => false, :method => :strip_tags, :nested => true).empty?
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Railroader::CheckSymbolDoS < Railroader::BaseCheck
|
|
4
|
+
Railroader::Checks.add_optional self
|
|
5
|
+
|
|
6
|
+
UNSAFE_METHODS = [:to_sym, :literal_to_sym, :intern, :symbolize_keys, :symbolize_keys!]
|
|
7
|
+
|
|
8
|
+
@description = "Checks for symbol denial of service"
|
|
9
|
+
|
|
10
|
+
def run_check
|
|
11
|
+
return if rails_version and rails_version >= "5.0.0"
|
|
12
|
+
return if tracker.config.ruby_version >= "2.2"
|
|
13
|
+
|
|
14
|
+
tracker.find_call(:methods => UNSAFE_METHODS, :nested => true).each do |result|
|
|
15
|
+
check_unsafe_symbol_creation(result)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def check_unsafe_symbol_creation result
|
|
20
|
+
return unless original? result
|
|
21
|
+
|
|
22
|
+
call = result[:call]
|
|
23
|
+
|
|
24
|
+
if result[:method] == :literal_to_sym
|
|
25
|
+
args = call.select { |e| sexp? e }
|
|
26
|
+
else
|
|
27
|
+
args = [call.target]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if input = args.map{ |arg| has_immediate_user_input?(arg) }.compact.first
|
|
31
|
+
confidence = :high
|
|
32
|
+
elsif input = args.map{ |arg| include_user_input?(arg) }.compact.first
|
|
33
|
+
confidence = :medium
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if confidence
|
|
38
|
+
return if safe_parameter? input.match
|
|
39
|
+
return if symbolizing_attributes? input
|
|
40
|
+
|
|
41
|
+
message = "Symbol conversion from unsafe string (#{friendly_type_of input})"
|
|
42
|
+
|
|
43
|
+
warn :result => result,
|
|
44
|
+
:warning_type => "Denial of Service",
|
|
45
|
+
:warning_code => :unsafe_symbol_creation,
|
|
46
|
+
:message => message,
|
|
47
|
+
:user_input => input,
|
|
48
|
+
:confidence => confidence
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def safe_parameter? input
|
|
53
|
+
if call? input
|
|
54
|
+
if node_type? input.target, :params
|
|
55
|
+
input.method == :[] and
|
|
56
|
+
symbol? input.first_arg and
|
|
57
|
+
[:controller, :action].include? input.first_arg.value
|
|
58
|
+
else
|
|
59
|
+
safe_parameter? input.target
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
false
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def symbolizing_attributes? input
|
|
67
|
+
input.type == :model and
|
|
68
|
+
call? input.match and
|
|
69
|
+
input.match.method == :attributes
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Railroader::CheckSymbolDoSCVE < Railroader::BaseCheck
|
|
4
|
+
Railroader::Checks.add self
|
|
5
|
+
|
|
6
|
+
@description = "Checks for versions with ActiveRecord symbol denial of service vulnerability"
|
|
7
|
+
|
|
8
|
+
def run_check
|
|
9
|
+
fix_version = case
|
|
10
|
+
when version_between?('2.0.0', '2.3.17')
|
|
11
|
+
'2.3.18'
|
|
12
|
+
when version_between?('3.1.0', '3.1.11')
|
|
13
|
+
'3.1.12'
|
|
14
|
+
when version_between?('3.2.0', '3.2.12')
|
|
15
|
+
'3.2.13'
|
|
16
|
+
else
|
|
17
|
+
nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if fix_version && active_record_models.any?
|
|
21
|
+
warn :warning_type => "Denial of Service",
|
|
22
|
+
:warning_code => :CVE_2013_1854,
|
|
23
|
+
:message => "Rails #{rails_version} has a denial of service vulnerability in ActiveRecord: upgrade to #{fix_version} or patch",
|
|
24
|
+
:confidence => :medium,
|
|
25
|
+
:gem_info => gemfile_or_environment,
|
|
26
|
+
:link => "https://groups.google.com/d/msg/rubyonrails-security/jgJ4cjjS8FE/BGbHRxnDRTIJ"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#Check for vulnerability in translate() helper that allows cross-site scripting
|
|
4
|
+
class Railroader::CheckTranslateBug < Railroader::BaseCheck
|
|
5
|
+
Railroader::Checks.add self
|
|
6
|
+
|
|
7
|
+
@description = "Report XSS vulnerability in translate helper"
|
|
8
|
+
|
|
9
|
+
def run_check
|
|
10
|
+
return if lts_version? '2.3.18.6'
|
|
11
|
+
if (version_between?('2.3.0', '2.3.99') and tracker.config.escape_html?) or
|
|
12
|
+
version_between?('3.0.0', '3.0.10') or
|
|
13
|
+
version_between?('3.1.0', '3.1.1')
|
|
14
|
+
|
|
15
|
+
confidence = if uses_translate?
|
|
16
|
+
:high
|
|
17
|
+
else
|
|
18
|
+
:medium
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
description = "have a vulnerability in the translate helper with keys ending in _html"
|
|
22
|
+
|
|
23
|
+
message = if rails_version =~ /^3\.1/
|
|
24
|
+
"Versions before 3.1.2 #{description}."
|
|
25
|
+
elsif rails_version =~ /^3\.0/
|
|
26
|
+
"Versions before 3.0.11 #{description}."
|
|
27
|
+
else
|
|
28
|
+
"Rails 2.3.x using the rails_xss plugin #{description}."
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
warn :warning_type => "Cross-Site Scripting",
|
|
32
|
+
:warning_code => :translate_vuln,
|
|
33
|
+
:message => message,
|
|
34
|
+
:confidence => confidence,
|
|
35
|
+
:gem_info => gemfile_or_environment,
|
|
36
|
+
:link_path => "http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2b61d70fb73c7cc5"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def uses_translate?
|
|
41
|
+
Railroader.debug "Finding calls to translate() or t()"
|
|
42
|
+
|
|
43
|
+
tracker.find_call(:target => nil, :methods => [:t, :translate]).any?
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
# Checks for string interpolation and parameters in calls to
|
|
4
|
+
# String#constantize, String#safe_constantize, Module#const_get and Module#qualified_const_get.
|
|
5
|
+
#
|
|
6
|
+
# Exploit examples at: http://blog.conviso.com.br/exploiting-unsafe-reflection-in-rubyrails-applications/
|
|
7
|
+
class Railroader::CheckUnsafeReflection < Railroader::BaseCheck
|
|
8
|
+
Railroader::Checks.add self
|
|
9
|
+
|
|
10
|
+
@description = "Checks for unsafe reflection"
|
|
11
|
+
|
|
12
|
+
def run_check
|
|
13
|
+
reflection_methods = [:constantize, :safe_constantize, :const_get, :qualified_const_get]
|
|
14
|
+
|
|
15
|
+
tracker.find_call(:methods => reflection_methods, :nested => true).each do |result|
|
|
16
|
+
check_unsafe_reflection result
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def check_unsafe_reflection result
|
|
21
|
+
return unless original? result
|
|
22
|
+
|
|
23
|
+
call = result[:call]
|
|
24
|
+
method = call.method
|
|
25
|
+
|
|
26
|
+
case method
|
|
27
|
+
when :constantize, :safe_constantize
|
|
28
|
+
arg = call.target
|
|
29
|
+
else
|
|
30
|
+
arg = call.first_arg
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if input = has_immediate_user_input?(arg)
|
|
34
|
+
confidence = :high
|
|
35
|
+
elsif input = include_user_input?(arg)
|
|
36
|
+
confidence = :medium
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if confidence
|
|
40
|
+
message = "Unsafe reflection method #{method} called with #{friendly_type_of input}"
|
|
41
|
+
|
|
42
|
+
warn :result => result,
|
|
43
|
+
:warning_type => "Remote Code Execution",
|
|
44
|
+
:warning_code => :unsafe_constantize,
|
|
45
|
+
:message => message,
|
|
46
|
+
:user_input => input,
|
|
47
|
+
:confidence => confidence
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
# Checks for unscoped calls to models' #find and #find_by_id methods.
|
|
4
|
+
class Railroader::CheckUnscopedFind < Railroader::BaseCheck
|
|
5
|
+
Railroader::Checks.add_optional self
|
|
6
|
+
|
|
7
|
+
@description = "Check for unscoped ActiveRecord queries"
|
|
8
|
+
|
|
9
|
+
def run_check
|
|
10
|
+
Railroader.debug("Finding instances of #find on models with associations")
|
|
11
|
+
|
|
12
|
+
associated_model_names = active_record_models.keys.select do |name|
|
|
13
|
+
if belongs_to = active_record_models[name].associations[:belongs_to]
|
|
14
|
+
not optional_belongs_to? belongs_to
|
|
15
|
+
else
|
|
16
|
+
false
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
calls = tracker.find_call :method => [:find, :find_by_id, :find_by_id!],
|
|
21
|
+
:targets => associated_model_names
|
|
22
|
+
|
|
23
|
+
calls.each do |call|
|
|
24
|
+
process_result call
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def process_result result
|
|
29
|
+
return if duplicate? result or result[:call].original_line
|
|
30
|
+
|
|
31
|
+
# Not interested unless argument is user controlled.
|
|
32
|
+
inputs = result[:call].args.map { |arg| include_user_input?(arg) }
|
|
33
|
+
return unless input = inputs.compact.first
|
|
34
|
+
|
|
35
|
+
add_result result
|
|
36
|
+
|
|
37
|
+
warn :result => result,
|
|
38
|
+
:warning_type => "Unscoped Find",
|
|
39
|
+
:warning_code => :unscoped_find,
|
|
40
|
+
:message => "Unscoped call to #{result[:target]}##{result[:method]}",
|
|
41
|
+
:code => result[:call],
|
|
42
|
+
:confidence => :weak,
|
|
43
|
+
:user_input => input
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def optional_belongs_to? exp
|
|
47
|
+
return false unless exp.is_a? Array
|
|
48
|
+
|
|
49
|
+
exp.each do |e|
|
|
50
|
+
if hash? e and true? hash_access(e, :optional)
|
|
51
|
+
return true
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
false
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
require 'railroader/checks/base_check'
|
|
2
|
+
|
|
3
|
+
#Reports any calls to +validates_format_of+ which do not use +\A+ and +\z+
|
|
4
|
+
#as anchors in the given regular expression.
|
|
5
|
+
#
|
|
6
|
+
#For example:
|
|
7
|
+
#
|
|
8
|
+
# #Allows anything after new line
|
|
9
|
+
# validates_format_of :user_name, :with => /^\w+$/
|
|
10
|
+
class Railroader::CheckValidationRegex < Railroader::BaseCheck
|
|
11
|
+
Railroader::Checks.add self
|
|
12
|
+
|
|
13
|
+
@description = "Report uses of validates_format_of with improper anchors"
|
|
14
|
+
|
|
15
|
+
WITH = Sexp.new(:lit, :with)
|
|
16
|
+
FORMAT = Sexp.new(:lit, :format)
|
|
17
|
+
|
|
18
|
+
def run_check
|
|
19
|
+
active_record_models.each do |name, model|
|
|
20
|
+
@current_model = name
|
|
21
|
+
format_validations = model.options[:validates_format_of]
|
|
22
|
+
|
|
23
|
+
if format_validations
|
|
24
|
+
format_validations.each do |v|
|
|
25
|
+
process_validates_format_of v
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
validates = model.options[:validates]
|
|
30
|
+
|
|
31
|
+
if validates
|
|
32
|
+
validates.each do |v|
|
|
33
|
+
process_validates v
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
#Check validates_format_of
|
|
40
|
+
def process_validates_format_of validator
|
|
41
|
+
if value = hash_access(validator.last, WITH)
|
|
42
|
+
check_regex value, validator
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
#Check validates ..., :format => ...
|
|
47
|
+
def process_validates validator
|
|
48
|
+
hash_arg = validator.last
|
|
49
|
+
return unless hash? hash_arg
|
|
50
|
+
|
|
51
|
+
value = hash_access(hash_arg, FORMAT)
|
|
52
|
+
|
|
53
|
+
if hash? value
|
|
54
|
+
value = hash_access(value, WITH)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
if value
|
|
58
|
+
check_regex value, validator
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Match secure regexp without extended option
|
|
63
|
+
SECURE_REGEXP_PATTERN = %r{
|
|
64
|
+
\A
|
|
65
|
+
\\A
|
|
66
|
+
.*
|
|
67
|
+
\\[zZ]
|
|
68
|
+
\z
|
|
69
|
+
}x
|
|
70
|
+
|
|
71
|
+
# Match secure of regexp with extended option
|
|
72
|
+
EXTENDED_SECURE_REGEXP_PATTERN = %r{
|
|
73
|
+
\A
|
|
74
|
+
\s*
|
|
75
|
+
\\A
|
|
76
|
+
.*
|
|
77
|
+
\\[zZ]
|
|
78
|
+
\s*
|
|
79
|
+
\z
|
|
80
|
+
}mx
|
|
81
|
+
|
|
82
|
+
#Issue warning if the regular expression does not use
|
|
83
|
+
#+\A+ and +\z+
|
|
84
|
+
def check_regex value, validator
|
|
85
|
+
return unless regexp? value
|
|
86
|
+
|
|
87
|
+
regex = value.value
|
|
88
|
+
unless secure_regex?(regex)
|
|
89
|
+
warn :model => @current_model,
|
|
90
|
+
:warning_type => "Format Validation",
|
|
91
|
+
:warning_code => :validation_regex,
|
|
92
|
+
:message => "Insufficient validation for '#{get_name validator}' using #{regex.inspect}. Use \\A and \\z as anchors",
|
|
93
|
+
:line => value.line,
|
|
94
|
+
:confidence => :high
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
#Get the name of the attribute being validated.
|
|
99
|
+
def get_name validator
|
|
100
|
+
name = validator[1]
|
|
101
|
+
|
|
102
|
+
if sexp? name
|
|
103
|
+
name.value
|
|
104
|
+
else
|
|
105
|
+
name
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
|
|
111
|
+
def secure_regex?(regex)
|
|
112
|
+
extended_regex = Regexp::EXTENDED == regex.options & Regexp::EXTENDED
|
|
113
|
+
regex_pattern = extended_regex ? EXTENDED_SECURE_REGEXP_PATTERN : SECURE_REGEXP_PATTERN
|
|
114
|
+
regex_pattern =~ regex.source
|
|
115
|
+
end
|
|
116
|
+
end
|