actionpack 6.1.7.5 → 7.0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +323 -399
  3. data/MIT-LICENSE +1 -0
  4. data/README.rdoc +4 -5
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +13 -26
  7. data/lib/abstract_controller/caching/fragments.rb +2 -2
  8. data/lib/abstract_controller/caching.rb +1 -1
  9. data/lib/abstract_controller/callbacks.rb +21 -7
  10. data/lib/abstract_controller/collector.rb +2 -2
  11. data/lib/abstract_controller/error.rb +1 -1
  12. data/lib/abstract_controller/helpers.rb +17 -12
  13. data/lib/abstract_controller/logger.rb +1 -1
  14. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  15. data/lib/abstract_controller/rendering.rb +9 -11
  16. data/lib/abstract_controller/translation.rb +27 -4
  17. data/lib/abstract_controller/url_for.rb +4 -6
  18. data/lib/action_controller/api.rb +7 -7
  19. data/lib/action_controller/base.rb +5 -4
  20. data/lib/action_controller/form_builder.rb +2 -2
  21. data/lib/action_controller/log_subscriber.rb +4 -3
  22. data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
  23. data/lib/action_controller/metal/conditional_get.rb +137 -102
  24. data/lib/action_controller/metal/content_security_policy.rb +36 -2
  25. data/lib/action_controller/metal/cookies.rb +1 -1
  26. data/lib/action_controller/metal/data_streaming.rb +23 -31
  27. data/lib/action_controller/metal/etag_with_flash.rb +1 -1
  28. data/lib/action_controller/metal/exceptions.rb +19 -30
  29. data/lib/action_controller/metal/flash.rb +6 -2
  30. data/lib/action_controller/metal/head.rb +1 -1
  31. data/lib/action_controller/metal/helpers.rb +2 -2
  32. data/lib/action_controller/metal/http_authentication.rb +66 -39
  33. data/lib/action_controller/metal/instrumentation.rb +57 -52
  34. data/lib/action_controller/metal/live.rb +43 -2
  35. data/lib/action_controller/metal/mime_responds.rb +3 -3
  36. data/lib/action_controller/metal/params_wrapper.rb +20 -11
  37. data/lib/action_controller/metal/permissions_policy.rb +19 -28
  38. data/lib/action_controller/metal/redirecting.rb +95 -22
  39. data/lib/action_controller/metal/renderers.rb +12 -13
  40. data/lib/action_controller/metal/rendering.rb +121 -9
  41. data/lib/action_controller/metal/request_forgery_protection.rb +83 -32
  42. data/lib/action_controller/metal/rescue.rb +5 -4
  43. data/lib/action_controller/metal/streaming.rb +7 -9
  44. data/lib/action_controller/metal/strong_parameters.rb +138 -115
  45. data/lib/action_controller/metal/testing.rb +9 -2
  46. data/lib/action_controller/metal/url_for.rb +3 -5
  47. data/lib/action_controller/metal.rb +10 -13
  48. data/lib/action_controller/railtie.rb +50 -6
  49. data/lib/action_controller/renderer.rb +1 -20
  50. data/lib/action_controller/test_case.rb +28 -7
  51. data/lib/action_controller.rb +2 -5
  52. data/lib/action_dispatch/http/cache.rb +20 -13
  53. data/lib/action_dispatch/http/content_security_policy.rb +113 -36
  54. data/lib/action_dispatch/http/filter_parameters.rb +4 -19
  55. data/lib/action_dispatch/http/headers.rb +1 -1
  56. data/lib/action_dispatch/http/mime_negotiation.rb +15 -5
  57. data/lib/action_dispatch/http/mime_type.rb +9 -11
  58. data/lib/action_dispatch/http/parameters.rb +5 -5
  59. data/lib/action_dispatch/http/permissions_policy.rb +17 -1
  60. data/lib/action_dispatch/http/request.rb +27 -37
  61. data/lib/action_dispatch/http/response.rb +3 -20
  62. data/lib/action_dispatch/http/upload.rb +13 -2
  63. data/lib/action_dispatch/http/url.rb +11 -19
  64. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  65. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  66. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  67. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  68. data/lib/action_dispatch/journey/path/pattern.rb +22 -13
  69. data/lib/action_dispatch/journey/route.rb +6 -13
  70. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  71. data/lib/action_dispatch/journey/router.rb +1 -1
  72. data/lib/action_dispatch/journey/routes.rb +3 -3
  73. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  74. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  75. data/lib/action_dispatch/middleware/actionable_exceptions.rb +0 -1
  76. data/lib/action_dispatch/middleware/cookies.rb +20 -13
  77. data/lib/action_dispatch/middleware/debug_exceptions.rb +6 -4
  78. data/lib/action_dispatch/middleware/debug_locks.rb +3 -3
  79. data/lib/action_dispatch/middleware/exception_wrapper.rb +4 -0
  80. data/lib/action_dispatch/middleware/executor.rb +3 -0
  81. data/lib/action_dispatch/middleware/flash.rb +17 -18
  82. data/lib/action_dispatch/middleware/host_authorization.rb +13 -17
  83. data/lib/action_dispatch/middleware/remote_ip.rb +20 -8
  84. data/lib/action_dispatch/middleware/request_id.rb +3 -3
  85. data/lib/action_dispatch/middleware/server_timing.rb +76 -0
  86. data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
  87. data/lib/action_dispatch/middleware/session/cookie_store.rb +9 -9
  88. data/lib/action_dispatch/middleware/show_exceptions.rb +17 -16
  89. data/lib/action_dispatch/middleware/stack.rb +27 -9
  90. data/lib/action_dispatch/middleware/static.rb +5 -9
  91. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +1 -1
  92. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  93. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  94. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
  95. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
  96. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +4 -4
  97. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  98. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +28 -18
  99. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +3 -3
  100. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +3 -3
  101. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  102. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  103. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +3 -3
  104. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +22 -22
  105. data/lib/action_dispatch/railtie.rb +8 -2
  106. data/lib/action_dispatch/request/session.rb +43 -13
  107. data/lib/action_dispatch/routing/inspector.rb +1 -1
  108. data/lib/action_dispatch/routing/mapper.rb +82 -83
  109. data/lib/action_dispatch/routing/redirection.rb +5 -2
  110. data/lib/action_dispatch/routing/route_set.rb +17 -7
  111. data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
  112. data/lib/action_dispatch/routing/url_for.rb +24 -25
  113. data/lib/action_dispatch/routing.rb +5 -6
  114. data/lib/action_dispatch/system_test_case.rb +5 -5
  115. data/lib/action_dispatch/system_testing/browser.rb +3 -13
  116. data/lib/action_dispatch/system_testing/driver.rb +34 -10
  117. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +11 -7
  118. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
  119. data/lib/action_dispatch/testing/assertions/response.rb +1 -1
  120. data/lib/action_dispatch/testing/assertions/routing.rb +3 -2
  121. data/lib/action_dispatch/testing/assertions.rb +2 -5
  122. data/lib/action_dispatch/testing/integration.rb +6 -8
  123. data/lib/action_dispatch/testing/test_process.rb +3 -29
  124. data/lib/action_dispatch/testing/test_response.rb +20 -2
  125. data/lib/action_dispatch.rb +1 -0
  126. data/lib/action_pack/gem_version.rb +5 -5
  127. data/lib/action_pack/version.rb +1 -1
  128. metadata +16 -15
