actionpack 3.2.19 → 4.2.11.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (244) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +412 -503
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +11 -294
  5. data/lib/abstract_controller/asset_paths.rb +2 -2
  6. data/lib/abstract_controller/base.rb +52 -18
  7. data/lib/abstract_controller/callbacks.rb +87 -89
  8. data/lib/abstract_controller/collector.rb +17 -3
  9. data/lib/abstract_controller/helpers.rb +41 -14
  10. data/lib/abstract_controller/logger.rb +1 -2
  11. data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
  12. data/lib/abstract_controller/rendering.rb +65 -118
  13. data/lib/abstract_controller/translation.rb +16 -1
  14. data/lib/abstract_controller/url_for.rb +7 -7
  15. data/lib/abstract_controller.rb +2 -10
  16. data/lib/action_controller/base.rb +61 -28
  17. data/lib/action_controller/caching/fragments.rb +30 -54
  18. data/lib/action_controller/caching.rb +38 -35
  19. data/lib/action_controller/log_subscriber.rb +35 -18
  20. data/lib/action_controller/metal/conditional_get.rb +103 -34
  21. data/lib/action_controller/metal/data_streaming.rb +20 -26
  22. data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
  23. data/lib/action_controller/metal/exceptions.rb +19 -6
  24. data/lib/action_controller/metal/flash.rb +41 -9
  25. data/lib/action_controller/metal/force_ssl.rb +70 -12
  26. data/lib/action_controller/metal/head.rb +30 -7
  27. data/lib/action_controller/metal/helpers.rb +11 -11
  28. data/lib/action_controller/metal/hide_actions.rb +0 -1
  29. data/lib/action_controller/metal/http_authentication.rb +140 -94
  30. data/lib/action_controller/metal/implicit_render.rb +1 -1
  31. data/lib/action_controller/metal/instrumentation.rb +11 -7
  32. data/lib/action_controller/metal/live.rb +328 -0
  33. data/lib/action_controller/metal/mime_responds.rb +161 -152
  34. data/lib/action_controller/metal/params_wrapper.rb +126 -81
  35. data/lib/action_controller/metal/rack_delegation.rb +10 -4
  36. data/lib/action_controller/metal/redirecting.rb +44 -41
  37. data/lib/action_controller/metal/renderers.rb +48 -19
  38. data/lib/action_controller/metal/rendering.rb +46 -11
  39. data/lib/action_controller/metal/request_forgery_protection.rb +250 -29
  40. data/lib/action_controller/metal/streaming.rb +30 -38
  41. data/lib/action_controller/metal/strong_parameters.rb +669 -0
  42. data/lib/action_controller/metal/testing.rb +12 -18
  43. data/lib/action_controller/metal/url_for.rb +31 -29
  44. data/lib/action_controller/metal.rb +31 -40
  45. data/lib/action_controller/model_naming.rb +12 -0
  46. data/lib/action_controller/railtie.rb +38 -18
  47. data/lib/action_controller/railties/helpers.rb +22 -0
  48. data/lib/action_controller/test_case.rb +359 -173
  49. data/lib/action_controller.rb +9 -16
  50. data/lib/action_dispatch/http/cache.rb +64 -11
  51. data/lib/action_dispatch/http/filter_parameters.rb +20 -10
  52. data/lib/action_dispatch/http/filter_redirect.rb +38 -0
  53. data/lib/action_dispatch/http/headers.rb +85 -17
  54. data/lib/action_dispatch/http/mime_negotiation.rb +55 -5
  55. data/lib/action_dispatch/http/mime_type.rb +167 -114
  56. data/lib/action_dispatch/http/mime_types.rb +2 -1
  57. data/lib/action_dispatch/http/parameter_filter.rb +44 -46
  58. data/lib/action_dispatch/http/parameters.rb +30 -46
  59. data/lib/action_dispatch/http/rack_cache.rb +2 -3
  60. data/lib/action_dispatch/http/request.rb +108 -45
  61. data/lib/action_dispatch/http/response.rb +247 -48
  62. data/lib/action_dispatch/http/upload.rb +60 -29
  63. data/lib/action_dispatch/http/url.rb +135 -45
  64. data/lib/action_dispatch/journey/backwards.rb +5 -0
  65. data/lib/action_dispatch/journey/formatter.rb +166 -0
  66. data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
  67. data/lib/action_dispatch/journey/gtg/simulator.rb +47 -0
  68. data/lib/action_dispatch/journey/gtg/transition_table.rb +157 -0
  69. data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
  70. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  71. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  72. data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
  73. data/lib/action_dispatch/journey/nodes/node.rb +128 -0
  74. data/lib/action_dispatch/journey/parser.rb +198 -0
  75. data/lib/action_dispatch/journey/parser.y +49 -0
  76. data/lib/action_dispatch/journey/parser_extras.rb +23 -0
  77. data/lib/action_dispatch/journey/path/pattern.rb +193 -0
  78. data/lib/action_dispatch/journey/route.rb +125 -0
  79. data/lib/action_dispatch/journey/router/strexp.rb +27 -0
  80. data/lib/action_dispatch/journey/router/utils.rb +93 -0
  81. data/lib/action_dispatch/journey/router.rb +144 -0
  82. data/lib/action_dispatch/journey/routes.rb +80 -0
  83. data/lib/action_dispatch/journey/scanner.rb +61 -0
  84. data/lib/action_dispatch/journey/visitors.rb +221 -0
  85. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  86. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  87. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  88. data/lib/action_dispatch/journey.rb +5 -0
  89. data/lib/action_dispatch/middleware/callbacks.rb +16 -11
  90. data/lib/action_dispatch/middleware/cookies.rb +346 -125
  91. data/lib/action_dispatch/middleware/debug_exceptions.rb +52 -24
  92. data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -9
  93. data/lib/action_dispatch/middleware/flash.rb +85 -72
  94. data/lib/action_dispatch/middleware/params_parser.rb +16 -31
  95. data/lib/action_dispatch/middleware/public_exceptions.rb +39 -14
  96. data/lib/action_dispatch/middleware/reloader.rb +16 -7
  97. data/lib/action_dispatch/middleware/remote_ip.rb +132 -40
  98. data/lib/action_dispatch/middleware/request_id.rb +3 -7
  99. data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
  100. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  101. data/lib/action_dispatch/middleware/session/cookie_store.rb +84 -29
  102. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
  103. data/lib/action_dispatch/middleware/show_exceptions.rb +15 -44
  104. data/lib/action_dispatch/middleware/ssl.rb +72 -0
  105. data/lib/action_dispatch/middleware/stack.rb +6 -1
  106. data/lib/action_dispatch/middleware/static.rb +80 -23
  107. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +34 -0
  108. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  109. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +27 -0
  110. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  111. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  112. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  113. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  114. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +133 -5
  115. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  116. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  123. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  124. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  125. data/lib/action_dispatch/railtie.rb +19 -6
  126. data/lib/action_dispatch/request/session.rb +193 -0
  127. data/lib/action_dispatch/request/utils.rb +35 -0
  128. data/lib/action_dispatch/routing/endpoint.rb +10 -0
  129. data/lib/action_dispatch/routing/inspector.rb +234 -0
  130. data/lib/action_dispatch/routing/mapper.rb +897 -436
  131. data/lib/action_dispatch/routing/polymorphic_routes.rb +213 -92
  132. data/lib/action_dispatch/routing/redirection.rb +97 -37
  133. data/lib/action_dispatch/routing/route_set.rb +432 -239
  134. data/lib/action_dispatch/routing/routes_proxy.rb +7 -4
  135. data/lib/action_dispatch/routing/url_for.rb +63 -34
  136. data/lib/action_dispatch/routing.rb +57 -89
  137. data/lib/action_dispatch/testing/assertions/dom.rb +2 -36
  138. data/lib/action_dispatch/testing/assertions/response.rb +24 -38
  139. data/lib/action_dispatch/testing/assertions/routing.rb +55 -54
  140. data/lib/action_dispatch/testing/assertions/selector.rb +2 -434
  141. data/lib/action_dispatch/testing/assertions/tag.rb +2 -137
  142. data/lib/action_dispatch/testing/assertions.rb +11 -7
  143. data/lib/action_dispatch/testing/integration.rb +88 -72
  144. data/lib/action_dispatch/testing/test_process.rb +9 -6
  145. data/lib/action_dispatch/testing/test_request.rb +13 -9
  146. data/lib/action_dispatch/testing/test_response.rb +1 -5
  147. data/lib/action_dispatch.rb +24 -21
  148. data/lib/action_pack/gem_version.rb +15 -0
  149. data/lib/action_pack/version.rb +5 -7
  150. data/lib/action_pack.rb +1 -1
  151. metadata +181 -292
  152. data/lib/abstract_controller/layouts.rb +0 -423
  153. data/lib/abstract_controller/view_paths.rb +0 -96
  154. data/lib/action_controller/caching/actions.rb +0 -185
  155. data/lib/action_controller/caching/pages.rb +0 -187
  156. data/lib/action_controller/caching/sweeping.rb +0 -97
  157. data/lib/action_controller/deprecated/integration_test.rb +0 -2
  158. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  159. data/lib/action_controller/deprecated.rb +0 -3
  160. data/lib/action_controller/metal/compatibility.rb +0 -65
  161. data/lib/action_controller/metal/responder.rb +0 -286
  162. data/lib/action_controller/metal/session_management.rb +0 -14
  163. data/lib/action_controller/railties/paths.rb +0 -25
  164. data/lib/action_controller/record_identifier.rb +0 -85
  165. data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
  166. data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
  167. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
  168. data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
  169. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
  170. data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
  171. data/lib/action_controller/vendor/html-scanner.rb +0 -20
  172. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  173. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  174. data/lib/action_dispatch/middleware/head.rb +0 -18
  175. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  176. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
  177. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
  178. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
  179. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
  180. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
  181. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
  182. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
  183. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  184. data/lib/action_view/asset_paths.rb +0 -142
  185. data/lib/action_view/base.rb +0 -220
  186. data/lib/action_view/buffers.rb +0 -43
  187. data/lib/action_view/context.rb +0 -36
  188. data/lib/action_view/flows.rb +0 -79
  189. data/lib/action_view/helpers/active_model_helper.rb +0 -50
  190. data/lib/action_view/helpers/asset_paths.rb +0 -7
  191. data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
  192. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  193. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  194. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  195. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  196. data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
  197. data/lib/action_view/helpers/cache_helper.rb +0 -64
  198. data/lib/action_view/helpers/capture_helper.rb +0 -203
  199. data/lib/action_view/helpers/controller_helper.rb +0 -25
  200. data/lib/action_view/helpers/csrf_helper.rb +0 -32
  201. data/lib/action_view/helpers/date_helper.rb +0 -1062
  202. data/lib/action_view/helpers/debug_helper.rb +0 -40
  203. data/lib/action_view/helpers/form_helper.rb +0 -1486
  204. data/lib/action_view/helpers/form_options_helper.rb +0 -658
  205. data/lib/action_view/helpers/form_tag_helper.rb +0 -685
  206. data/lib/action_view/helpers/javascript_helper.rb +0 -110
  207. data/lib/action_view/helpers/number_helper.rb +0 -622
  208. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  209. data/lib/action_view/helpers/record_tag_helper.rb +0 -111
  210. data/lib/action_view/helpers/rendering_helper.rb +0 -90
  211. data/lib/action_view/helpers/sanitize_helper.rb +0 -259
  212. data/lib/action_view/helpers/tag_helper.rb +0 -160
  213. data/lib/action_view/helpers/text_helper.rb +0 -426
  214. data/lib/action_view/helpers/translation_helper.rb +0 -91
  215. data/lib/action_view/helpers/url_helper.rb +0 -693
  216. data/lib/action_view/helpers.rb +0 -60
  217. data/lib/action_view/locale/en.yml +0 -160
  218. data/lib/action_view/log_subscriber.rb +0 -28
  219. data/lib/action_view/lookup_context.rb +0 -254
  220. data/lib/action_view/path_set.rb +0 -89
  221. data/lib/action_view/railtie.rb +0 -55
  222. data/lib/action_view/renderer/abstract_renderer.rb +0 -41
  223. data/lib/action_view/renderer/partial_renderer.rb +0 -415
  224. data/lib/action_view/renderer/renderer.rb +0 -54
  225. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
  226. data/lib/action_view/renderer/template_renderer.rb +0 -94
  227. data/lib/action_view/template/error.rb +0 -128
  228. data/lib/action_view/template/handlers/builder.rb +0 -26
  229. data/lib/action_view/template/handlers/erb.rb +0 -125
  230. data/lib/action_view/template/handlers.rb +0 -50
  231. data/lib/action_view/template/resolver.rb +0 -272
  232. data/lib/action_view/template/text.rb +0 -30
  233. data/lib/action_view/template.rb +0 -337
  234. data/lib/action_view/test_case.rb +0 -245
  235. data/lib/action_view/testing/resolvers.rb +0 -50
  236. data/lib/action_view.rb +0 -84
  237. data/lib/sprockets/assets.rake +0 -99
  238. data/lib/sprockets/bootstrap.rb +0 -37
  239. data/lib/sprockets/compressors.rb +0 -83
  240. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  241. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  242. data/lib/sprockets/helpers.rb +0 -6
  243. data/lib/sprockets/railtie.rb +0 -62
  244. data/lib/sprockets/static_compiler.rb +0 -56
