actionpack 8.0.2 → 8.1.0.beta1

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +252 -137
  3. data/README.rdoc +1 -1
  4. data/lib/abstract_controller/asset_paths.rb +4 -2
  5. data/lib/abstract_controller/base.rb +11 -14
  6. data/lib/abstract_controller/caching.rb +6 -3
  7. data/lib/abstract_controller/collector.rb +1 -1
  8. data/lib/abstract_controller/logger.rb +2 -1
  9. data/lib/action_controller/base.rb +1 -1
  10. data/lib/action_controller/caching.rb +1 -2
  11. data/lib/action_controller/form_builder.rb +1 -1
  12. data/lib/action_controller/log_subscriber.rb +7 -0
  13. data/lib/action_controller/metal/allow_browser.rb +1 -1
  14. data/lib/action_controller/metal/conditional_get.rb +25 -0
  15. data/lib/action_controller/metal/data_streaming.rb +1 -3
  16. data/lib/action_controller/metal/exceptions.rb +5 -0
  17. data/lib/action_controller/metal/flash.rb +1 -4
  18. data/lib/action_controller/metal/head.rb +3 -1
  19. data/lib/action_controller/metal/live.rb +0 -6
  20. data/lib/action_controller/metal/permissions_policy.rb +9 -0
  21. data/lib/action_controller/metal/rate_limiting.rb +22 -7
  22. data/lib/action_controller/metal/redirecting.rb +63 -7
  23. data/lib/action_controller/metal/renderers.rb +27 -6
  24. data/lib/action_controller/metal/rendering.rb +8 -2
  25. data/lib/action_controller/metal/request_forgery_protection.rb +18 -10
  26. data/lib/action_controller/metal/rescue.rb +9 -0
  27. data/lib/action_controller/railtie.rb +2 -6
  28. data/lib/action_controller/renderer.rb +0 -1
  29. data/lib/action_dispatch/constants.rb +6 -0
  30. data/lib/action_dispatch/http/cache.rb +111 -1
  31. data/lib/action_dispatch/http/content_security_policy.rb +13 -1
  32. data/lib/action_dispatch/http/filter_parameters.rb +5 -3
  33. data/lib/action_dispatch/http/mime_negotiation.rb +8 -3
  34. data/lib/action_dispatch/http/mime_types.rb +1 -0
  35. data/lib/action_dispatch/http/param_builder.rb +28 -27
  36. data/lib/action_dispatch/http/parameters.rb +3 -3
  37. data/lib/action_dispatch/http/permissions_policy.rb +4 -0
  38. data/lib/action_dispatch/http/query_parser.rb +12 -10
  39. data/lib/action_dispatch/http/request.rb +10 -5
  40. data/lib/action_dispatch/http/response.rb +65 -17
  41. data/lib/action_dispatch/http/url.rb +101 -5
  42. data/lib/action_dispatch/journey/gtg/simulator.rb +33 -12
  43. data/lib/action_dispatch/journey/gtg/transition_table.rb +29 -39
  44. data/lib/action_dispatch/journey/nodes/node.rb +2 -1
  45. data/lib/action_dispatch/journey/route.rb +45 -31
  46. data/lib/action_dispatch/journey/router/utils.rb +8 -14
  47. data/lib/action_dispatch/journey/router.rb +59 -81
  48. data/lib/action_dispatch/journey/routes.rb +7 -0
  49. data/lib/action_dispatch/journey/visitors.rb +55 -23
  50. data/lib/action_dispatch/journey/visualizer/fsm.js +4 -6
  51. data/lib/action_dispatch/middleware/cookies.rb +4 -2
  52. data/lib/action_dispatch/middleware/debug_exceptions.rb +10 -2
  53. data/lib/action_dispatch/middleware/debug_view.rb +11 -0
  54. data/lib/action_dispatch/middleware/exception_wrapper.rb +14 -8
  55. data/lib/action_dispatch/middleware/executor.rb +12 -2
  56. data/lib/action_dispatch/middleware/public_exceptions.rb +6 -6
  57. data/lib/action_dispatch/middleware/session/cache_store.rb +17 -0
  58. data/lib/action_dispatch/middleware/templates/rescues/_copy_button.html.erb +1 -0
  59. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +3 -2
  60. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +9 -5
  61. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +1 -0
  62. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +1 -0
  63. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +1 -0
  64. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +50 -0
  65. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -0
  66. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -0
  67. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -0
  68. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -0
  69. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -0
  70. data/lib/action_dispatch/railtie.rb +10 -2
  71. data/lib/action_dispatch/routing/inspector.rb +4 -1
  72. data/lib/action_dispatch/routing/mapper.rb +323 -173
  73. data/lib/action_dispatch/routing/route_set.rb +3 -6
  74. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +2 -2
  75. data/lib/action_dispatch/testing/assertion_response.rb +1 -1
  76. data/lib/action_dispatch/testing/assertions/response.rb +14 -0
  77. data/lib/action_dispatch/testing/assertions/routing.rb +11 -3
  78. data/lib/action_pack/gem_version.rb +3 -3
  79. metadata +13 -12