@@ -4,11 +4,11 @@ require "rack/session/abstract/id"
4
4
  require "action_controller/metal/exceptions"
5
5
  require "active_support/security_utils"
6
6
 
7
- module ActionController #:nodoc:
8
- class InvalidAuthenticityToken < ActionControllerError #:nodoc:
7
+ module ActionController # :nodoc:
8
+ class InvalidAuthenticityToken < ActionControllerError # :nodoc:
9
9
  end
10
10
 
11
- class InvalidCrossOriginRequest < ActionControllerError #:nodoc:
11
+ class InvalidCrossOriginRequest < ActionControllerError # :nodoc:
12
12
  end
13
13
 
14
14
  # Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
@@ -32,7 +32,7 @@ module ActionController #:nodoc:
32
32
  # response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or
33
33
  # Ajax) requests are allowed to make requests for JavaScript responses.
34
34
  #
35
- # Subclasses of <tt>ActionController::Base</tt> are protected by default with the
35
+ # Subclasses of ActionController::Base are protected by default with the
36
36
  # <tt>:exception</tt> strategy, which raises an
37
37
  # <tt>ActionController::InvalidAuthenticityToken</tt> error on unverified requests.
38
38
  #
@@ -92,7 +92,16 @@ module ActionController #:nodoc:
92
92
 
93
93
  # Controls whether URL-safe CSRF tokens are generated.
94
94
  config_accessor :urlsafe_csrf_tokens, instance_writer: false