@@ -2,35 +2,56 @@ module ActionController
2
2
  module Rendering
3
3
  extend ActiveSupport::Concern
4
4
 
5
- include AbstractController::Rendering
5
+ RENDER_FORMATS_IN_PRIORITY = [:body, :text, :plain, :html]
6
6
 
7
7
  # Before processing, set the request formats in current controller formats.
8
8
  def process_action(*) #:nodoc:
9
- self.formats = request.formats.map { |x| x.ref }
9
+ self.formats = request.formats.map(&:ref).compact
10
10
  super
11
11
  end
12
12
 
13
13
  # Check for double render errors and set the content_type after rendering.
14
14
  def render(*args) #:nodoc:
15
- raise ::AbstractController::DoubleRenderError if response_body
15
+ raise ::AbstractController::DoubleRenderError if self.response_body
16
16
  super
17
- self.content_type ||= Mime[lookup_context.rendered_format].to_s
18
- response_body
19
17
  end
20
18
 
21
19
  # Overwrite render_to_string because body can now be set to a rack body.
22
20
  def render_to_string(*)
23
- if self.response_body = super
21
+ result = super
22
+ if result.respond_to?(:each)
24
23
  string = ""
25
- response_body.each { |r| string << r }
24
+ result.each { |r| string << r }
26
25
  string
