actionpack 5.2.8.1 → 6.0.6

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.

Potentially problematic release.


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

Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +270 -347
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/abstract_controller/base.rb +4 -3
  6. data/lib/abstract_controller/caching/fragments.rb +6 -22
  7. data/lib/abstract_controller/caching.rb +1 -1
  8. data/lib/abstract_controller/callbacks.rb +12 -0
  9. data/lib/abstract_controller/collector.rb +1 -2
  10. data/lib/abstract_controller/helpers.rb +7 -6
  11. data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
  12. data/lib/abstract_controller/translation.rb +4 -4
  13. data/lib/action_controller/api.rb +2 -1
  14. data/lib/action_controller/base.rb +2 -7
  15. data/lib/action_controller/caching.rb +1 -2
  16. data/lib/action_controller/log_subscriber.rb +8 -5
  17. data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
  18. data/lib/action_controller/metal/conditional_get.rb +9 -3
  19. data/lib/action_controller/metal/content_security_policy.rb +0 -1
  20. data/lib/action_controller/metal/data_streaming.rb +5 -6
  21. data/lib/action_controller/metal/default_headers.rb +17 -0
  22. data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
  23. data/lib/action_controller/metal/exceptions.rb +23 -2
  24. data/lib/action_controller/metal/flash.rb +5 -5
  25. data/lib/action_controller/metal/force_ssl.rb +15 -56
  26. data/lib/action_controller/metal/head.rb +1 -1
  27. data/lib/action_controller/metal/helpers.rb +3 -4
  28. data/lib/action_controller/metal/http_authentication.rb +20 -21
  29. data/lib/action_controller/metal/implicit_render.rb +4 -14
  30. data/lib/action_controller/metal/instrumentation.rb +3 -6
  31. data/lib/action_controller/metal/live.rb +29 -31
  32. data/lib/action_controller/metal/mime_responds.rb +13 -2
  33. data/lib/action_controller/metal/params_wrapper.rb +18 -14
  34. data/lib/action_controller/metal/redirecting.rb +5 -5
  35. data/lib/action_controller/metal/renderers.rb +4 -4
  36. data/lib/action_controller/metal/rendering.rb +2 -3
  37. data/lib/action_controller/metal/request_forgery_protection.rb +25 -48
  38. data/lib/action_controller/metal/streaming.rb +0 -1
  39. data/lib/action_controller/metal/strong_parameters.rb +65 -44
  40. data/lib/action_controller/metal/url_for.rb +1 -1
  41. data/lib/action_controller/metal.rb +8 -6
  42. data/lib/action_controller/railties/helpers.rb +1 -1
  43. data/lib/action_controller/renderer.rb +17 -3
  44. data/lib/action_controller/template_assertions.rb +1 -1
  45. data/lib/action_controller/test_case.rb +7 -8
  46. data/lib/action_controller.rb +5 -1
  47. data/lib/action_dispatch/http/cache.rb +14 -11
  48. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  49. data/lib/action_dispatch/http/content_security_policy.rb +28 -17
  50. data/lib/action_dispatch/http/filter_parameters.rb +8 -7
  51. data/lib/action_dispatch/http/filter_redirect.rb +1 -2
  52. data/lib/action_dispatch/http/headers.rb +1 -2
  53. data/lib/action_dispatch/http/mime_negotiation.rb +13 -6
  54. data/lib/action_dispatch/http/mime_type.rb +14 -8
  55. data/lib/action_dispatch/http/parameter_filter.rb +5 -79
  56. data/lib/action_dispatch/http/parameters.rb +15 -6
  57. data/lib/action_dispatch/http/request.rb +21 -14
  58. data/lib/action_dispatch/http/response.rb +40 -21
  59. data/lib/action_dispatch/http/upload.rb +9 -1
  60. data/lib/action_dispatch/http/url.rb +81 -82
  61. data/lib/action_dispatch/journey/formatter.rb +2 -3
  62. data/lib/action_dispatch/journey/gtg/builder.rb +0 -1
  63. data/lib/action_dispatch/journey/gtg/transition_table.rb +0 -1
  64. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
  65. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -1
  66. data/lib/action_dispatch/journey/nodes/node.rb +9 -8
  67. data/lib/action_dispatch/journey/path/pattern.rb +6 -3
  68. data/lib/action_dispatch/journey/route.rb +5 -4
  69. data/lib/action_dispatch/journey/router/utils.rb +10 -10
  70. data/lib/action_dispatch/journey/router.rb +0 -4
  71. data/lib/action_dispatch/journey/routes.rb +0 -2
  72. data/lib/action_dispatch/journey/scanner.rb +10 -4
  73. data/lib/action_dispatch/journey/visitors.rb +1 -4
  74. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  75. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  76. data/lib/action_dispatch/middleware/cookies.rb +62 -78
  77. data/lib/action_dispatch/middleware/debug_exceptions.rb +45 -61
  78. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  79. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  80. data/lib/action_dispatch/middleware/exception_wrapper.rb +49 -16
  81. data/lib/action_dispatch/middleware/flash.rb +1 -1
  82. data/lib/action_dispatch/middleware/host_authorization.rb +121 -0
  83. data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
  84. data/lib/action_dispatch/middleware/remote_ip.rb +9 -12
  85. data/lib/action_dispatch/middleware/request_id.rb +2 -2
  86. data/lib/action_dispatch/middleware/session/abstract_store.rb +0 -1
  87. data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -7
  88. data/lib/action_dispatch/middleware/show_exceptions.rb +1 -2
  89. data/lib/action_dispatch/middleware/ssl.rb +8 -8
  90. data/lib/action_dispatch/middleware/stack.rb +38 -2
  91. data/lib/action_dispatch/middleware/static.rb +6 -7
  92. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  93. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  94. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
  95. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  96. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  97. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  98. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  99. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -4
  101. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  102. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +7 -4
  103. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +5 -2
  104. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -0
  105. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  106. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  107. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  108. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  109. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
  110. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -0
  111. data/lib/action_dispatch/railtie.rb +7 -2
  112. data/lib/action_dispatch/request/session.rb +9 -2
  113. data/lib/action_dispatch/routing/inspector.rb +97 -50
  114. data/lib/action_dispatch/routing/mapper.rb +63 -42
  115. data/lib/action_dispatch/routing/polymorphic_routes.rb +3 -6
  116. data/lib/action_dispatch/routing/route_set.rb +25 -31
  117. data/lib/action_dispatch/routing/url_for.rb +2 -2
  118. data/lib/action_dispatch/routing.rb +21 -20
  119. data/lib/action_dispatch/system_test_case.rb +44 -6
  120. data/lib/action_dispatch/system_testing/browser.rb +38 -7
  121. data/lib/action_dispatch/system_testing/driver.rb +11 -2
  122. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +6 -5
  123. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +7 -6
  124. data/lib/action_dispatch/testing/assertion_response.rb +0 -1
  125. data/lib/action_dispatch/testing/assertions/response.rb +2 -3
  126. data/lib/action_dispatch/testing/assertions/routing.rb +15 -3
  127. data/lib/action_dispatch/testing/assertions.rb +1 -1
  128. data/lib/action_dispatch/testing/integration.rb +33 -12
  129. data/lib/action_dispatch/testing/request_encoder.rb +2 -2
  130. data/lib/action_dispatch/testing/test_process.rb +2 -2
  131. data/lib/action_dispatch/testing/test_response.rb +4 -32
  132. data/lib/action_dispatch.rb +7 -2
  133. data/lib/action_pack/gem_version.rb +4 -4
  134. data/lib/action_pack.rb +1 -1
  135. metadata +29 -15
  136. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -34,7 +34,7 @@ module ActionController