95
- self.urlsafe_csrf_tokens = false
95
+ self.urlsafe_csrf_tokens = true
96
+
97
+ singleton_class.redefine_method(:urlsafe_csrf_tokens=) do |urlsafe_csrf_tokens|
98
+ if urlsafe_csrf_tokens
99
+ ActiveSupport::Deprecation.warn("URL-safe CSRF tokens are now the default. Use 6.1 defaults or above.")
100
+ else
101
+ ActiveSupport::Deprecation.warn("Non-URL-safe CSRF tokens are deprecated. Use 6.1 defaults or above.")
102
+ end
103
+ config.urlsafe_csrf_tokens = urlsafe_csrf_tokens
104
+ end
96
105
 
97
106
  helper_method :form_authenticity_token
98
107
  helper_method :protect_against_forgery?
@@ -109,14 +118,16 @@ module ActionController #:nodoc:
109
118
  # protect_from_forgery except: :index
110
119
  # end
111
120
  #
112
- # You can disable forgery protection on controller by skipping the verification before_action:
121
+ # You can disable forgery protection on a controller using skip_forgery_protection:
113
122
  #
114
- # skip_before_action :verify_authenticity_token
123
+ # class BarController < ApplicationController
124
+ # skip_forgery_protection
125
+ # end
115
126
  #
116
127
  # Valid Options:
117
128
  #
118
- # * <tt>:only/:except</tt> - Only apply forgery protection to a subset of actions. For example <tt>only: [ :create, :create_all ]</tt>.
119
- # * <tt>:if/:unless</tt> - Turn off the forgery protection entirely depending on the passed Proc or method reference.
129
+ # * <tt>:only</tt> / <tt>:except</tt> - Only apply forgery protection to a subset of actions. For example <tt>only: [ :create, :create_all ]</tt>.
130
+ # * <tt>:if</tt> / <tt>:unless</tt> - Turn off the forgery protection entirely depending on the passed Proc or method reference.
120
131
  # * <tt>:prepend</tt> - By default, the verification of the authentication token will be added at the position of the
121
132
  # protect_from_forgery call in your application. This means any callbacks added before are run first. This is useful
122
133
  # when you want your forgery protection to depend on other callbacks, like authentication methods (Oauth vs Cookie auth).
@@ -124,10 +135,26 @@ module ActionController #:nodoc:
124
135
  # If you need to add verification to the beginning of the callback chain, use <tt>prepend: true</tt>.
125
136
  # * <tt>:with</tt> - Set the method to handle unverified request.
126
137
  #
127
- # Valid unverified request handling methods are:
138
+ # Built-in unverified request handling methods are:
128
139
  # * <tt>:exception</tt> - Raises ActionController::InvalidAuthenticityToken exception.
129
140
  # * <tt>:reset_session</tt> - Resets the session.
130
141
  # * <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.
142
+ #
143
+ # You can also implement custom strategy classes for unverified request handling:
144
+ #
145
+ # class CustomStrategy
146
+ # def initialize(controller)
147
+ # @controller = controller
148
+ # end
149
+ #
150
+ # def handle_unverified_request
151
+ # # Custom behaviour for unverfied request
152
+ # end
153
+ # end
154
+ #
155
+ # class ApplicationController < ActionController:x:Base
156
+ # protect_from_forgery with: CustomStrategy
157
+ # end
131
158
  def protect_from_forgery(options = {})
132
159
  options = options.reverse_merge(prepend: false)
133
160
 
@@ -143,14 +170,23 @@ module ActionController #:nodoc:
143
170
  #
144
171
  # See +skip_before_action+ for allowed options.
145
172
  def skip_forgery_protection(options = {})
146
- skip_before_action :verify_authenticity_token, options
173
+ skip_before_action :verify_authenticity_token, options.reverse_merge(raise: false)
147
174
  end
148
175
 
149
176
  private
150
177
  def protection_method_class(name)
151
- ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
152
- rescue NameError
153
- raise ArgumentError, "Invalid request forgery protection method, use :null_session, :exception, or :reset_session"
178
+ case name
179
+ when :null_session
180
+ ProtectionMethods::NullSession
181
+ when :reset_session
182
+ ProtectionMethods::ResetSession
183
+ when :exception
184
+ ProtectionMethods::Exception
185
+ when Class
186
+ name
187
+ else
188
+ raise ArgumentError, "Invalid request forgery protection method, use :null_session, :exception, :reset_session, or a custom forgery protection class."
189
+ end
154
190
  end