26
+ else
27
+ result
27
28
  end
28
- ensure
29
- self.response_body = nil
29
+ end
30
+
31
+ def render_to_body(options = {})
32
+ super || _render_in_priorities(options) || ' '
30
33
  end
31
34
 
32
35
  private
33
36
 
37
+ def _render_in_priorities(options)
38
+ RENDER_FORMATS_IN_PRIORITY.each do |format|
39
+ return options[format] if options.key?(format)
40
+ end
41
+
42
+ nil
43
+ end
44
+
45
+ def _process_format(format, options = {})
46
+ super
47
+
48
+ if options[:plain]
49
+ self.content_type = Mime::TEXT
50
+ else
51
+ self.content_type ||= format.to_s
52
+ end
53
+ end
54
+
34
55
  # Normalize arguments by catching blocks and setting them on :update.
35
56
  def _normalize_args(action=nil, options={}, &blk) #:nodoc:
36
57
  options = super
@@ -40,8 +61,14 @@ module ActionController
40
61
 
41
62
  # Normalize both text and status options.
42
63
  def _normalize_options(options) #:nodoc:
43
- if options.key?(:text) && options[:text].respond_to?(:to_text)
44
- options[:text] = options[:text].to_text
64
+ _normalize_text(options)
65
+
66
+ if options[:html]
67
+ options[:html] = ERB::Util.html_escape(options[:html])
68
+ end
69
+
70
+ if options.delete(:nothing)
71
+ options[:body] = nil
45
72
  end
