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,43 @@
1
+ require 'railroader/checks/base_check'
2
+
3
+ class Railroader::CheckPermitAttributes < Railroader::BaseCheck
4
+ Railroader::Checks.add self
5
+
6
+ @description = "Warn on potentially dangerous attributes whitelisted via permit"
7
+
8
+ SUSPICIOUS_KEYS = {
9
+ admin: :high,
10
+ account_id: :high,
11
+ role: :medium,
12
+ banned: :medium,
13
+ }
14
+
15
+ def run_check
16
+ tracker.find_call(:method => :permit).each do |result|
17
+ check_permit result
18
+ end
19
+ end
20
+
21
+ def check_permit result
22
+ return unless original? result
23
+
24
+ call = result[:call]
25
+
26
+ call.each_arg do |arg|
27
+ if symbol? arg
28
+ if SUSPICIOUS_KEYS.key? arg.value
29
+ warn_on_permit_key result, arg
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def warn_on_permit_key result, key, confidence = nil
36
+ warn :result => result,
37
+ :warning_type => "Mass Assignment",
38
+ :warning_code => :dangerous_permit_key,
39
+ :message => "Potentially dangerous key allowed for mass assignment",
40
+ :confidence => (confidence || SUSPICIOUS_KEYS[key.value]),
41
+ :user_input => key
42
+ end
43
+ end
@@ -0,0 +1,40 @@
1
+ require 'railroader/checks/base_check'
2
+
3
+ #Check for uses of quote_table_name in Rails versions before 2.3.13 and 3.0.10
4
+ #http://groups.google.com/group/rubyonrails-security/browse_thread/thread/6a1e473744bc389b
5
+ class Railroader::CheckQuoteTableName < Railroader::BaseCheck
6
+ Railroader::Checks.add self
7
+
8
+ @description = "Checks for quote_table_name vulnerability in versions before 2.3.14 and 3.0.10"
9
+
10
+ def run_check
11
+ if (version_between?('2.0.0', '2.3.13') or
12
+ version_between?('3.0.0', '3.0.9'))
13
+
14
+ if uses_quote_table_name?
15
+ confidence = :high
16
+ else
17
+ confidence = :medium
18
+ end
19
+
20
+ if rails_version =~ /^3/
21
+ message = "Versions before 3.0.10 have a vulnerability in quote_table_name: CVE-2011-2930"
22
+ else
23
+ message = "Versions before 2.3.14 have a vulnerability in quote_table_name: CVE-2011-2930"
24
+ end
25
+
26
+ warn :warning_type => "SQL Injection",
27
+ :warning_code => :CVE_2011_2930,
28
+ :message => message,
29
+ :confidence => confidence,
30
+ :gem_info => gemfile_or_environment,
31
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/ah5HN0S8OJs/discussion"
32
+ end
33
+ end
34
+
35
+ def uses_quote_table_name?
36
+ Railroader.debug "Finding calls to quote_table_name()"
37
+
38
+ not tracker.find_call(:target => false, :method => :quote_table_name).empty?
39
+ end
40
+ end
@@ -0,0 +1,256 @@
1
+ require 'railroader/checks/base_check'
2
+
3
+ #Reports any calls to +redirect_to+ which include parameters in the arguments.
4
+ #
5
+ #For example:
6
+ #
7
+ # redirect_to params.merge(:action => :elsewhere)
8
+ class Railroader::CheckRedirect < Railroader::BaseCheck
9
+ Railroader::Checks.add self
10
+
11
+ @description = "Looks for calls to redirect_to with user input as arguments"
12
+
13
+ def run_check
14
+ Railroader.debug "Finding calls to redirect_to()"
15
+
16
+ @model_find_calls = Set[:all, :create, :create!, :find, :find_by_sql, :first, :last, :new]
17
+
18
+ if tracker.options[:rails3]
19
+ @model_find_calls.merge [:from, :group, :having, :joins, :lock, :order, :reorder, :select, :where]
20
+ end
21
+
22
+ if version_between? "4.0.0", "9.9.9"
23
+ @model_find_calls.merge [:find_by, :find_by!, :take]
24
+ end
25
+
26
+ @tracker.find_call(:target => false, :method => :redirect_to).each do |res|
27
+ process_result res
28
+ end
29
+ end
30
+
31
+ def process_result result
32
+ return unless original? result
33
+
34
+ call = result[:call]
35
+ method = call.method
36
+
37
+ opt = call.first_arg
38
+
39
+ if method == :redirect_to and
40
+ not only_path?(call) and
41
+ not explicit_host?(opt) and
42
+ not slice_call?(opt) and
43
+ not safe_permit?(opt) and
44
+ res = include_user_input?(call)
45
+
46
+ if res.type == :immediate
47
+ confidence = :high
48
+ else
49
+ confidence = :weak
50
+ end
51
+
52
+ warn :result => result,
53
+ :warning_type => "Redirect",
54
+ :warning_code => :open_redirect,
55
+ :message => "Possible unprotected redirect",
56
+ :code => call,
57
+ :user_input => res,
58
+ :confidence => confidence
59
+ end
60
+ end
61
+
62
+ #Custom check for user input. First looks to see if the user input
63
+ #is being output directly. This is necessary because of tracker.options[:check_arguments]
64
+ #which can be used to enable/disable reporting output of method calls which use
65
+ #user input as arguments.
66
+ def include_user_input? call, immediate = :immediate
67
+ Railroader.debug "Checking if call includes user input"
68
+
69
+ arg = call.first_arg
70
+
71
+ # if the first argument is an array, rails assumes you are building a
72
+ # polymorphic route, which will never jump off-host
73
+ return false if array? arg
74
+
75
+ if tracker.options[:ignore_redirect_to_model]
76
+ if model_instance?(arg) or decorated_model?(arg)
77
+ return false
78
+ end
79
+ end
80
+
81
+ if res = has_immediate_model?(arg)
82
+ unless call? arg and arg.method.to_s =~ /_path/
83
+ return Match.new(immediate, res)
84
+ end
85
+ elsif call? arg
86
+ if request_value? arg
87
+ return Match.new(immediate, arg)
88
+ elsif request_value? arg.target
89
+ return Match.new(immediate, arg.target)
90
+ elsif arg.method == :url_for and include_user_input? arg
91
+ return Match.new(immediate, arg)
92
+ #Ignore helpers like some_model_url?
93
+ elsif arg.method.to_s =~ /_(url|path)\z/
94
+ return false
95
+ end
96
+ elsif request_value? arg
97
+ return Match.new(immediate, arg)
98
+ end
99
+
100
+ if tracker.options[:check_arguments] and call? arg
101
+ include_user_input? arg, false #I'm doubting if this is really necessary...
102
+ else
103
+ false
104
+ end
105
+ end
106
+
107
+ #Checks +redirect_to+ arguments for +only_path => true+ which essentially
108
+ #nullifies the danger posed by redirecting with user input
109
+ def only_path? call
110
+ arg = call.first_arg
111
+
112
+ if hash? arg
113
+ return has_only_path? arg
114
+ elsif call? arg and arg.method == :url_for
115
+ return check_url_for(arg)
116
+ elsif call? arg and hash? arg.first_arg and use_unsafe_hash_method? arg
117
+ return has_only_path? arg.first_arg
118
+ end
119
+
120
+ false
121
+ end
122
+
123
+ def use_unsafe_hash_method? arg
124
+ return call_has_param(arg, :to_unsafe_hash) || call_has_param(arg, :to_unsafe_h)
125
+ end
126
+
127
+ def call_has_param arg, key
128
+ if call? arg and call? arg.target
129
+ target = arg.target
130
+ method = target.method
131
+
132
+ node_type? target.target, :params and method == key
133
+ else
134
+ false
135
+ end
136
+ end
137
+
138
+ def has_only_path? arg
139
+ if value = hash_access(arg, :only_path)
140
+ return true if true?(value)
141
+ end
142
+
143
+ false
144
+ end
145
+
146
+ def explicit_host? arg
147
+ return unless sexp? arg
148
+
149
+ if hash? arg
150
+ if value = hash_access(arg, :host)
151
+ return !has_immediate_user_input?(value)
152
+ end
153
+ elsif call? arg
154
+ target = arg.target
155
+
156
+ if hash? target and value = hash_access(target, :host)
157
+ return !has_immediate_user_input?(value)
158
+ elsif call? arg
159
+ return explicit_host? target
160
+ end
161
+ end
162
+
163
+ false
164
+ end
165
+
166
+ #+url_for+ is only_path => true by default. This checks to see if it is
167
+ #set to false for some reason.
168
+ def check_url_for call
169
+ arg = call.first_arg
170
+
171
+ if hash? arg
172
+ if value = hash_access(arg, :only_path)
173
+ return false if false?(value)
174
+ end
175
+ end
176
+
177
+ true
178
+ end
179
+
180
+ #Returns true if exp is (probably) a model instance
181
+ def model_instance? exp
182
+ if node_type? exp, :or
183
+ model_instance? exp.lhs or model_instance? exp.rhs
184
+ elsif call? exp
185
+ if model_target? exp and
186
+ (@model_find_calls.include? exp.method or exp.method.to_s.match(/^find_by_/))
187
+ true
188
+ else
189
+ association?(exp.target, exp.method)
190
+ end
191
+ end
192
+ end
193
+
194
+ def model_target? exp
195
+ return false unless call? exp
196
+ model_name? exp.target or
197
+ friendly_model? exp.target or
198
+ model_target? exp.target
199
+ end
200
+
201
+ #Returns true if exp is (probably) a friendly model instance
202
+ #using the FriendlyId gem
203
+ def friendly_model? exp
204
+ call? exp and model_name? exp.target and exp.method == :friendly
205
+ end
206
+
207
+ #Returns true if exp is (probably) a decorated model instance
208
+ #using the Draper gem
209
+ def decorated_model? exp
210
+ if node_type? exp, :or
211
+ decorated_model? exp.lhs or decorated_model? exp.rhs
212
+ else
213
+ tracker.config.has_gem? :draper and
214
+ call? exp and
215
+ node_type?(exp.target, :const) and
216
+ exp.target.value.to_s.match(/Decorator$/) and
217
+ exp.method == :decorate
218
+ end
219
+ end
220
+
221
+ #Check if method is actually an association in a Model
222
+ def association? model_name, meth
223
+ if call? model_name
224
+ return association? model_name.target, meth
225
+ elsif model_name? model_name
226
+ model = tracker.models[class_name(model_name)]
227
+ else
228
+ return false
229
+ end
230
+
231
+ return false unless model
232
+
233
+ model.association? meth
234
+ end
235
+
236
+ def slice_call? exp
237
+ return unless call? exp
238
+ exp.method == :slice
239
+ end
240
+
241
+ DANGEROUS_KEYS = [:host, :subdomain, :domain, :port]
242
+
243
+ def safe_permit? exp
244
+ if call? exp and params? exp.target and exp.method == :permit
245
+ exp.each_arg do |opt|
246
+ if symbol? opt and DANGEROUS_KEYS.include? opt.value
247
+ return false
248
+ end
249
+ end
250
+
251
+ return true
252
+ end
253
+
254
+ false
255
+ end
256
+ end
@@ -0,0 +1,68 @@
1
+ require 'railroader/checks/base_check'
2
+
3
+ #This check looks for regexes that include user input.
4
+ class Railroader::CheckRegexDoS < Railroader::BaseCheck
5
+ Railroader::Checks.add self
6
+
7
+ ESCAPES = {
8
+ s(:const, :Regexp) => [
9
+ :escape,
10
+ :quote
11
+ ]
12
+ }
13
+
14
+ @description = "Searches regexes including user input"
15
+
16
+ #Process calls
17
+ def run_check
18
+ Railroader.debug "Finding dynamic regexes"
19
+ calls = tracker.find_call :method => [:railroader_regex_interp]
20
+
21
+ Railroader.debug "Processing dynamic regexes"
22
+ calls.each do |call|
23
+ process_result call
24
+ end
25
+ end
26
+
27
+ #Warns if regex includes user input
28
+ def process_result result
29
+ return unless original? result
30
+
31
+ call = result[:call]
32
+ components = call[1..-1]
33
+
34
+ components.any? do |component|
35
+ next unless sexp? component
36
+
37
+ if match = has_immediate_user_input?(component)
38
+ confidence = :high
39
+ elsif match = has_immediate_model?(component)
40
+ match = Match.new(:model, match)
41
+ confidence = :medium
42
+ elsif match = include_user_input?(component)
43
+ confidence = :weak
44
+ end
45
+
46
+ if match
47
+ message = "#{friendly_type_of(match).capitalize} used in regex"
48
+
49
+ warn :result => result,
50
+ :warning_type => "Denial of Service",
51
+ :warning_code => :regex_dos,
52
+ :message => message,
53
+ :confidence => confidence,
54
+ :user_input => match
55
+ end
56
+ end
57
+ end
58
+
59
+ def process_call(exp)
60
+ if escape_methods = ESCAPES[exp.target]
61
+ if escape_methods.include? exp.method
62
+ return exp
63
+ end
64
+ end
65
+
66
+ super
67
+ end
68
+ end
@@ -0,0 +1,97 @@
1
+ require 'railroader/checks/base_check'
2
+
3
+ #Check calls to +render()+ for dangerous values
4
+ class Railroader::CheckRender < Railroader::BaseCheck
5
+ Railroader::Checks.add self
6
+
7
+ @description = "Finds calls to render that might allow file access or code execution"
8
+
9
+ def run_check
10
+ tracker.find_call(:target => nil, :method => :render).each do |result|
11
+ process_render_result result
12
+ end
13
+ end
14
+
15
+ def process_render_result result
16
+ return unless node_type? result[:call], :render
17
+
18
+ case result[:call].render_type
19
+ when :partial, :template, :action, :file
20
+ check_for_rce(result) or
21
+ check_for_dynamic_path(result)
22
+ when :inline
23
+ when :js
24
+ when :json
25
+ when :text
26
+ when :update
27
+ when :xml
28
+ end
29
+ end
30
+
31
+ #Check if path to action or file is determined dynamically
32
+ def check_for_dynamic_path result
33
+ view = result[:call][2]
34
+
35
+ if sexp? view and original? result
36
+
37
+ if input = has_immediate_user_input?(view)
38
+ if string_interp? view
39
+ confidence = :medium
40
+ else
41
+ confidence = :high
42
+ end
43
+ elsif input = include_user_input?(view)
44
+ confidence = :weak
45
+ else
46
+ return
47
+ end
48
+
49
+ return if input.type == :model #skip models
50
+ return if safe_param? input.match
51
+
52
+ message = "Render path contains #{friendly_type_of input}"
53
+
54
+ warn :result => result,
55
+ :warning_type => "Dynamic Render Path",
56
+ :warning_code => :dynamic_render_path,
57
+ :message => message,
58
+ :user_input => input,
59
+ :confidence => confidence
60
+ end
61
+ end
62
+
63
+ def check_for_rce result
64
+ return unless version_between? "0.0.0", "3.2.22" or
65
+ version_between? "4.0.0", "4.1.14" or
66
+ version_between? "4.2.0", "4.2.5"
67
+
68
+
69
+ view = result[:call][2]
70
+ if sexp? view and not duplicate? result
71
+ if params? view
72
+ add_result result
73
+ return if safe_param? view
74
+
75
+ warn :result => result,
76
+ :warning_type => "Remote Code Execution",
77
+ :warning_code => :dynamic_render_path_rce,
78
+ :message => "Passing query parameters to render() is vulnerable in Rails #{rails_version} (CVE-2016-0752)",
79
+ :user_input => view,
80
+ :confidence => :high
81
+ end
82
+ end
83
+ end
84
+
85
+ def safe_param? exp
86
+ if params? exp and call? exp
87
+ method_name = exp.method
88
+
89
+ if method_name == :[]
90
+ arg = exp.first_arg
91
+ symbol? arg and [:controller, :action].include? arg.value
92
+ else
93
+ boolean_method? method_name
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,37 @@
1
+ require 'railroader/checks/base_check'
2
+
3
+ class Railroader::CheckRenderDoS < Railroader::BaseCheck
4
+ Railroader::Checks.add self
5
+
6
+ @description = "Warn about denial of service with render :text (CVE-2014-0082)"
7
+
8
+ def run_check
9
+ if version_between? "3.0.0", "3.0.20" or
10
+ version_between? "3.1.0", "3.1.12" or
11
+ version_between? "3.2.0", "3.2.16"
12
+
13
+ tracker.find_call(:target => nil, :method => :render).each do |result|
14
+ if text_render? result
15
+ warn_about_text_render
16
+ break
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ def text_render? result
23
+ node_type? result[:call], :render and
24
+ result[:call].render_type == :text
25
+ end
26
+
27
+ def warn_about_text_render
28
+ message = "Rails #{rails_version} has a denial of service vulnerability (CVE-2014-0082). Upgrade to Rails version 3.2.17"
29
+
30
+ warn :warning_type => "Denial of Service",
31
+ :warning_code => :CVE_2014_0082,
32
+ :message => message,
33
+ :confidence => :high,
34
+ :link_path => "https://groups.google.com/d/msg/rubyonrails-security/LMxO_3_eCuc/ozGBEhKaJbIJ",
35
+ :gem_info => gemfile_or_environment
36
+ end
37
+ end
@@ -0,0 +1,53 @@
1
+ class Railroader::CheckRenderInline < Railroader::CheckCrossSiteScripting
2
+ Railroader::Checks.add self
3
+
4
+ @description = "Checks for cross-site scripting in render calls"
5
+
6
+ def run_check
7
+ setup
8
+
9
+ tracker.find_call(:target => nil, :method => :render).each do |result|
10
+ check_render result
11
+ end
12
+ end
13
+
14
+ def check_render result
15
+ return unless original? result
16
+
17
+ call = result[:call]
18
+
19
+ if node_type? call, :render and
20
+ (call.render_type == :text or call.render_type == :inline)
21
+
22
+ unless call.render_type == :text and content_type_set? call[3]
23
+ render_value = call[2]
24
+
25
+ if input = has_immediate_user_input?(render_value)
26
+ warn :result => result,
27
+ :warning_type => "Cross-Site Scripting",
28
+ :warning_code => :cross_site_scripting_inline,
29
+ :message => "Unescaped #{friendly_type_of input} rendered inline",
30
+ :user_input => input,
31
+ :confidence => :high
32
+ elsif input = has_immediate_model?(render_value)
33
+ warn :result => result,
34
+ :warning_type => "Cross-Site Scripting",
35
+ :warning_code => :cross_site_scripting_inline,
36
+ :message => "Unescaped model attribute rendered inline",
37
+ :user_input => input,
38
+ :confidence => :medium
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ CONTENT_TYPES = ["text/html", "text/javascript", "application/javascript"]
45
+
46
+ def content_type_set? opts
47
+ if hash? opts
48
+ content_type = hash_access(opts, :content_type)
49
+
50
+ string? content_type and not CONTENT_TYPES.include? content_type.value
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,21 @@
1
+ require 'railroader/checks/base_check'
2
+
3
+ #Warn about response splitting in Rails versions before 2.3.13
4
+ #http://groups.google.com/group/rubyonrails-security/browse_thread/thread/6ffc93bde0298768
5
+ class Railroader::CheckResponseSplitting < Railroader::BaseCheck
6
+ Railroader::Checks.add self
7
+
8
+ @description = "Report response splitting in Rails 2.3.0 - 2.3.13"
9
+
10
+ def run_check
11
+ if version_between?('2.3.0', '2.3.13')
12
+
13
+ warn :warning_type => "Response Splitting",
14
+ :warning_code => :CVE_2011_3186,
15
+ :message => "Versions before 2.3.14 have a vulnerability content type handling allowing injection of headers: CVE-2011-3186",
16
+ :confidence => :medium,
17
+ :gem_info => gemfile_or_environment,
18
+ :link_path => "https://groups.google.com/d/topic/rubyonrails-security/b_yTveAph2g/discussion"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,42 @@
1
+ require 'railroader/checks/base_check'
2
+
3
+ class Railroader::CheckRouteDoS < Railroader::BaseCheck
4
+ Railroader::Checks.add self
5
+
6
+ @description = "Checks for route DoS (CVE-2015-7581)"
7
+
8
+ def run_check
9
+ fix_version = case
10
+ when version_between?("4.0.0", "4.1.14")
11
+ "4.1.14.1"
12
+ when version_between?("4.2.0", "4.2.5")
13
+ "4.2.5.1"
14
+ else
15
+ return
16
+ end
17
+
18
+ if controller_wildcards?
19
+ message = "Rails #{rails_version} has a denial of service vulnerability with :controller routes (CVE-2015-7581). Upgrade to Rails #{fix_version}"
20
+
21
+ warn :warning_type => "Denial of Service",
22
+ :warning_code => :CVE_2015_7581,
23
+ :message => message,
24
+ :confidence => :medium,
25
+ :gem_info => gemfile_or_environment,
26
+ :link_path => "https://groups.google.com/d/msg/rubyonrails-security/dthJ5wL69JE/YzPnFelbFQAJ"
27
+ end
28
+ end
29
+
30
+ def controller_wildcards?
31
+ tracker.routes.each do |name, actions|
32
+ if name == :':controllerController'
33
+ # awful hack for routes with :controller in them
34
+ return true
35
+ elsif string? actions and actions.value.include? ":controller"
36
+ return true
37
+ end
38
+ end
39
+
40
+ false
41
+ end
42
+ end
@@ -0,0 +1,31 @@
1
+ require 'railroader/checks/base_check'
2
+
3
+ #Check for unsafe manipulation of strings
4
+ #Right now this is just a version check for
5
+ #https://groups.google.com/group/rubyonrails-security/browse_thread/thread/edd28f1e3d04e913?pli=1
6
+ class Railroader::CheckSafeBufferManipulation < Railroader::BaseCheck
7
+ Railroader::Checks.add self
8
+
9
+ @description = "Check for Rails versions with SafeBuffer bug"
10
+
11
+ def run_check
12
+
13
+ if version_between? "3.0.0", "3.0.11"
14
+ suggested_version = "3.0.12"
15
+ elsif version_between? "3.1.0", "3.1.3"
16
+ suggested_version = "3.1.4"
17
+ elsif version_between? "3.2.0", "3.2.1"
18
+ suggested_version = "3.2.2"
19
+ else
20
+ return
21
+ end
22
+
23
+ message = "Rails #{rails_version} has a vulnerabilty in SafeBuffer. Upgrade to #{suggested_version} or apply patches."
24
+
25
+ warn :warning_type => "Cross-Site Scripting",
26
+ :warning_code => :safe_buffer_vuln,
27
+ :message => message,
28
+ :confidence => :medium,
29
+ :gem_info => gemfile_or_environment
30
+ end
31
+ end