@@ -128,7 +128,7 @@ module ActionController
128
128
  #
129
129
  # Action Controller sends content to the user by using one of five rendering
130
130
  # methods. The most versatile and common is the rendering of a template.
131
- # Included in the Action Pack is the Action View, which enables rendering of ERB
131
+ # Also included with \Rails is Action View, which enables rendering of ERB
132
132
  # templates. It's automatically configured. The controller passes objects to the
133
133
  # view by assigning instance variables:
134
134
  #
@@ -9,9 +9,8 @@ module ActionController
9
9
  # of calculations, renderings, and database calls around for subsequent
10
10
  # requests.
11
11
  #
12
- # You can read more about each approach by clicking the modules below.
13
- #
14
12
  # Note: To turn off all caching provided by Action Controller, set
13
+ #
15
14
  # config.action_controller.perform_caching = false
16
15
  #
17
16
  # ## Caching stores
@@ -40,7 +40,7 @@ module ActionController
40
40
  # rendered by this controller and its subclasses.
41
41
  #
42
42
  # #### Parameters
43
- # * `builder` - Default form builder, an instance of
43
+ # * `builder` - Default form builder. Accepts a subclass of
44
44
  # ActionView::Helpers::FormBuilder
45
45
  def default_form_builder(builder)
46
46
  self._default_form_builder = builder
@@ -49,6 +49,13 @@ module ActionController
49
49
  end
50
50
  subscribe_log_level :halted_callback, :info
51
51
 