34
34
  # end
35
35
  # end
36
36
  #
37
- # Then, in any view rendered by <tt>EventController</tt>, the <tt>format_time</tt> method can be called:
37
+ # Then, in any view rendered by <tt>EventsController</tt>, the <tt>format_time</tt> method can be called:
38
38
  #
39
39
  # <% @events.each do |event| -%>
40
40
  # <p>
@@ -75,7 +75,7 @@ module ActionController
75
75
  # Provides a proxy to access helper methods from outside the view.
76
76
  def helpers
77
77
  @helper_proxy ||= begin
78
- proxy = ActionView::Base.new
78
+ proxy = ActionView::Base.empty
79
79
  proxy.config = config.inheritable_copy
80
80
  proxy.extend(_helpers)
81
81
  end
@@ -100,8 +100,7 @@ module ActionController
100
100
  # # => ["application", "chart", "rubygems"]
101
101
  def all_helpers_from_path(path)
102
102
  helpers = Array(path).flat_map do |_path|
103
- extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
104
- names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1'.freeze) }
103
+ names = Dir["#{_path}/**/*_helper.rb"].map { |file| file[_path.to_s.size + 1..-"_helper.rb".size - 1] }
105
104
  names.sort!
106
105
  end
107
106
  helpers.uniq!