155
191
  end
156
192
 
@@ -170,7 +206,7 @@ module ActionController #:nodoc:
170
206
  end
171
207
 
172
208
  private
173
- class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
209
+ class NullSessionHash < Rack::Session::Abstract::SessionHash # :nodoc:
174
210
  def initialize(req)
175
211
  super(nil, req)
176
212
  @data = {}
@@ -183,9 +219,13 @@ module ActionController #:nodoc:
183
219
  def exists?
184
220
  true
185
221
  end
222
+
223
+ def enabled?
224
+ false
225
+ end
186
226
  end
187
227
 
188
- class NullCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc:
228
+ class NullCookieJar < ActionDispatch::Cookies::CookieJar # :nodoc:
189
229
  def write(*)
190
230
  # nothing
191
231
  end
@@ -203,12 +243,14 @@ module ActionController #:nodoc:
203
243
  end
204
244
 
205
245
  class Exception
246
+ attr_accessor :warning_message
247
+
206
248
  def initialize(controller)
207
249
  @controller = controller
208
250
  end
209
251
 
210
252
  def handle_unverified_request
211
- raise ActionController::InvalidAuthenticityToken
253
+ raise ActionController::InvalidAuthenticityToken, warning_message
212
254
  end
213
255
  end
214
256
  end
@@ -228,22 +270,31 @@ module ActionController #:nodoc:
228
270
  mark_for_same_origin_verification!
229
271
 
230
272
  if !verified_request?
231
- if logger && log_warning_on_csrf_failure
232
- if valid_request_origin?
233
- logger.warn "Can't verify CSRF token authenticity."
234
- else
235
- logger.warn "HTTP Origin header (#{request.origin}) didn't match request.base_url (#{request.base_url})"
236
- end
237
- end
273
+ logger.warn unverified_request_warning_message if logger && log_warning_on_csrf_failure
274
+
238
275
  handle_unverified_request
239
276
  end
240
277
  end
241
278
 
242
279
  def handle_unverified_request # :doc:
243
- forgery_protection_strategy.new(self).handle_unverified_request
280
+ protection_strategy = forgery_protection_strategy.new(self)
281
+
282
+ if protection_strategy.respond_to?(:warning_message)
283
+ protection_strategy.warning_message = unverified_request_warning_message
284
+ end
285
+
286
+ protection_strategy.handle_unverified_request
287
+ end
288
+
289
+ def unverified_request_warning_message # :nodoc:
290
+ if valid_request_origin?
291
+ "Can't verify CSRF token authenticity."
292
+ else
293
+ "HTTP Origin header (#{request.origin}) didn't match request.base_url (#{request.base_url})"
294
+ end
244
295
  end
245
296
 
246
- #:nodoc:
297
+ # :nodoc:
247
298
  CROSS_ORIGIN_JAVASCRIPT_WARNING = "Security warning: an embedded " \
248
299
  "<script> tag on another site requested protected JavaScript. " \
249
300
  "If you know what you're doing, go ahead and disable forgery " \
@@ -285,7 +336,7 @@ module ActionController #:nodoc:
285
336
  #
286
337
  # * Is it a GET or HEAD request? GETs should be safe and idempotent
287
338
  # * Does the form_authenticity_token match the given token value from the params?
288
- # * Does the X-CSRF-Token header match the form_authenticity_token?
339
+ # * Does the +X-CSRF-Token+ header match the form_authenticity_token?
289
340
  def verified_request? # :doc:
290
341
  !protect_against_forgery? || request.get? || request.head? ||
291
342
  (valid_request_origin? && any_authenticity_token_valid?)
@@ -303,15 +354,15 @@ module ActionController #:nodoc:
303
354
  [form_authenticity_param, request.x_csrf_token]
304
355
  end
305
356
 
306
- # Sets the token value for the current session.
307
- def form_authenticity_token(form_options: {})
357
+ # Creates the authenticity token for the current request.
358
+ def form_authenticity_token(form_options: {}) # :doc:
308
359
  masked_authenticity_token(session, form_options: form_options)
309
360
  end
310
361
 
311
362
  # Creates a masked version of the authenticity token that varies
312
363
  # on each request. The masking is used to mitigate SSL attacks
313
364
  # like BREACH.
314
- def masked_authenticity_token(session, form_options: {}) # :doc:
365
+ def masked_authenticity_token(session, form_options: {})
315
366
  action, method = form_options.values_at(:action, :method)