46
73
 
47
74
  if options[:status]
@@ -51,6 +78,14 @@ module ActionController
51
78
  super
52
79
  end
53
80
 
81
+ def _normalize_text(options)
82
+ RENDER_FORMATS_IN_PRIORITY.each do |format|
83
+ if options.key?(format) && options[format].respond_to?(:to_text)
84
+ options[format] = options[format].to_text
85
+ end
86
+ end
87
+ end
88
+
54
89
  # Process controller specific options, as status, content-type and location.
55
90
  def _process_options(options) #:nodoc:
56
91
  status, content_type, location = options.values_at(:status, :content_type, :location)
@@ -1,27 +1,51 @@
1
- require 'active_support/core_ext/class/attribute'
1
+ require 'rack/session/abstract/id'
2
2
  require 'action_controller/metal/exceptions'
3
+ require 'active_support/security_utils'
3
4
 
4
5
  module ActionController #:nodoc:
5
6
  class InvalidAuthenticityToken < ActionControllerError #:nodoc:
6
7
  end
7
8
 
9
+ class InvalidCrossOriginRequest < ActionControllerError #:nodoc:
10
+ end
11
+
8
12
  # Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
9
- # by including a token in the rendered html for your application. This token is
13
+ # by including a token in the rendered HTML for your application. This token is
10
14
  # stored as a random string in the session, to which an attacker does not have
11
15
  # access. When a request reaches your application, \Rails verifies the received
12
16
  # token with the token in the session. Only HTML and JavaScript requests are checked,
13
17
  # so this will not protect your XML API (presumably you'll have a different
14
- # authentication scheme there anyway). Also, GET requests are not protected as these
15
- # should be idempotent.
18
+ # authentication scheme there anyway).
19
+ #
20
+ # GET requests are not protected since they don't have side effects like writing
21
+ # to the database and don't leak sensitive information. JavaScript requests are
22
+ # an exception: a third-party site can use a <script> tag to reference a JavaScript
23
+ # URL on your site. When your JavaScript response loads on their site, it executes.
24
+ # With carefully crafted JavaScript on their end, sensitive data in your JavaScript
25
+ # response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or
26
+ # Ajax) requests are allowed to make GET requests for JavaScript responses.
27
+ #
28
+ # It's important to remember that XML or JSON requests are also affected and if
29
+ # you're building an API you'll need something like:
30
+ #
31
+ # class ApplicationController < ActionController::Base
32
+ # protect_from_forgery
33
+ # skip_before_action :verify_authenticity_token, if: :json_request?
34
+ #
35
+ # protected
36
+ #
37
+ # def json_request?
38
+ # request.format.json?
39
+ # end
40
+ # end
16
41
  #