@@ -56,8 +56,9 @@ module ActionController
56
56
  # In your integration tests, you can do something like this:
57
57
  #
58
58
  # def test_access_granted_from_xml
59
- # @request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
60
- # get "/notes/1.xml"
59
+ # authorization = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
60
+ #
61
+ # get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
61
62
  #
62
63
  # assert_equal 200, status
63
64
  # end
@@ -68,21 +69,20 @@ module ActionController
68
69
  extend ActiveSupport::Concern
69
70
 
70
71
  module ClassMethods
71
- def http_basic_authenticate_with(options = {})
72
- before_action(options.except(:name, :password, :realm)) do
73
- authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
74
- # This comparison uses & so that it doesn't short circuit and
75
- # uses `secure_compare` so that length information
76
- # isn't leaked.
77
- ActiveSupport::SecurityUtils.secure_compare(name, options[:name]) &
78
- ActiveSupport::SecurityUtils.secure_compare(password, options[:password])
79
- end
80
- end
72
+ def http_basic_authenticate_with(name:, password:, realm: nil, **options)
73
+ before_action(options) { http_basic_authenticate_or_request_with name: name, password: password, realm: realm }
74
+ end
75
+ end
76
+
77
+ def http_basic_authenticate_or_request_with(name:, password:, realm: nil, message: nil)
78
+ authenticate_or_request_with_http_basic(realm, message) do |given_name, given_password|
79
+ ActiveSupport::SecurityUtils.secure_compare(given_name, name) &
80
+ ActiveSupport::SecurityUtils.secure_compare(given_password, password)
81
81
  end
82
82
  end
83
83
 
84
- def authenticate_or_request_with_http_basic(realm = "Application", message = nil, &login_procedure)
85
- authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm, message)
84
+ def authenticate_or_request_with_http_basic(realm = nil, message = nil, &login_procedure)
85
+ authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm || "Application", message)
86
86
  end
87
87
 
88
88
  def authenticate_with_http_basic(&login_procedure)
@@ -126,7 +126,7 @@ module ActionController
126
126
 
127
127
  def authentication_request(controller, realm, message)
128
128
  message ||= "HTTP Basic: Access denied.\n"
129
- controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"'.freeze, "".freeze)}")
129
+ controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"', "")}")
130
130
  controller.status = 401
131
131
  controller.response_body = message
132
132
  end
@@ -389,10 +389,9 @@ module ActionController
389
389
  # In your integration tests, you can do something like this:
390
390
  #
391
391
  # def test_access_granted_from_xml
392
- # get(
393
- # "/notes/1.xml", nil,
394
- # 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
395
- # )
392
+ # authorization = ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
393
+ #
394
+ # get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
396
395
  #
397
396
  # assert_equal 200, status
398
397
  # end
@@ -474,7 +473,7 @@ module ActionController
474
473
 
475
474
  # This removes the <tt>"</tt> characters wrapping the value.
476
475
  def rewrite_param_values(array_params)
477
- array_params.each { |param| (param[1] || "".dup).gsub! %r/^"|"$/, "" }
476
+ array_params.each { |param| (param[1] || +"").gsub! %r/^"|"$/, "" }
478
477
  end
479
478
 
480
479
  # This method takes an authorization body and splits up the key-value
@@ -511,7 +510,7 @@ module ActionController
511
510
  # Returns nothing.
512
511
  def authentication_request(controller, realm, message = nil)
513
512
  message ||= "HTTP Token: Access denied.\n"
514
- controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"'.freeze, "".freeze)}")
513
+ controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"', "")}")
515
514
  controller.__send__ :render, plain: message, status: :unauthorized
516
515
  end
517
516
  end
@@ -30,9 +30,9 @@ module ActionController
30
30
  # :stopdoc:
