railroader 4.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES.md +1091 -0
  3. data/FEATURES +16 -0
  4. data/README.md +174 -0
  5. data/bin/railroader +8 -0
  6. data/lib/railroader/app_tree.rb +191 -0
  7. data/lib/railroader/call_index.rb +219 -0
  8. data/lib/railroader/checks/base_check.rb +505 -0
  9. data/lib/railroader/checks/check_basic_auth.rb +88 -0
  10. data/lib/railroader/checks/check_basic_auth_timing_attack.rb +33 -0
  11. data/lib/railroader/checks/check_content_tag.rb +200 -0
  12. data/lib/railroader/checks/check_create_with.rb +74 -0
  13. data/lib/railroader/checks/check_cross_site_scripting.rb +381 -0
  14. data/lib/railroader/checks/check_default_routes.rb +86 -0
  15. data/lib/railroader/checks/check_deserialize.rb +56 -0
  16. data/lib/railroader/checks/check_detailed_exceptions.rb +55 -0
  17. data/lib/railroader/checks/check_digest_dos.rb +38 -0
  18. data/lib/railroader/checks/check_divide_by_zero.rb +42 -0
  19. data/lib/railroader/checks/check_dynamic_finders.rb +48 -0
  20. data/lib/railroader/checks/check_escape_function.rb +21 -0
  21. data/lib/railroader/checks/check_evaluation.rb +35 -0
  22. data/lib/railroader/checks/check_execute.rb +189 -0
  23. data/lib/railroader/checks/check_file_access.rb +71 -0
  24. data/lib/railroader/checks/check_file_disclosure.rb +35 -0
  25. data/lib/railroader/checks/check_filter_skipping.rb +31 -0
  26. data/lib/railroader/checks/check_forgery_setting.rb +81 -0
  27. data/lib/railroader/checks/check_header_dos.rb +31 -0
  28. data/lib/railroader/checks/check_i18n_xss.rb +48 -0
  29. data/lib/railroader/checks/check_jruby_xml.rb +36 -0
  30. data/lib/railroader/checks/check_json_encoding.rb +47 -0
  31. data/lib/railroader/checks/check_json_parsing.rb +107 -0
  32. data/lib/railroader/checks/check_link_to.rb +132 -0
  33. data/lib/railroader/checks/check_link_to_href.rb +146 -0
  34. data/lib/railroader/checks/check_mail_to.rb +49 -0
  35. data/lib/railroader/checks/check_mass_assignment.rb +196 -0
  36. data/lib/railroader/checks/check_mime_type_dos.rb +39 -0
  37. data/lib/railroader/checks/check_model_attr_accessible.rb +55 -0
  38. data/lib/railroader/checks/check_model_attributes.rb +119 -0
  39. data/lib/railroader/checks/check_model_serialize.rb +67 -0
  40. data/lib/railroader/checks/check_nested_attributes.rb +38 -0
  41. data/lib/railroader/checks/check_nested_attributes_bypass.rb +58 -0
  42. data/lib/railroader/checks/check_number_to_currency.rb +74 -0
  43. data/lib/railroader/checks/check_permit_attributes.rb +43 -0
  44. data/lib/railroader/checks/check_quote_table_name.rb +40 -0
  45. data/lib/railroader/checks/check_redirect.rb +256 -0
  46. data/lib/railroader/checks/check_regex_dos.rb +68 -0
  47. data/lib/railroader/checks/check_render.rb +97 -0
  48. data/lib/railroader/checks/check_render_dos.rb +37 -0
  49. data/lib/railroader/checks/check_render_inline.rb +53 -0
  50. data/lib/railroader/checks/check_response_splitting.rb +21 -0
  51. data/lib/railroader/checks/check_route_dos.rb +42 -0
  52. data/lib/railroader/checks/check_safe_buffer_manipulation.rb +31 -0
  53. data/lib/railroader/checks/check_sanitize_methods.rb +112 -0
  54. data/lib/railroader/checks/check_secrets.rb +40 -0
  55. data/lib/railroader/checks/check_select_tag.rb +59 -0
  56. data/lib/railroader/checks/check_select_vulnerability.rb +60 -0
  57. data/lib/railroader/checks/check_send.rb +47 -0
  58. data/lib/railroader/checks/check_send_file.rb +19 -0
  59. data/lib/railroader/checks/check_session_manipulation.rb +35 -0
  60. data/lib/railroader/checks/check_session_settings.rb +176 -0
  61. data/lib/railroader/checks/check_simple_format.rb +58 -0
  62. data/lib/railroader/checks/check_single_quotes.rb +101 -0
  63. data/lib/railroader/checks/check_skip_before_filter.rb +60 -0
  64. data/lib/railroader/checks/check_sql.rb +700 -0
  65. data/lib/railroader/checks/check_sql_cves.rb +106 -0
  66. data/lib/railroader/checks/check_ssl_verify.rb +48 -0
  67. data/lib/railroader/checks/check_strip_tags.rb +89 -0
  68. data/lib/railroader/checks/check_symbol_dos.rb +71 -0
  69. data/lib/railroader/checks/check_symbol_dos_cve.rb +30 -0
  70. data/lib/railroader/checks/check_translate_bug.rb +45 -0
  71. data/lib/railroader/checks/check_unsafe_reflection.rb +50 -0
  72. data/lib/railroader/checks/check_unscoped_find.rb +57 -0
  73. data/lib/railroader/checks/check_validation_regex.rb +116 -0
  74. data/lib/railroader/checks/check_weak_hash.rb +148 -0
  75. data/lib/railroader/checks/check_without_protection.rb +80 -0
  76. data/lib/railroader/checks/check_xml_dos.rb +45 -0
  77. data/lib/railroader/checks/check_yaml_parsing.rb +121 -0
  78. data/lib/railroader/checks.rb +209 -0
  79. data/lib/railroader/codeclimate/engine_configuration.rb +97 -0
  80. data/lib/railroader/commandline.rb +179 -0
  81. data/lib/railroader/differ.rb +66 -0
  82. data/lib/railroader/file_parser.rb +54 -0
  83. data/lib/railroader/format/style.css +133 -0
  84. data/lib/railroader/options.rb +339 -0
  85. data/lib/railroader/parsers/rails2_erubis.rb +6 -0
  86. data/lib/railroader/parsers/rails2_xss_plugin_erubis.rb +48 -0
  87. data/lib/railroader/parsers/rails3_erubis.rb +81 -0
  88. data/lib/railroader/parsers/template_parser.rb +108 -0
  89. data/lib/railroader/processor.rb +102 -0
  90. data/lib/railroader/processors/alias_processor.rb +1229 -0
  91. data/lib/railroader/processors/base_processor.rb +295 -0
  92. data/lib/railroader/processors/config_processor.rb +14 -0
  93. data/lib/railroader/processors/controller_alias_processor.rb +278 -0
  94. data/lib/railroader/processors/controller_processor.rb +249 -0
  95. data/lib/railroader/processors/erb_template_processor.rb +77 -0
  96. data/lib/railroader/processors/erubis_template_processor.rb +92 -0
  97. data/lib/railroader/processors/gem_processor.rb +64 -0
  98. data/lib/railroader/processors/haml_template_processor.rb +191 -0
  99. data/lib/railroader/processors/lib/basic_processor.rb +37 -0
  100. data/lib/railroader/processors/lib/call_conversion_helper.rb +90 -0
  101. data/lib/railroader/processors/lib/find_all_calls.rb +224 -0
  102. data/lib/railroader/processors/lib/find_call.rb +183 -0
  103. data/lib/railroader/processors/lib/find_return_value.rb +166 -0
  104. data/lib/railroader/processors/lib/module_helper.rb +111 -0
  105. data/lib/railroader/processors/lib/processor_helper.rb +88 -0
  106. data/lib/railroader/processors/lib/rails2_config_processor.rb +145 -0
  107. data/lib/railroader/processors/lib/rails2_route_processor.rb +313 -0
  108. data/lib/railroader/processors/lib/rails3_config_processor.rb +132 -0
  109. data/lib/railroader/processors/lib/rails3_route_processor.rb +308 -0
  110. data/lib/railroader/processors/lib/render_helper.rb +181 -0
  111. data/lib/railroader/processors/lib/render_path.rb +107 -0
  112. data/lib/railroader/processors/lib/route_helper.rb +68 -0
  113. data/lib/railroader/processors/lib/safe_call_helper.rb +16 -0
  114. data/lib/railroader/processors/library_processor.rb +74 -0
  115. data/lib/railroader/processors/model_processor.rb +91 -0
  116. data/lib/railroader/processors/output_processor.rb +144 -0
  117. data/lib/railroader/processors/route_processor.rb +17 -0
  118. data/lib/railroader/processors/slim_template_processor.rb +111 -0
  119. data/lib/railroader/processors/template_alias_processor.rb +118 -0
  120. data/lib/railroader/processors/template_processor.rb +85 -0
  121. data/lib/railroader/report/config/remediation.yml +71 -0
  122. data/lib/railroader/report/ignore/config.rb +153 -0
  123. data/lib/railroader/report/ignore/interactive.rb +362 -0
  124. data/lib/railroader/report/pager.rb +112 -0
  125. data/lib/railroader/report/renderer.rb +24 -0
  126. data/lib/railroader/report/report_base.rb +292 -0
  127. data/lib/railroader/report/report_codeclimate.rb +79 -0
  128. data/lib/railroader/report/report_csv.rb +55 -0
  129. data/lib/railroader/report/report_hash.rb +23 -0
  130. data/lib/railroader/report/report_html.rb +216 -0
  131. data/lib/railroader/report/report_json.rb +45 -0
  132. data/lib/railroader/report/report_markdown.rb +107 -0
  133. data/lib/railroader/report/report_table.rb +117 -0
  134. data/lib/railroader/report/report_tabs.rb +17 -0
  135. data/lib/railroader/report/report_text.rb +198 -0
  136. data/lib/railroader/report/templates/controller_overview.html.erb +22 -0
  137. data/lib/railroader/report/templates/controller_warnings.html.erb +21 -0
  138. data/lib/railroader/report/templates/error_overview.html.erb +29 -0
  139. data/lib/railroader/report/templates/header.html.erb +58 -0
  140. data/lib/railroader/report/templates/ignored_warnings.html.erb +25 -0
  141. data/lib/railroader/report/templates/model_warnings.html.erb +21 -0
  142. data/lib/railroader/report/templates/overview.html.erb +38 -0
  143. data/lib/railroader/report/templates/security_warnings.html.erb +23 -0
  144. data/lib/railroader/report/templates/template_overview.html.erb +21 -0
  145. data/lib/railroader/report/templates/view_warnings.html.erb +34 -0
  146. data/lib/railroader/report/templates/warning_overview.html.erb +17 -0
  147. data/lib/railroader/report.rb +88 -0
  148. data/lib/railroader/rescanner.rb +483 -0
  149. data/lib/railroader/scanner.rb +321 -0
  150. data/lib/railroader/tracker/collection.rb +93 -0
  151. data/lib/railroader/tracker/config.rb +154 -0
  152. data/lib/railroader/tracker/constants.rb +171 -0
  153. data/lib/railroader/tracker/controller.rb +161 -0
  154. data/lib/railroader/tracker/library.rb +17 -0
  155. data/lib/railroader/tracker/model.rb +90 -0
  156. data/lib/railroader/tracker/template.rb +33 -0
  157. data/lib/railroader/tracker.rb +362 -0
  158. data/lib/railroader/util.rb +503 -0
  159. data/lib/railroader/version.rb +3 -0
  160. data/lib/railroader/warning.rb +294 -0
  161. data/lib/railroader/warning_codes.rb +117 -0
  162. data/lib/railroader.rb +544 -0
  163. data/lib/ruby_parser/bm_sexp.rb +626 -0
  164. data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
  165. metadata +386 -0
@@ -0,0 +1,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