17
42
  # CSRF protection is turned on with the <tt>protect_from_forgery</tt> method,
18
43
  # which checks the token and resets the session if it doesn't match what was expected.
19
44
  # A call to this method is generated for new \Rails applications by default.
20
- # You can customize the error message by editing public/422.html.
21
45
  #
22
46
  # The token parameter is named <tt>authenticity_token</tt> by default. The name and
23
47
  # value of this token must be added to every layout that renders forms by including
24
- # <tt>csrf_meta_tags</tt> in the html +head+.
48
+ # <tt>csrf_meta_tags</tt> in the HTML +head+.
25
49
  #
26
50
  # Learn more about CSRF attacks and securing your application in the
27
51
  # {Ruby on Rails Security Guide}[http://guides.rubyonrails.org/security.html].
@@ -37,68 +61,264 @@ module ActionController #:nodoc:
37
61
  config_accessor :request_forgery_protection_token
38
62
  self.request_forgery_protection_token ||= :authenticity_token
39
63
 
64
+ # Holds the class which implements the request forgery protection.
65
+ config_accessor :forgery_protection_strategy
66
+ self.forgery_protection_strategy = nil
67
+
40
68
  # Controls whether request forgery protection is turned on or not. Turned off by default only in test mode.
41
69
  config_accessor :allow_forgery_protection
42
70
  self.allow_forgery_protection = true if allow_forgery_protection.nil?
43
71
 
72
+ # Controls whether a CSRF failure logs a warning. On by default.
73
+ config_accessor :log_warning_on_csrf_failure
74
+ self.log_warning_on_csrf_failure = true
75
+
44
76
  helper_method :form_authenticity_token
45
77
  helper_method :protect_against_forgery?
46
78
  end
47
79
 
48
80
  module ClassMethods
49
- # Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
81
+ # Turn on request forgery protection. Bear in mind that GET and HEAD requests are not checked.
50
82
  #
51
- # Example:
83
+ # class ApplicationController < ActionController::Base
84
+ # protect_from_forgery
85
+ # end
52
86
  #
53
87
  # class FooController < ApplicationController
54
- # protect_from_forgery :except => :index
55
- #
56
- # You can disable csrf protection on controller-by-controller basis:
88
+ # protect_from_forgery except: :index
57
89
  #
58
- # skip_before_filter :verify_authenticity_token
59
- #
60
- # It can also be disabled for specific controller actions:
61
- #
62
- # skip_before_filter :verify_authenticity_token, :except => [:create]
90
+ # You can disable CSRF protection on controller by skipping the verification before_action:
91
+ # skip_before_action :verify_authenticity_token
63
92
  #
64
93
  # Valid Options:
65
94
  #
66
- # * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
95
+ # * <tt>:only/:except</tt> - Passed to the <tt>before_action</tt> call. Set which actions are verified.
96
+ # * <tt>:with</tt> - Set the method to handle unverified request.
97
+ #
98
+ # Valid unverified request handling methods are:
99
+ # * <tt>:exception</tt> - Raises ActionController::InvalidAuthenticityToken exception.
100
+ # * <tt>:reset_session</tt> - Resets the session.
101
+ # * <tt>:null_session</tt> - Provides an empty session during request but doesn't reset it completely. Used as default if <tt>:with</tt> option is not specified.
67
102
  def protect_from_forgery(options = {})
103
+ self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session)
68
104
  self.request_forgery_protection_token ||= :authenticity_token