31
31
  include BasicImplicitRender
32
32
 
33
- def default_render(*args)
33
+ def default_render
34
34
  if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
35
- render(*args)
35
+ render
36
36
  elsif any_templates?(action_name.to_s, _prefixes)
37
37
  message = "#{self.class.name}\##{action_name} is missing a template " \
38
38
  "for this request format and variant.\n" \
@@ -41,18 +41,8 @@ module ActionController
41
41
 
42
42
  raise ActionController::UnknownFormat, message
43
43
  elsif interactive_browser_request?
44
- message = "#{self.class.name}\##{action_name} is missing a template " \
45
- "for this request format and variant.\n\n" \
46
- "request.formats: #{request.formats.map(&:to_s).inspect}\n" \
47
- "request.variant: #{request.variant.inspect}\n\n" \
48
- "NOTE! For XHR/Ajax or API requests, this action would normally " \
49
- "respond with 204 No Content: an empty white screen. Since you're " \
50
- "loading it in a web browser, we assume that you expected to " \
51
- "actually render a template, not nothing, so we're showing an " \
52
- "error to be extra-clear. If you expect 204 No Content, carry on. " \
53
- "That's what you'll get from an XHR or API request. Give it a shot."
54
-
55
- raise ActionController::UnknownFormat, message
44
+ message = "#{self.class.name}\##{action_name} is missing a template for request formats: #{request.formats.map(&:to_s).join(',')}"
45
+ raise ActionController::MissingExactTemplate, message
56
46
  else
57
47
  logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
58
48
  super
@@ -30,13 +30,11 @@ module ActionController
30
30
  ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
31
31
 
32
32
  ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
33
- begin
34
- result = super
33
+ super.tap do
35
34
  payload[:status] = response.status
36
- result
37
- ensure
38
- append_info_to_payload(payload)
39
35
  end
36
+ ensure
37
+ append_info_to_payload(payload)
40
38
  end
41
39
  end
42
40
 
@@ -71,7 +69,6 @@ module ActionController
71
69
  end
72
70
 
73
71
  private
74
-
75
72
  # A hook invoked every time a before callback is halted.
76
73
  def halted_callback_hook(filter)
77
74
  ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
@@ -86,7 +86,7 @@ module ActionController
86
86
  # Note: SSEs are not currently supported by IE. However, they are supported
87
87
  # by Chrome, Firefox, Opera, and Safari.
88
88
  class SSE
89
- WHITELISTED_OPTIONS = %w( retry event id )
89
+ PERMITTED_OPTIONS = %w( retry event id )
90
90
 
91
91
  def initialize(stream, options = {})
92
92
  @stream = stream
@@ -107,17 +107,16 @@ module ActionController
107
107
  end
108
108
 
109
109
  private
110
-
111
110
  def perform_write(json, options)
112
111
  current_options = @options.merge(options).stringify_keys
113
112
 
114
- WHITELISTED_OPTIONS.each do |option_name|
113
+ PERMITTED_OPTIONS.each do |option_name|
115
114
  if (option_value = current_options[option_name])
116
115
  @stream.write "#{option_name}: #{option_value}\n"
117
116
  end
118
117
  end
119
118
 
120
- message = json.gsub("\n".freeze, "\ndata: ".freeze)
119
+ message = json.gsub("\n", "\ndata: ")
121
120
  @stream.write "data: #{message}\n\n"
122
121
  end
123
122
  end
@@ -146,7 +145,7 @@ module ActionController
146
145
 
147
146
  def write(string)
148
147
  unless @response.committed?
149
- @response.set_header "Cache-Control", "no-cache"
148
+ @response.headers["Cache-Control"] ||= "no-cache"
150
149
  @response.delete_header "Content-Length"
151
150
  end
152
151
 
@@ -205,7 +204,6 @@ module ActionController
205
204
  end
206
205
 
207
206
  private
208
-
209
207
  def each_chunk(&block)
210
208
  loop do
211
209
  str = nil
@@ -220,7 +218,6 @@ module ActionController
220
218
 
221
219
  class Response < ActionDispatch::Response #:nodoc: all
222
220
  private
223
-
224
221
  def before_committed
225
222
  super
226
223
  jar = request.cookie_jar
