actionpack 6.1.7.3 → 7.0.8

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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +320 -390
  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 +5 -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 +111 -19
  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)