69
- prepend_before_filter :verify_authenticity_token, options
105
+ prepend_before_action :verify_authenticity_token, options
106
+ append_after_action :verify_same_origin_request
107
+ end
108
+
109
+ private
110
+
111
+ def protection_method_class(name)
112
+ ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
113
+ rescue NameError
114
+ raise ArgumentError, 'Invalid request forgery protection method, use :null_session, :exception, or :reset_session'
115
+ end
116
+ end
117
+
118
+ module ProtectionMethods
119
+ class NullSession
120
+ def initialize(controller)
121
+ @controller = controller
122
+ end
123
+
124
+ # This is the method that defines the application behavior when a request is found to be unverified.
125
+ def handle_unverified_request
126
+ request = @controller.request
127
+ request.session = NullSessionHash.new(request.env)
128
+ request.env['action_dispatch.request.flash_hash'] = nil
129
+ request.env['rack.session.options'] = { skip: true }
130
+ request.env['action_dispatch.cookies'] = NullCookieJar.build(request)
131
+ end
132
+
133
+ protected
134
+
135
+ class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
136
+ def initialize(env)
137
+ super(nil, env)
138
+ @data = {}
139
+ @loaded = true
140
+ end
141
+
142
+ # no-op
143
+ def destroy; end
144
+
145
+ def exists?
146
+ true
147
+ end
148
+ end
149
+
150
+ class NullCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc:
151
+ def self.build(request)
152
+ key_generator = request.env[ActionDispatch::Cookies::GENERATOR_KEY]
153
+ host = request.host
154
+ secure = request.ssl?
155
+
156
+ new(key_generator, host, secure, options_for_env({}))
157
+ end
158
+
159
+ def write(*)
160
+ # nothing
161
+ end
162
+ end
163
+ end
164
+
165
+ class ResetSession
166
+ def initialize(controller)
167
+ @controller = controller
168
+ end
169
+
170
+ def handle_unverified_request
171
+ @controller.reset_session
172
+ end
173
+ end
174
+
175
+ class Exception
176
+ def initialize(controller)
177
+ @controller = controller
178
+ end
179
+
180
+ def handle_unverified_request
181
+ raise ActionController::InvalidAuthenticityToken
182
+ end
70
183
  end
71
184
  end
72
185
 
73
186
  protected
74
- # The actual before_filter that is used. Modify this to change how you handle unverified requests.
187
+ # The actual before_action that is used to verify the CSRF token.
188
+ # Don't override this directly. Provide your own forgery protection
189
+ # strategy instead. If you override, you'll disable same-origin
190
+ # `<script>` verification.
191
+ #
192
+ # Lean on the protect_from_forgery declaration to mark which actions are
193
+ # due for same-origin request verification. If protect_from_forgery is
194
+ # enabled on an action, this before_action flags its after_action to
195
+ # verify that JavaScript responses are for XHR requests, ensuring they
196
+ # follow the browser's same-origin policy.
75
197
  def verify_authenticity_token
76
- unless verified_request?
77
- logger.warn "WARNING: Can't verify CSRF token authenticity" if logger
198
+ mark_for_same_origin_verification!
199
+
200
+ if !verified_request?
201
+ if logger && log_warning_on_csrf_failure
202
+ logger.warn "Can't verify CSRF token authenticity"
203
+ end
78
204
  handle_unverified_request
79
205
  end
80
206
  end
81
207
 
82
- # This is the method that defines the application behavior when a request is found to be unverified.
83
- # By default, \Rails resets the session when it finds an unverified request.
84
208
  def handle_unverified_request
85
- reset_session
209
+ forgery_protection_strategy.new(self).handle_unverified_request
210
+ end
211
+
212
+ #:nodoc:
213
+ CROSS_ORIGIN_JAVASCRIPT_WARNING = "Security warning: an embedded " \
214
+ "<script> tag on another site requested protected JavaScript. " \
215
+ "If you know what you're doing, go ahead and disable forgery " \
216
+ "protection on this action to permit cross-origin JavaScript embedding."
217
+ private_constant :CROSS_ORIGIN_JAVASCRIPT_WARNING
218
+
219
+ # If `verify_authenticity_token` was run (indicating that we have
220
+ # forgery protection enabled for this request) then also verify that
221
+ # we aren't serving an unauthorized cross-origin response.
222
+ def verify_same_origin_request
223
+ if marked_for_same_origin_verification? && non_xhr_javascript_response?
224
+ logger.warn CROSS_ORIGIN_JAVASCRIPT_WARNING if logger
225
+ raise ActionController::InvalidCrossOriginRequest, CROSS_ORIGIN_JAVASCRIPT_WARNING
226
+ end
227
+ end
228
+
229
+ # GET requests are checked for cross-origin JavaScript after rendering.
230
+ def mark_for_same_origin_verification!
231
+ @marked_for_same_origin_verification = request.get?
232
+ end
233
+
234
+ # If the `verify_authenticity_token` before_action ran, verify that
235
+ # JavaScript responses are only served to same-origin GET requests.
236
+ def marked_for_same_origin_verification?
237
+ @marked_for_same_origin_verification ||= false
86
238
  end
87
239
 
240
+ # Check for cross-origin JavaScript responses.
241
+ def non_xhr_javascript_response?
242
+ content_type =~ %r(\Atext/javascript) && !request.xhr?
243
+ end
244
+
245
+ AUTHENTICITY_TOKEN_LENGTH = 32
246
+
88
247
  # Returns true or false if a request is verified. Checks:
89
248
  #
90
- # * is it a GET request? Gets should be safe and idempotent
249
+ # * is it a GET or HEAD request? Gets should be safe and idempotent
91
250
  # * Does the form_authenticity_token match the given token value from the params?