@@ -280,33 +277,34 @@ module ActionController
280
277
  raise error if error
281
278
  end
282
279
 
283
- # Spawn a new thread to serve up the controller in. This is to get
284
- # around the fact that Rack isn't based around IOs and we need to use
285
- # a thread to stream data from the response bodies. Nobody should call
286
- # this method except in Rails internals. Seriously!
287
- def new_controller_thread # :nodoc:
288
- Thread.new {
289
- t2 = Thread.current
290
- t2.abort_on_exception = true
291
- yield
292
- }
293
- end
294
-
295
- def log_error(exception)
296
- logger = ActionController::Base.logger
297
- return unless logger
298
-
299
- logger.fatal do
300
- message = "\n#{exception.class} (#{exception.message}):\n".dup
301
- message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
302
- message << " " << exception.backtrace.join("\n ")
303
- "#{message}\n\n"
304
- end
305
- end
306
-
307
280
  def response_body=(body)
308
281
  super
309
282
  response.close if response
310
283
  end
284
+
285
+ private
286
+ # Spawn a new thread to serve up the controller in. This is to get
287
+ # around the fact that Rack isn't based around IOs and we need to use
288
+ # a thread to stream data from the response bodies. Nobody should call
289
+ # this method except in Rails internals. Seriously!
290
+ def new_controller_thread # :nodoc:
291
+ Thread.new {
292
+ t2 = Thread.current
293
+ t2.abort_on_exception = true
294
+ yield
295
+ }
296
+ end
297
+
298
+ def log_error(exception)
299
+ logger = ActionController::Base.logger
300
+ return unless logger
301
+
302
+ logger.fatal do
303
+ message = +"\n#{exception.class} (#{exception.message}):\n"
304
+ message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
305
+ message << " " << exception.backtrace.join("\n ")
306
+ "#{message}\n\n"
307
+ end
308
+ end
311
309
  end
312
310
  end
@@ -11,7 +11,7 @@ module ActionController #:nodoc:
11
11
  # @people = Person.all
12
12
  # end
13
13
  #
14
- # That action implicitly responds to all formats, but formats can also be whitelisted:
14
+ # That action implicitly responds to all formats, but formats can also be explicitly enumerated:
15
15
  #
16
16
  # def index
17
17
  # @people = Person.all
@@ -105,7 +105,7 @@ module ActionController #:nodoc:
105
105
  #
106
106
  # Mime::Type.register "image/jpg", :jpg
107
107
  #
108
- # Respond to also allows you to specify a common block for different formats by using +any+:
108
+ # +respond_to+ also allows you to specify a common block for different formats by using +any+:
109
109
  #
110
110
  # def index
111
111
  # @people = Person.all
@@ -124,6 +124,14 @@ module ActionController #:nodoc:
124
124
  #
125
125
  # render json: @people
126
126
  #
127
+ # +any+ can also be used with no arguments, in which case it will be used for any format requested by
128
+ # the user:
129
+ #
130
+ # respond_to do |format|
131
+ # format.html
132
+ # format.any { redirect_to support_path }
133
+ # end
134
+ #
127
135
  # Formats can have different variants.
128
136
  #
129
137
  # The request variant is a specialization of the request format, like <tt>:tablet</tt>,
@@ -197,6 +205,9 @@ module ActionController #:nodoc:
197
205
  yield collector if block_given?
198
206
 
199
207
  if format = collector.negotiate_format(request)
208
+ if media_type && media_type != format
209
+ raise ActionController::RespondToMismatchError
210
+ end
200
211
  _process_format(format)
201
212
  _set_rendered_content_type format
202
213
  response = collector.response
@@ -115,7 +115,7 @@ module ActionController
115
115
 
116
116
  if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any?
117
117
  self.include += m.nested_attributes_options.keys.map do |key|
118
- key.to_s.dup.concat("_attributes")
118
+ (+key.to_s).concat("_attributes")
119
119
  end
120
120
  end
121
121
 
@@ -241,23 +241,11 @@ module ActionController
241
241
  # Performs parameters wrapping upon the request. Called automatically
242
242
  # by the metal call stack.
243
243
  def process_action(*args)