316
367
 
317
368
  raw_token = if per_form_csrf_tokens && action && method
@@ -438,7 +489,7 @@ module ActionController #:nodoc:
438
489
 
439
490
  # Checks if the controller allows forgery protection.
440
491
  def protect_against_forgery? # :doc:
441
- allow_forgery_protection
492
+ allow_forgery_protection && (!session.respond_to?(:enabled?) || session.enabled?)
442
493
  end
443
494
 
444
495
  NULL_ORIGIN_MESSAGE = <<~MSG
@@ -469,7 +520,7 @@ module ActionController #:nodoc:
469
520
 
470
521
  def generate_csrf_token # :nodoc:
471
522
  if urlsafe_csrf_tokens
472
- SecureRandom.urlsafe_base64(AUTHENTICITY_TOKEN_LENGTH, padding: false)
523
+ SecureRandom.urlsafe_base64(AUTHENTICITY_TOKEN_LENGTH)
473
524
  else
474
525
  SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH)
475
526
  end
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ActionController #:nodoc:
4
- # This module is responsible for providing +rescue_from+ helpers
5
- # to controllers and configuring when detailed exceptions must be
6
- # shown.
3
+ module ActionController # :nodoc:
4
+ # This module is responsible for providing
5
+ # {rescue_from}[rdoc-ref:ActiveSupport::Rescuable::ClassMethods#rescue_from]
6
+ # to controllers, wrapping actions to handle configured errors, and
7
+ # configuring when detailed exceptions must be shown.
7
8
  module Rescue
8
9
  extend ActiveSupport::Concern
9
10
  include ActiveSupport::Rescuable
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "rack/chunked"
4
4
 
5
- module ActionController #:nodoc:
5
+ module ActionController # :nodoc:
6
6
  # Allows views to be streamed back to the client as they are rendered.
7
7
  #
8
8
  # By default, Rails renders views by first rendering the template
@@ -24,7 +24,7 @@ module ActionController #:nodoc:
24
24
  # Ruby implementation).
25
25
  #
26
26
  # Streaming can be added to a given template easily, all you need to do is
27
- # to pass the :stream option.
27
+ # to pass the +:stream+ option.
28
28
  #
29
29
  # class PostsController
30
30
  # def index
@@ -59,8 +59,8 @@ module ActionController #:nodoc:
59
59
  # render stream: true
60
60
  # end
61
61
  #
62
- # Notice that :stream only works with templates. Rendering :json
63
- # or :xml with :stream won't work.
62
+ # Notice that +:stream+ only works with templates. Rendering +:json+
63
+ # or +:xml+ with +:stream+ won't work.
64
64
  #
65
65
  # == Communication between layout and template
66
66
  #
@@ -72,7 +72,7 @@ module ActionController #:nodoc:
72
72
  # variables set in the template to be used in the layout, they won't
73
73
  # work once you move to streaming. The proper way to communicate
74
74
  # between layout and template, regardless of whether you use streaming
75
- # or not, is by using +content_for+, +provide+ and +yield+.
75
+ # or not, is by using +content_for+, +provide+, and +yield+.
76
76
  #
77
77
  # Take a simple example where the layout expects the template to tell
78
78
  # which title to use:
@@ -132,7 +132,7 @@ module ActionController #:nodoc:
132
132
  # That said, when streaming, you need to properly check your templates
133
133
  # and choose when to use +provide+ and +content_for+.
134
134
  #
135
- # == Headers, cookies, session and flash
135
+ # == Headers, cookies, session, and flash
136
136
  #
137
137
  # When streaming, the HTTP headers are sent to the client right before
138
138
  # it renders the first line. This means that, modifying headers, cookies,
@@ -147,7 +147,7 @@ module ActionController #:nodoc:
147
147
  # needs to inject contents in the HTML body.
148
148
  #
149
149
  # Also <tt>Rack::Cache</tt> won't work with streaming as it does not support
150
- # streaming bodies yet. Whenever streaming Cache-Control is automatically
150
+ # streaming bodies yet. Whenever streaming +Cache-Control+ is automatically
151
151
  # set to "no-cache".
152
152
  #
153
153
  # == Errors
@@ -193,8 +193,6 @@ module ActionController #:nodoc:
193
193
  # To be described.
194
194
  #
195
195
  module Streaming
196
- extend ActiveSupport::Concern
197
-
198
196
  private
199
197
  # Set proper cache control and transfer encoding when streaming
200
198
  def _process_options(options)