92
251
  # * Does the X-CSRF-Token header match the form_authenticity_token
93
252
  def verified_request?
94
- !protect_against_forgery? || request.get? ||
95
- form_authenticity_token == params[request_forgery_protection_token] ||
96
- form_authenticity_token == request.headers['X-CSRF-Token']
253
+ !protect_against_forgery? || request.get? || request.head? ||
254
+ valid_authenticity_token?(session, form_authenticity_param) ||
255
+ valid_authenticity_token?(session, request.headers['X-CSRF-Token'])
97
256
  end
98
257
 
99
258
  # Sets the token value for the current session.
100
259
  def form_authenticity_token
101
- session[:_csrf_token] ||= SecureRandom.base64(32)
260
+ masked_authenticity_token(session)
261
+ end
262
+
263
+ # Creates a masked version of the authenticity token that varies
264
+ # on each request. The masking is used to mitigate SSL attacks
265
+ # like BREACH.
266
+ def masked_authenticity_token(session)
267
+ one_time_pad = SecureRandom.random_bytes(AUTHENTICITY_TOKEN_LENGTH)
268
+ encrypted_csrf_token = xor_byte_strings(one_time_pad, real_csrf_token(session))
269
+ masked_token = one_time_pad + encrypted_csrf_token
270
+ Base64.strict_encode64(masked_token)
271
+ end
272
+
273
+ # Checks the client's masked token to see if it matches the
274
+ # session token. Essentially the inverse of
275
+ # +masked_authenticity_token+.
276
+ def valid_authenticity_token?(session, encoded_masked_token)
277
+ if encoded_masked_token.nil? || encoded_masked_token.empty? || !encoded_masked_token.is_a?(String)
278
+ return false
279
+ end
280
+
281
+ begin
282
+ masked_token = Base64.strict_decode64(encoded_masked_token)
283
+ rescue ArgumentError # encoded_masked_token is invalid Base64
284
+ return false
285
+ end
286
+
287
+ # See if it's actually a masked token or not. In order to
288
+ # deploy this code, we should be able to handle any unmasked
289
+ # tokens that we've issued without error.
290
+
291
+ if masked_token.length == AUTHENTICITY_TOKEN_LENGTH
292
+ # This is actually an unmasked token. This is expected if
293
+ # you have just upgraded to masked tokens, but should stop
294
+ # happening shortly after installing this gem
295
+ compare_with_real_token masked_token, session
296
+
297
+ elsif masked_token.length == AUTHENTICITY_TOKEN_LENGTH * 2
298
+ # Split the token into the one-time pad and the encrypted
299
+ # value and decrypt it
300
+ one_time_pad = masked_token[0...AUTHENTICITY_TOKEN_LENGTH]
301
+ encrypted_csrf_token = masked_token[AUTHENTICITY_TOKEN_LENGTH..-1]
302
+ csrf_token = xor_byte_strings(one_time_pad, encrypted_csrf_token)
303
+
304
+ compare_with_real_token csrf_token, session
305
+
306
+ else
307
+ false # Token is malformed
308
+ end
309
+ end
310
+
311
+ def compare_with_real_token(token, session)
312
+ ActiveSupport::SecurityUtils.secure_compare(token, real_csrf_token(session))
313
+ end
314
+
315
+ def real_csrf_token(session)
316
+ session[:_csrf_token] ||= SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH)
317
+ Base64.strict_decode64(session[:_csrf_token])
318
+ end
319
+
320
+ def xor_byte_strings(s1, s2)
321
+ s1.bytes.zip(s2.bytes).map { |(c1,c2)| c1 ^ c2 }.pack('c*')
102
322
  end
103
323
 
104
324
  # The form's authenticity parameter. Override to provide your own.
@@ -106,6 +326,7 @@ module ActionController #:nodoc:
106
326
  params[request_forgery_protection_token]
107
327
  end
108
328
 
329
+ # Checks if the controller allows forgery protection.
109
330
  def protect_against_forgery?
110
331
  allow_forgery_protection
111
332
  end
@@ -1,4 +1,3 @@
1
- require 'active_support/core_ext/file/path'
2
1
  require 'rack/chunked'
3
2
 
4
3
  module ActionController #:nodoc:
@@ -22,15 +21,13 @@ module ActionController #:nodoc:
22
21
  # supports fibers (fibers are supported since version 1.9.2 of the main
23
22
  # Ruby implementation).
24
23
  #
25
- # == Examples
26
- #
27
24
  # Streaming can be added to a given template easily, all you need to do is
28
25
  # to pass the :stream option.
29
26
  #
30
27
  # class PostsController
31
28
  # def index
32
- # @posts = Post.scoped
33
- # render :stream => true
29
+ # @posts = Post.all
30
+ # render stream: true
34
31
  # end