244
- if _wrapper_enabled?
245
- wrapped_hash = _wrap_parameters request.request_parameters
246
- wrapped_keys = request.request_parameters.keys
247
- wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
248
-
249
- # This will make the wrapped hash accessible from controller and view.
250
- request.parameters.merge! wrapped_hash
251
- request.request_parameters.merge! wrapped_hash
252
-
253
- # This will display the wrapped hash in the log file.
254
- request.filtered_parameters.merge! wrapped_filtered_hash
255
- end
244
+ _perform_parameter_wrapping if _wrapper_enabled?
256
245
  super
257
246
  end
258
247
 
259
248
  private
260
-
261
249
  # Returns the wrapper key which will be used to store wrapped parameters.
262
250
  def _wrapper_key
263
251
  _wrapper_options.name
@@ -287,7 +275,23 @@ module ActionController
287
275
  return false unless request.has_content_type?
288
276
 
289
277
  ref = request.content_mime_type.ref
278
+
290
279
  _wrapper_formats.include?(ref) && _wrapper_key && !request.parameters.key?(_wrapper_key)
280
+ rescue ActionDispatch::Http::Parameters::ParseError
281
+ false
282
+ end
283
+
284
+ def _perform_parameter_wrapping
285
+ wrapped_hash = _wrap_parameters request.request_parameters
286
+ wrapped_keys = request.request_parameters.keys
287
+ wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
288
+
289
+ # This will make the wrapped hash accessible from controller and view.
290
+ request.parameters.merge! wrapped_hash
291
+ request.request_parameters.merge! wrapped_hash
292
+
293
+ # This will display the wrapped hash in the log file.
294
+ request.filtered_parameters.merge! wrapped_filtered_hash
291
295
  end
292
296
  end
293
297
  end
@@ -55,11 +55,11 @@ module ActionController
55
55
  # Statements after +redirect_to+ in our controller get executed, so +redirect_to+ doesn't stop the execution of the function.
56
56
  # To terminate the execution of the function immediately after the +redirect_to+, use return.
57
57
  # redirect_to post_url(@post) and return
58
- def redirect_to(options = {}, response_status = {})
58
+ def redirect_to(options = {}, response_options = {})
59
59
  raise ActionControllerError.new("Cannot redirect to nil!") unless options
60
60
  raise AbstractController::DoubleRenderError if response_body
61
61
 
62
- self.status = _extract_redirect_to_status(options, response_status)
62
+ self.status = _extract_redirect_to_status(options, response_options)
63
63
  self.location = _compute_redirect_to_location(request, options)
64
64
  self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
65
65
  end
@@ -114,11 +114,11 @@ module ActionController
114
114
  public :_compute_redirect_to_location
115
115
 
116
116
  private
117
- def _extract_redirect_to_status(options, response_status)
117
+ def _extract_redirect_to_status(options, response_options)
118
118
  if options.is_a?(Hash) && options.key?(:status)
119
119
  Rack::Utils.status_code(options.delete(:status))
120
- elsif response_status.key?(:status)
121
- Rack::Utils.status_code(response_status[:status])
120
+ elsif response_options.key?(:status)
121
+ Rack::Utils.status_code(response_options[:status])
122
122
  else
123
123
  302
124
124
  end
@@ -157,24 +157,24 @@ module ActionController
157
157
  json = json.to_json(options) unless json.kind_of?(String)
158
158
 
159
159
  if options[:callback].present?
160
- if content_type.nil? || content_type == Mime[:json]
160
+ if media_type.nil? || media_type == Mime[:json]
161
161
  self.content_type = Mime[:js]
162
162
  end
163
163
 
164
164
  "/**/#{options[:callback]}(#{json})"
165
165
  else
166
- self.content_type ||= Mime[:json]
166
+ self.content_type = Mime[:json] if media_type.nil?
167
167
  json
168
168
  end
169
169
  end
170
170
 
171
171
  add :js do |js, options|
172
- self.content_type ||= Mime[:js]
172
+ self.content_type = Mime[:js] if media_type.nil?
173
173
  js.respond_to?(:to_js) ? js.to_js(options) : js
174
174
  end
175
175
 
176
176
  add :xml do |xml, options|
177
- self.content_type ||= Mime[:xml]
177
+ self.content_type = Mime[:xml] if media_type.nil?
178
178
  xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
179
179
  end
180
180
  end
@@ -40,7 +40,7 @@ module ActionController
40
40
  def render_to_string(*)