52
+ # Manually subscribed below
53
+ def rescue_from_callback(event)
54
+ exception = event.payload[:exception]
55
+ info { "rescue_from handled #{exception.class} (#{exception.message}) - #{exception.backtrace.first.delete_prefix("#{Rails.root}/")}" }
56
+ end
57
+ subscribe_log_level :rescue_from_callback, :info
58
+
52
59
  def send_file(event)
53
60
  info { "Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)" }
54
61
  end
@@ -14,7 +14,7 @@ module ActionController # :nodoc:
14
14
  # aren't reporting a user-agent header, will be allowed access.
15
15
  #
16
16
  # A browser that's blocked will by default be served the file in
17
- # public/406-unsupported-browser.html with a HTTP status code of "406 Not
17
+ # public/406-unsupported-browser.html with an HTTP status code of "406 Not
18
18
  # Acceptable".
19
19
  #
20
20
  # In addition to specifically named browser versions, you can also pass
@@ -332,6 +332,31 @@ module ActionController
332
332
  response.cache_control.replace(no_store: true)
333
333
  end
334
334
 
335
+ # Adds the `must-understand` directive to the `Cache-Control` header, which indicates
336
+ # that a cache MUST understand the semantics of the response status code that has been
337
+ # received, or discard the response.
338
+ #
339
+ # This is particularly useful when returning responses with new or uncommon
340
+ # status codes that might not be properly interpreted by older caches.
341
+ #
342
+ # #### Example
343
+ #
344
+ # def show
345
+ # @article = Article.find(params[:id])
346
+ #
347
+ # if @article.early_access?
348
+ # must_understand
349
+ # render status: 203 # Non-Authoritative Information
350
+ # else
351
+ # fresh_when @article
352
+ # end
353
+ # end
354
+ #
355
+ def must_understand
356
+ response.cache_control[:must_understand] = true
357
+ response.cache_control[:no_store] = true
358
+ end
359
+
335
360
  private
336
361
  def combine_etags(validator, options)
337
362
  [validator, *etaggers.map { |etagger| instance_exec(options, &etagger) }].compact
@@ -134,9 +134,7 @@ module ActionController # :nodoc:
134
134
  raise ArgumentError, ":type option required" if content_type.nil?
135
135
 
136
136
  if content_type.is_a?(Symbol)
137
- extension = Mime[content_type]
138
- raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension
139
- self.content_type = extension
137
+ self.content_type = content_type
140
138
  else
141
139
  if !type_provided && options[:filename]
142
140
  # If type wasn't provided, try guessing from file extension.
@@ -103,4 +103,9 @@ module ActionController
103
103
  super(message)
104
104
  end
105
105
  end
106
+
107
+ # Raised when a Rate Limit is exceeded by too many requests within a period of
108
+ # time.
109
+ class TooManyRequests < ActionControllerError
110
+ end
106
111
  end
@@ -38,15 +38,12 @@ module ActionController # :nodoc:
38
38
  define_method(type) do
39
39
  request.flash[type]
40
40
  end
41
+ private type
41
42
  helper_method(type) if respond_to?(:helper_method)
42
43
 
43
44
  self._flash_types += [type]
44
45
  end
45
46
  end
46
-
47
- def action_methods # :nodoc:
48
- @action_methods ||= super - _flash_types.map(&:to_s).to_set
49
- end
50
47
  end
51
48
 
52
49
  private
@@ -25,6 +25,8 @@ module ActionController
25
25
  raise ArgumentError, "#{status.inspect} is not a valid value for `status`."
26
26
  end
27
27
 
28
+ raise ::AbstractController::DoubleRenderError if response_body
29
+
28
30
  status ||= :ok
29
31
 
30
32
  if options
@@ -41,7 +43,7 @@ module ActionController
41
43
 
42
44
  if include_content?(response_code)
43
45
  unless self.media_type
44
- self.content_type = content_type || ((f = formats) && Mime[f.first]) || Mime[:html]
46
+ self.content_type = content_type || ((f = formats) && Mime[f.first]) || :html
45
47
  end
46
48
 
47
49
  response.charset = false
@@ -171,12 +171,6 @@ module ActionController
171
171
  @ignore_disconnect = false
172
172
  end
173
173
 
174
- # ActionDispatch::Response delegates #to_ary to the internal
175
- # ActionDispatch::Response::Buffer, defining #to_ary is an indicator that the
176
- # response body can be buffered and/or cached by Rack middlewares, this is not
177
- # the case for Live responses so we undefine it for this Buffer subclass.
178
- undef_method :to_ary
179
-
180
174
  def write(string)
181
175
  unless @response.committed?
182
176
  @response.headers["Cache-Control"] ||= "no-cache"
@@ -24,8 +24,17 @@ module ActionController # :nodoc:
24
24
  # end
25
25
  # end
26
26
  #
27
+ # Requires a global policy defined in an initializer, which can be
28
+ # empty:
29
+ #
30
+ # Rails.application.config.permissions_policy do |policy|
31
+ # # policy.gyroscope :none
32
+ # end
27
33
  def permissions_policy(**options, &block)
28
34
  before_action(options) do
35
+ unless request.respond_to?(:permissions_policy)
36
+ raise "Cannot override permissions_policy if no global permissions_policy configured."
37
+ end
29
38
  if block_given?
30
39
  policy = request.permissions_policy.clone
31
40
  instance_exec(policy, &block)
@@ -18,8 +18,13 @@ module ActionController # :nodoc:
18
18
  # parameter. It's evaluated within the context of the controller processing the
19
19
  # request.
20
20
  #
21
- # Requests that exceed the rate limit are refused with a `429 Too Many Requests`
22
- # response. You can specialize this by passing a callable in the `with:`
21
+ # By default, rate limits are scoped to the controller's path. If you want to
22
+ # share rate limits across multiple controllers, you can provide your own scope,
23
+ # by passing value in the `scope:` parameter.
24
+ #
25
+ # Requests that exceed the rate limit will raise an `ActionController::TooManyRequests`
26
+ # error. By default, Action Dispatch will rescue from the error and refuse the request
27
+ # with a `429 Too Many Requests` response. You can specialize this by passing a callable in the `with:`
23
28
  # parameter. It's evaluated within the context of the controller processing the
24
29
  # request.
25
30
  #
@@ -46,23 +51,33 @@ module ActionController # :nodoc:
46
51
  # class APIController < ApplicationController
47
52
  # RATE_LIMIT_STORE = ActiveSupport::Cache::RedisCacheStore.new(url: ENV["REDIS_URL"])
48
53
  # rate_limit to: 10, within: 3.minutes, store: RATE_LIMIT_STORE
54
+ # rate_limit to: 100, within: 5.minutes, scope: :api_global
49
55
  # end
50
56
  #
51
57
  # class SessionsController < ApplicationController
52
58
  # rate_limit to: 3, within: 2.seconds, name: "short-term"
53
59
  # rate_limit to: 10, within: 5.minutes, name: "long-term"
54
60
  # end
55
- def rate_limit(to:, within:, by: -> { request.remote_ip }, with: -> { head :too_many_requests }, store: cache_store, name: nil, **options)
56
- before_action -> { rate_limiting(to: to, within: within, by: by, with: with, store: store, name: name) }, **options
61
+ def rate_limit(to:, within:, by: -> { request.remote_ip }, with: -> { raise TooManyRequests }, store: cache_store, name: nil, scope: nil, **options)
62
+ before_action -> { rate_limiting(to: to, within: within, by: by, with: with, store: store, name: name, scope: scope || controller_path) }, **options
57
63
  end
58
64
  end
59
65
 
60
66
  private
61
- def rate_limiting(to:, within:, by:, with:, store:, name:)
62
- cache_key = ["rate-limit", controller_path, name, instance_exec(&by)].compact.join(":")
67
+ def rate_limiting(to:, within:, by:, with:, store:, name:, scope:)
68
+ by = instance_exec(&by)
69
+ cache_key = ["rate-limit", scope, name, by].compact.join(":")
63
70
  count = store.increment(cache_key, 1, expires_in: within)
64
71
  if count && count > to
65
- ActiveSupport::Notifications.instrument("rate_limit.action_controller", request: request) do
72
+ ActiveSupport::Notifications.instrument("rate_limit.action_controller",
73
+ request: request,
74
+ count: count,
75
+ to: to,
76
+ within: within,
77
+ by: by,
78
+ name: name,
79
+ scope: scope,
80
+ cache_key: cache_key) do
66
81
  instance_exec(&with)
67
82
  end
68
83
  end
@@ -15,6 +15,19 @@ module ActionController
15
15
 
16
16
  included do
17
17
  mattr_accessor :raise_on_open_redirects, default: false
18
+ mattr_accessor :action_on_path_relative_redirect, default: :log
19
+ class_attribute :_allowed_redirect_hosts, :allowed_redirect_hosts_permissions, instance_accessor: false, instance_predicate: false
20
+ singleton_class.alias_method :allowed_redirect_hosts, :_allowed_redirect_hosts
21
+ end
22
+
23
+ module ClassMethods # :nodoc:
24
+ def allowed_redirect_hosts=(hosts)
25
+ hosts = hosts.dup.freeze
26
+ self._allowed_redirect_hosts = hosts
27
+ self.allowed_redirect_hosts_permissions = if hosts.present?
28
+ ActionDispatch::HostAuthorization::Permissions.new(hosts)
29
+ end
30
+ end
18
31
  end
19
32
 
20
33
  # Redirects the browser to the target specified in `options`. This parameter can
@@ -100,6 +113,26 @@ module ActionController
100
113
  #
101
114
  # See #url_from for more information on what an internal and safe URL is, or how
102
115
  # to fall back to an alternate redirect URL in the unsafe case.
116
+ #
117
+ # ### Path Relative URL Redirect Protection
118
+ #
119
+ # Rails also protects against potentially unsafe path relative URL redirects that don't
120
+ # start with a leading slash. These can create security vulnerabilities:
121
+ #
122
+ # redirect_to "example.com" # Creates http://yourdomain.comexample.com
123
+ # redirect_to "@attacker.com" # Creates http://yourdomain.com@attacker.com
124
+ # # which browsers interpret as user@host
125
+ #
126
+ # You can configure how Rails handles these cases using:
127
+ #
128
+ # config.action_controller.action_on_path_relative_redirect = :log # default
129
+ # config.action_controller.action_on_path_relative_redirect = :notify
130
+ # config.action_controller.action_on_path_relative_redirect = :raise
131
+ #
132
+ # * `:log` - Logs a warning but allows the redirect
133
+ # * `:notify` - Sends an ActiveSupport notification but allows the redirect
134
+ # (includes stack trace to help identify the source)
135
+ # * `:raise` - Raises an UnsafeRedirectError
103
136
  def redirect_to(options = {}, response_options = {})
104
137
  raise ActionControllerError.new("Cannot redirect to nil!") unless options
105
138
  raise AbstractController::DoubleRenderError if response_body
@@ -166,6 +199,10 @@ module ActionController
166
199
  when /\A([a-z][a-z\d\-+.]*:|\/\/).*/i
167
200
  options.to_str
168
201
  when String
202
+ if !options.start_with?("/", "?") && !options.empty?
203
+ _handle_path_relative_redirect(options)
204
+ end
205
+
169
206
  request.protocol + request.host_with_port + options
170
207
  when Proc
171
208
  _compute_redirect_to_location request, instance_eval(&options)
@@ -212,9 +249,9 @@ module ActionController
212
249
 
213
250
  def _extract_redirect_to_status(options, response_options)
214
251
  if options.is_a?(Hash) && options.key?(:status)
215
- Rack::Utils.status_code(options.delete(:status))
252
+ ActionDispatch::Response.rack_status_code(options.delete(:status))
216
253
  elsif response_options.key?(:status)
217
- Rack::Utils.status_code(response_options[:status])
254
+ ActionDispatch::Response.rack_status_code(response_options[:status])
218
255
  else
219
256
  302
220
257
  end
@@ -229,12 +266,14 @@ module ActionController
229
266
  end
230
267
 
231
268
  def _url_host_allowed?(url)
232
- host = URI(url.to_s).host
269
+ url_to_s = url.to_s
270
+ host = URI(url_to_s).host
233
271
 
234
- return true if host == request.host
235
- return false unless host.nil?
236
- return false unless url.to_s.start_with?("/")
237
- !url.to_s.start_with?("//")
272
+ if host.nil?
273
+ url_to_s.start_with?("/") && !url_to_s.start_with?("//")
274
+ else
275
+ host == request.host || self.class.allowed_redirect_hosts_permissions&.allows?(host)
276
+ end
238
277
  rescue ArgumentError, URI::Error
239
278
  false
240
279
  end
@@ -248,5 +287,22 @@ module ActionController
248
287
  raise UnsafeRedirectError, msg
249
288
  end
250
289
  end
290
+
291
+ def _handle_path_relative_redirect(url)
292
+ message = "Path relative URL redirect detected: #{url.inspect}"
293
+
294
+ case action_on_path_relative_redirect
295
+ when :log
296
+ logger&.warn message
297
+ when :notify
298
+ ActiveSupport::Notifications.instrument("unsafe_redirect.action_controller",
299
+ url: url,
300
+ message: message,
301
+ stack_trace: caller
302
+ )
303
+ when :raise
304
+ raise UnsafeRedirectError, message
305
+ end
306
+ end
251
307
  end
252
308
  end
@@ -27,8 +27,23 @@ module ActionController
27
27
  # Default values are `:json`, `:js`, `:xml`.
28
28
  RENDERERS = Set.new
29
29
 
30
+ module DeprecatedEscapeJsonResponses # :nodoc:
31
+ def escape_json_responses=(value)
32
+ if value
33
+ ActionController.deprecator.warn(<<~MSG.squish)
34
+ Setting action_controller.escape_json_responses = true is deprecated and will have no effect in Rails 8.2.
35
+ Set it to `false`, or remove the config.
36
+ MSG
37
+ end
38
+ super
39
+ end
40
+ end
41
+
30
42
  included do
31
43
  class_attribute :_renderers, default: Set.new.freeze
44
+ class_attribute :escape_json_responses, instance_writer: false, instance_accessor: false, default: true
45
+
46
+ singleton_class.prepend DeprecatedEscapeJsonResponses
32
47
  end
33
48
 
34
49
  # Used in ActionController::Base and ActionController::API to include all
@@ -86,7 +101,7 @@ module ActionController
86
101
  remove_possible_method(method_name)
87
102
  end
88
103
 
89
- def self._render_with_renderer_method_name(key)
104
+ def self._render_with_renderer_method_name(key) # :nodoc:
90
105
  "_render_with_renderer_#{key}"
91
106
  end
92
107
 
@@ -140,7 +155,7 @@ module ActionController
140
155
  _render_to_body_with_renderer(options) || super
141
156
  end
142
157
 
143
- def _render_to_body_with_renderer(options)
158
+ def _render_to_body_with_renderer(options) # :nodoc:
144
159
  _renderers.each do |name|
145
160
  if options.key?(name)
146
161
  _process_options(options)
@@ -153,28 +168,34 @@ module ActionController
153
168
 
154
169
  add :json do |json, options|
155
170
  json_options = options.except(:callback, :content_type, :status)
171
+ json_options[:escape] ||= false if !self.class.escape_json_responses? && options[:callback].blank?
156
172
  json = json.to_json(json_options) unless json.kind_of?(String)
157
173
 
158
174
  if options[:callback].present?
159
175
  if media_type.nil? || media_type == Mime[:json]
160
- self.content_type = Mime[:js]
176
+ self.content_type = :js
161
177
  end
162
178
 
163
179
  "/**/#{options[:callback]}(#{json})"
164
180
  else
165
- self.content_type = Mime[:json] if media_type.nil?
181
+ self.content_type = :json if media_type.nil?
166
182
  json
167
183
  end
168
184
  end
169
185
 
170
186
  add :js do |js, options|
171
- self.content_type = Mime[:js] if media_type.nil?
187
+ self.content_type = :js if media_type.nil?
172
188
  js.respond_to?(:to_js) ? js.to_js(options) : js
173
189
  end
174
190
 
175
191
  add :xml do |xml, options|
176
- self.content_type = Mime[:xml] if media_type.nil?
192
+ self.content_type = :xml if media_type.nil?
177
193
  xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
178
194
  end
195
+
196
+ add :markdown do |md, options|
197
+ self.content_type = :md if media_type.nil?
198
+ md.respond_to?(:to_markdown) ? md.to_markdown : md
199
+ end
179
200
  end
180
201
  end
@@ -160,6 +160,12 @@ module ActionController
160
160
  # render "posts/new", status: :unprocessable_entity
161
161
  # # => renders app/views/posts/new.html.erb with HTTP status code 422
162
162
  #
163
+ # `:variants`
164
+ # : This tells Rails to look for the first template matching any of the variations.
165
+ #
166
+ # render "posts/index", variants: [:mobile]
167
+ # # => renders app/views/posts/index.html+mobile.erb
168
+ #
163
169
  #--
164
170
  # Check for double render errors and set the content_type after rendering.
165
171
  def render(*args)
@@ -208,7 +214,7 @@ module ActionController
208
214
  end
209
215
 
210
216
  def _set_html_content_type
211
- self.content_type = Mime[:html].to_s
217
+ self.content_type = :html
212
218
  end
213
219
 
214
220
  def _set_rendered_content_type(format)
@@ -232,7 +238,7 @@ module ActionController
232
238
  end
233
239
 
234
240
  if options[:status]
235
- options[:status] = Rack::Utils.status_code(options[:status])
241
+ options[:status] = ActionDispatch::Response.rack_status_code(options[:status])
236
242
  end
237
243
 
238
244
  super
@@ -71,32 +71,39 @@ module ActionController # :nodoc:
71
71
  included do
72
72
  # Sets the token parameter name for RequestForgery. Calling
73
73
  # `protect_from_forgery` sets it to `:authenticity_token` by default.
74
- config_accessor :request_forgery_protection_token
74
+ singleton_class.delegate :request_forgery_protection_token, :request_forgery_protection_token=, to: :config
75
+ delegate :request_forgery_protection_token, :request_forgery_protection_token=, to: :config
75
76
  self.request_forgery_protection_token ||= :authenticity_token
76
77
 
77
78
  # Holds the class which implements the request forgery protection.
78
- config_accessor :forgery_protection_strategy
79
+ singleton_class.delegate :forgery_protection_strategy, :forgery_protection_strategy=, to: :config
80
+ delegate :forgery_protection_strategy, :forgery_protection_strategy=, to: :config
79
81
  self.forgery_protection_strategy = nil
80
82
 
81
83
  # Controls whether request forgery protection is turned on or not. Turned off by
82
84
  # default only in test mode.
83
- config_accessor :allow_forgery_protection
85
+ singleton_class.delegate :allow_forgery_protection, :allow_forgery_protection=, to: :config
86
+ delegate :allow_forgery_protection, :allow_forgery_protection=, to: :config
84
87
  self.allow_forgery_protection = true if allow_forgery_protection.nil?
85
88
 
86
89
  # Controls whether a CSRF failure logs a warning. On by default.
87
- config_accessor :log_warning_on_csrf_failure
90
+ singleton_class.delegate :log_warning_on_csrf_failure, :log_warning_on_csrf_failure=, to: :config
91
+ delegate :log_warning_on_csrf_failure, :log_warning_on_csrf_failure=, to: :config
88
92
  self.log_warning_on_csrf_failure = true
89
93
 
90
94
  # Controls whether the Origin header is checked in addition to the CSRF token.
91
- config_accessor :forgery_protection_origin_check
95
+ singleton_class.delegate :forgery_protection_origin_check, :forgery_protection_origin_check=, to: :config
96
+ delegate :forgery_protection_origin_check, :forgery_protection_origin_check=, to: :config
92
97
  self.forgery_protection_origin_check = false
93
98
 
94
99
  # Controls whether form-action/method specific CSRF tokens are used.
95
- config_accessor :per_form_csrf_tokens
100
+ singleton_class.delegate :per_form_csrf_tokens, :per_form_csrf_tokens=, to: :config
101
+ delegate :per_form_csrf_tokens, :per_form_csrf_tokens=, to: :config
96
102
  self.per_form_csrf_tokens = false
97
103
 
98
104
  # The strategy to use for storing and retrieving CSRF tokens.
99
- config_accessor :csrf_token_storage_strategy
105
+ singleton_class.delegate :csrf_token_storage_strategy, :csrf_token_storage_strategy=, to: :config
106
+ delegate :csrf_token_storage_strategy, :csrf_token_storage_strategy=, to: :config
100
107
  self.csrf_token_storage_strategy = SessionStore.new
101
108
 
102
109
  helper_method :form_authenticity_token
@@ -461,7 +468,7 @@ module ActionController # :nodoc:
461
468
  # * Does the `X-CSRF-Token` header match the form_authenticity_token?
462
469
  #
463
470
  def verified_request? # :doc:
464
- !protect_against_forgery? || request.get? || request.head? ||
471
+ request.get? || request.head? || !protect_against_forgery? ||
465
472
  (valid_request_origin? && any_authenticity_token_valid?)
466
473
  end
467
474
 
@@ -621,6 +628,7 @@ module ActionController # :nodoc:
621
628
  If you cannot change the referrer policy, you can disable origin checking with the
622
629
  Rails.application.config.action_controller.forgery_protection_origin_check setting.
623
630
  MSG
631
+ private_constant :NULL_ORIGIN_MESSAGE
624
632
 
625
633
  # Checks if the request originated from the same origin by looking at the Origin
626
634
  # header.
@@ -634,7 +642,7 @@ module ActionController # :nodoc:
634
642
  end
635
643
  end
636
644
 
637
- def normalize_action_path(action_path) # :doc:
645
+ def normalize_action_path(action_path)
638
646
  uri = URI.parse(action_path)
639
647
 
640
648
  if uri.relative? && (action_path.blank? || !action_path.start_with?("/"))
@@ -644,7 +652,7 @@ module ActionController # :nodoc:
644
652
  end
645
653
  end
646
654
 
647
- def normalize_relative_action_path(rel_action_path) # :doc:
655
+ def normalize_relative_action_path(rel_action_path)
648
656
  uri = URI.parse(request.path)
649
657
  # add the action path to the request.path
650
658
  uri.path += "/#{rel_action_path}"
@@ -13,6 +13,15 @@ module ActionController # :nodoc:
13
13
  extend ActiveSupport::Concern
14
14
  include ActiveSupport::Rescuable
15
15
 
16
+ module ClassMethods
17
+ def handler_for_rescue(exception, ...) # :nodoc:
18
+ if handler = super
19
+ ActiveSupport::Notifications.instrument("rescue_from_callback.action_controller", exception: exception)
20
+ handler
21
+ end
22
+ end
23
+ end
24
+
16
25
  # Override this method if you want to customize when detailed exceptions must be
17
26
  # shown. This method is only called when `consider_all_requests_local` is
18
27
  # `false`. By default, it returns `false`, but someone may set it to
@@ -13,8 +13,10 @@ module ActionController
13
13
  class Railtie < Rails::Railtie # :nodoc:
14
14
  config.action_controller = ActiveSupport::OrderedOptions.new
15
15
  config.action_controller.raise_on_open_redirects = false
16
+ config.action_controller.action_on_path_relative_redirect = :log
16
17
  config.action_controller.log_query_tags_around_actions = true
17
18
  config.action_controller.wrap_parameters_by_default = false
19
+ config.action_controller.allowed_redirect_hosts = []
18
20
 
19
21
  config.eager_load_namespaces << AbstractController
20
22
  config.eager_load_namespaces << ActionController
@@ -93,12 +95,6 @@ module ActionController
93
95
  end
94
96
  end
95
97
 
96
- initializer "action_controller.compile_config_methods" do
97
- ActiveSupport.on_load(:action_controller) do
98
- config.compile_methods! if config.respond_to?(:compile_methods!)
99
- end
100
- end
101
-
102
98
  initializer "action_controller.request_forgery_protection" do |app|
103
99
  ActiveSupport.on_load(:action_controller_base) do
104
100
  if app.config.action_controller.default_protect_from_forgery
@@ -96,7 +96,6 @@ module ActionController
96
96
  # * `:script_name` - The portion of the incoming request's URL path that
97
97
  # corresponds to the application. Converts to Rack's `SCRIPT_NAME`.
98
98
  # * `:input` - The input stream. Converts to Rack's `rack.input`.
99
- #
100
99
  # * `defaults` - Default values for the Rack env. Entries are specified in the
101
100
  # same format as `env`. `env` will be merged on top of these values.
102
101
  # `defaults` will be retained when calling #new on a renderer instance.
@@ -30,5 +30,11 @@ module ActionDispatch
30
30
  SERVER_TIMING = "server-timing"
31
31
  STRICT_TRANSPORT_SECURITY = "strict-transport-security"
32
32
  end
33
+
34
+ if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3.1")
35
+ UNPROCESSABLE_CONTENT = :unprocessable_entity
36
+ else
37
+ UNPROCESSABLE_CONTENT = :unprocessable_content
38
+ end
33
39
  end
34
40
  end