35
32
  # end
36
33
  #
@@ -54,10 +51,10 @@ module ActionController #:nodoc:
54
51
  #
55
52
  # def dashboard
56
53
  # # Allow lazy execution of the queries
57
- # @posts = Post.scoped
58
- # @pages = Page.scoped
59
- # @articles = Article.scoped
60
- # render :stream => true
54
+ # @posts = Post.all
55
+ # @pages = Page.all
56
+ # @articles = Article.all
57
+ # render stream: true
61
58
  # end
62
59
  #
63
60
  # Notice that :stream only works with templates. Rendering :json
@@ -140,17 +137,14 @@ module ActionController #:nodoc:
140
137
  # session or flash after the template starts rendering will not propagate
141
138
  # to the client.
142
139
  #
143
- # If you try to modify cookies, session or flash, an +ActionDispatch::ClosedError+
144
- # will be raised, showing those objects are closed for modification.
145
- #
146
140
  # == Middlewares
147
141
  #
148
142
  # Middlewares that need to manipulate the body won't work with streaming.
149
143
  # You should disable those middlewares whenever streaming in development
150
- # or production. For instance, +Rack::Bug+ won't work when streaming as it
144
+ # or production. For instance, <tt>Rack::Bug</tt> won't work when streaming as it
151
145
  # needs to inject contents in the HTML body.
152
146
  #
153
- # Also +Rack::Cache+ won't work with streaming as it does not support
147
+ # Also <tt>Rack::Cache</tt> won't work with streaming as it does not support
154
148
  # streaming bodies yet. Whenever streaming Cache-Control is automatically
155
149
  # set to "no-cache".
156
150
  #
@@ -163,7 +157,7 @@ module ActionController #:nodoc:
163
157
  # Currently, when an exception happens in development or production, Rails
164
158
  # will automatically stream to the client:
165
159
  #
166
- # "><script type="text/javascript">window.location = "/500.html"</script></html>
160
+ # "><script>window.location = "/500.html"</script></html>
167
161
  #
168
162
  # The first two characters (">) are required in case the exception happens
169
163
  # while rendering attributes for a given tag. You can check the real cause
@@ -180,7 +174,7 @@ module ActionController #:nodoc:
180
174
  # need to create a config file as follow:
181
175
  #
182
176
  # # unicorn.config.rb
183
- # listen 3000, :tcp_nopush => false
177
+ # listen 3000, tcp_nopush: false
184
178
  #
185
179
  # And use it on initialization:
186
180
  #
@@ -189,41 +183,39 @@ module ActionController #:nodoc:
189
183
  # You may also want to configure other parameters like <tt>:tcp_nodelay</tt>.
190
184
  # Please check its documentation for more information: http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen
191
185
  #
192
- # If you are using Unicorn with Nginx, you may need to tweak Nginx.
186
+ # If you are using Unicorn with NGINX, you may need to tweak NGINX.
193
187
  # Streaming should work out of the box on Rainbows.
194
188
  #
195
189
  # ==== Passenger
196
190
  #
197
191
  # To be described.
198
- #
192
+ #
199
193
  module Streaming
200
194
  extend ActiveSupport::Concern
201
195
 
202
- include AbstractController::Rendering
203
-
204
196
  protected
205
197
 
206
- # Set proper cache control and transfer encoding when streaming
207
- def _process_options(options) #:nodoc:
208
- super
209
- if options[:stream]
210
- if env["HTTP_VERSION"] == "HTTP/1.0"
211
- options.delete(:stream)
212
- else
213
- headers["Cache-Control"] ||= "no-cache"
214
- headers["Transfer-Encoding"] = "chunked"
215
- headers.delete("Content-Length")
198
+ # Set proper cache control and transfer encoding when streaming
199
+ def _process_options(options) #:nodoc:
200
+ super
201
+ if options[:stream]
202
+ if env["HTTP_VERSION"] == "HTTP/1.0"
203
+ options.delete(:stream)
204
+ else
205
+ headers["Cache-Control"] ||= "no-cache"
206
+ headers["Transfer-Encoding"] = "chunked"
207
+ headers.delete("Content-Length")
208
+ end
216
209
  end
217
210
  end
218
- end
219
211
 
220
- # Call render_to_body if we are streaming instead of usual +render+.
221
- def _render_template(options) #:nodoc:
222
- if options.delete(:stream)
223
- Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
224
- else
225
- super
212
+ # Call render_body if we are streaming instead of usual +render+.
213
+ def _render_template(options) #:nodoc:
214
+ if options.delete(:stream)
215
+ Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
216
+ else
217
+ super
218
+ end
226
219
  end
227
- end
228
220
  end
229
221
  end