41
41
  result = super
42
42
  if result.respond_to?(:each)
43
- string = "".dup
43
+ string = +""
44
44
  result.each { |r| string << r }
45
45
  string
46
46
  else
@@ -53,7 +53,6 @@ module ActionController
53
53
  end
54
54
 
55
55
  private
56
-
57
56
  def _process_variant(options)
58
57
  if defined?(request) && !request.nil? && request.variant.present?
59
58
  options[:variant] = request.variant
@@ -73,7 +72,7 @@ module ActionController
73
72
  end
74
73
 
75
74
  def _set_rendered_content_type(format)
76
- if format && !response.content_type
75
+ if format && !response.media_type
77
76
  self.content_type = format.to_s
78
77
  end
79
78
  end
@@ -3,7 +3,6 @@
3
3
  require "rack/session/abstract/id"
4
4
  require "action_controller/metal/exceptions"
5
5
  require "active_support/security_utils"
6
- require "active_support/core_ext/string/strip"
7
6
 
8
7
  module ActionController #:nodoc:
9
8
  class InvalidAuthenticityToken < ActionControllerError #:nodoc:
@@ -18,7 +17,7 @@ module ActionController #:nodoc:
18
17
  # access. When a request reaches your application, \Rails verifies the received
19
18
  # token with the token in the session. All requests are checked except GET requests
20
19
  # as these should be idempotent. Keep in mind that all session-oriented requests
21
- # should be CSRF protected, including JavaScript and HTML requests.
20
+ # are CSRF protected by default, including JavaScript and HTML requests.
22
21
  #
23
22
  # Since HTML and JavaScript requests are typically made from the browser, we
24
23
  # need to ensure to verify request authenticity for the web browser. We can
@@ -31,31 +30,30 @@ module ActionController #:nodoc:
31
30
  # URL on your site. When your JavaScript response loads on their site, it executes.
32
31
  # With carefully crafted JavaScript on their end, sensitive data in your JavaScript
33
32
  # response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or
34
- # Ajax) requests are allowed to make GET requests for JavaScript responses.
33
+ # Ajax) requests are allowed to make requests for JavaScript responses.
35
34
  #
36
- # It's important to remember that XML or JSON requests are also affected and if
37
- # you're building an API you should change forgery protection method in
38
- # <tt>ApplicationController</tt> (by default: <tt>:exception</tt>):
35
+ # Subclasses of <tt>ActionController::Base</tt> are protected by default with the
36
+ # <tt>:exception</tt> strategy, which raises an
37
+ # <tt>ActionController::InvalidAuthenticityToken</tt> error on unverified requests.
38
+ #
39
+ # APIs may want to disable this behavior since they are typically designed to be
40
+ # state-less: that is, the request API client handles the session instead of Rails.
41
+ # One way to achieve this is to use the <tt>:null_session</tt> strategy instead,
42
+ # which allows unverified requests to be handled, but with an empty session:
39
43
  #
40
44
  # class ApplicationController < ActionController::Base
41
- # protect_from_forgery unless: -> { request.format.json? }
45
+ # protect_from_forgery with: :null_session
42
46
  # end
43
47
  #
44
- # CSRF protection is turned on with the <tt>protect_from_forgery</tt> method.
45
- # By default <tt>protect_from_forgery</tt> protects your session with
46
- # <tt>:null_session</tt> method, which provides an empty session
47
- # during request.
48
- #
49
- # We may want to disable CSRF protection for APIs since they are typically
50
- # designed to be state-less. That is, the request API client will handle
51
- # the session for you instead of Rails.
48
+ # Note that API only applications don't include this module or a session middleware
49
+ # by default, and so don't require CSRF protection to be configured.
52
50
  #
53
51
  # The token parameter is named <tt>authenticity_token</tt> by default. The name and
54
52
  # value of this token must be added to every layout that renders forms by including
55
53
  # <tt>csrf_meta_tags</tt> in the HTML +head+.
56
54
  #
57
55
  # Learn more about CSRF attacks and securing your application in the
58
- # {Ruby on Rails Security Guide}[http://guides.rubyonrails.org/security.html].
56
+ # {Ruby on Rails Security Guide}[https://guides.rubyonrails.org/security.html].
59
57
  module RequestForgeryProtection
60
58
  extend ActiveSupport::Concern
61
59
 
@@ -149,7 +147,6 @@ module ActionController #:nodoc:
149
147
  end
150
148
 
151
149
  private
152
-
153
150
  def protection_method_class(name)
154
151
  ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
155
152
  rescue NameError
@@ -173,7 +170,6 @@ module ActionController #:nodoc:
173
170
  end
174
171
 
175
172
  private
176
-
177
173
  class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
178
174
  def initialize(req)
179
175
  super(nil, req)
@@ -280,7 +276,7 @@ module ActionController #:nodoc:
280
276
 
281
277
  # Check for cross-origin JavaScript responses.
282
278
  def non_xhr_javascript_response? # :doc:
283
- content_type =~ %r(\Atext/javascript) && !request.xhr?
279
+ %r(\A(?:text|application)/javascript).match?(media_type) && !request.xhr?
284
280
  end
285
281
 
286
282
  AUTHENTICITY_TOKEN_LENGTH = 32
@@ -425,9 +421,14 @@ module ActionController #:nodoc:
425
421
  end
426
422
 
427
423
  def xor_byte_strings(s1, s2) # :doc:
428
- s2_bytes = s2.bytes
429
- s1.each_byte.with_index { |c1, i| s2_bytes[i] ^= c1 }
430
- s2_bytes.pack("C*")
424
+ s2 = s2.dup
425
+ size = s1.bytesize
426
+ i = 0
427
+ while i < size
428
+ s2.setbyte(i, s1.getbyte(i) ^ s2.getbyte(i))
429
+ i += 1
430
+ end
431
+ s2
431
432
  end
432
433
 
433
434
  # The form's authenticity parameter. Override to provide your own.
@@ -440,11 +441,11 @@ module ActionController #:nodoc:
440
441
  allow_forgery_protection
441
442
  end
442
443
 
443
- NULL_ORIGIN_MESSAGE = <<-MSG.strip_heredoc
444
+ NULL_ORIGIN_MESSAGE = <<~MSG
444
445
  The browser returned a 'null' origin for a request with origin-based forgery protection turned on. This usually
445
446
  means you have the 'no-referrer' Referrer-Policy header enabled, or that the request came from a site that
446
447
  refused to give its origin. This makes it impossible for Rails to verify the source of the requests. Likely the
447
- best solution is to change your referrer policy to something less strict like same-origin or strict-same-origin.
448
+ best solution is to change your referrer policy to something less strict like same-origin or strict-origin.
448
449
  If you cannot change the referrer policy, you can disable origin checking with the
449
450
  Rails.application.config.action_controller.forgery_protection_origin_check setting.
450
451
  MSG
@@ -474,30 +475,6 @@ module ActionController #:nodoc:
474
475
  end
475
476
  end
476
477
 
477
- if RUBY_VERSION.start_with?("2.2")
478
- # Backported https://github.com/ruby/ruby/commit/6b6680945ed3274cddbc34fdfd410d74081a3e94
479
- using Module.new {
480
- refine Base64.singleton_class do
481
- def urlsafe_encode64(bin, padding: true)
482
- str = strict_encode64(bin).tr("+/", "-_")
483
- str = str.delete("=") unless padding
484
- str
485
- end
486
-
487
- def urlsafe_decode64(str)
488
- # NOTE: RFC 4648 does say nothing about unpadded input, but says that
489
- # "the excess pad characters MAY also be ignored", so it is inferred that
490
- # unpadded input is also acceptable.
491
- str = str.tr("-_", "+/")
492
- if !str.end_with?("=") && str.length % 4 != 0
493
- str = str.ljust((str.length + 3) & ~3, "=")
494
- end
495
- strict_decode64(str)
496
- end
497
- end
498
- }
499
- end
500
-
501
478
  def encode_csrf_token(csrf_token) # :nodoc:
502
479
  if urlsafe_csrf_tokens
503
480
  Base64.urlsafe_encode64(csrf_token, padding: false)
@@ -196,7 +196,6 @@ module ActionController #:nodoc:
196
196
  extend ActiveSupport::Concern
197
197
 
198
198
  private
199
-
200
199
  # Set proper cache control and transfer encoding when streaming
201
200
  def _process_options(options)
202
201
  super