actionpack 6.1.7.5 → 7.1.3.1

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 (160) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +355 -435
  3. data/MIT-LICENSE +2 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +33 -37
  7. data/lib/abstract_controller/caching/fragments.rb +4 -2
  8. data/lib/abstract_controller/caching.rb +1 -1
  9. data/lib/abstract_controller/callbacks.rb +50 -11
  10. data/lib/abstract_controller/collector.rb +2 -2
  11. data/lib/abstract_controller/deprecator.rb +7 -0
  12. data/lib/abstract_controller/error.rb +1 -1
  13. data/lib/abstract_controller/helpers.rb +78 -30
  14. data/lib/abstract_controller/logger.rb +1 -1
  15. data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
  16. data/lib/abstract_controller/rendering.rb +12 -14
  17. data/lib/abstract_controller/translation.rb +26 -7
  18. data/lib/abstract_controller/url_for.rb +6 -6
  19. data/lib/abstract_controller.rb +6 -0
  20. data/lib/action_controller/api.rb +12 -10
  21. data/lib/action_controller/base.rb +8 -21
  22. data/lib/action_controller/caching.rb +2 -0
  23. data/lib/action_controller/deprecator.rb +7 -0
  24. data/lib/action_controller/form_builder.rb +4 -2
  25. data/lib/action_controller/log_subscriber.rb +20 -7
  26. data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
  27. data/lib/action_controller/metal/conditional_get.rb +137 -102
  28. data/lib/action_controller/metal/content_security_policy.rb +37 -3
  29. data/lib/action_controller/metal/cookies.rb +1 -1
  30. data/lib/action_controller/metal/data_streaming.rb +25 -31
  31. data/lib/action_controller/metal/default_headers.rb +2 -0
  32. data/lib/action_controller/metal/etag_with_flash.rb +3 -1
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
  34. data/lib/action_controller/metal/exceptions.rb +27 -30
  35. data/lib/action_controller/metal/flash.rb +6 -2
  36. data/lib/action_controller/metal/head.rb +9 -7
  37. data/lib/action_controller/metal/helpers.rb +5 -16
  38. data/lib/action_controller/metal/http_authentication.rb +78 -42
  39. data/lib/action_controller/metal/implicit_render.rb +5 -3
  40. data/lib/action_controller/metal/instrumentation.rb +62 -50
  41. data/lib/action_controller/metal/live.rb +67 -2
  42. data/lib/action_controller/metal/mime_responds.rb +5 -5
  43. data/lib/action_controller/metal/params_wrapper.rb +24 -13
  44. data/lib/action_controller/metal/permissions_policy.rb +20 -29
  45. data/lib/action_controller/metal/redirecting.rb +96 -23
  46. data/lib/action_controller/metal/renderers.rb +14 -15
  47. data/lib/action_controller/metal/rendering.rb +121 -16
  48. data/lib/action_controller/metal/request_forgery_protection.rb +208 -68
  49. data/lib/action_controller/metal/rescue.rb +7 -4
  50. data/lib/action_controller/metal/streaming.rb +74 -36
  51. data/lib/action_controller/metal/strong_parameters.rb +254 -151
  52. data/lib/action_controller/metal/testing.rb +9 -2
  53. data/lib/action_controller/metal/url_for.rb +10 -5
  54. data/lib/action_controller/metal.rb +89 -34
  55. data/lib/action_controller/railtie.rb +66 -9
  56. data/lib/action_controller/renderer.rb +99 -85
  57. data/lib/action_controller/test_case.rb +42 -11
  58. data/lib/action_controller.rb +10 -6
  59. data/lib/action_dispatch/constants.rb +32 -0
  60. data/lib/action_dispatch/deprecator.rb +7 -0
  61. data/lib/action_dispatch/http/cache.rb +21 -16
  62. data/lib/action_dispatch/http/content_security_policy.rb +122 -44
  63. data/lib/action_dispatch/http/filter_parameters.rb +14 -23
  64. data/lib/action_dispatch/http/headers.rb +3 -1
  65. data/lib/action_dispatch/http/mime_negotiation.rb +25 -15
  66. data/lib/action_dispatch/http/mime_type.rb +43 -22
  67. data/lib/action_dispatch/http/mime_types.rb +3 -1
  68. data/lib/action_dispatch/http/parameters.rb +6 -6
  69. data/lib/action_dispatch/http/permissions_policy.rb +57 -19
  70. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  71. data/lib/action_dispatch/http/request.rb +75 -51
  72. data/lib/action_dispatch/http/response.rb +81 -77
  73. data/lib/action_dispatch/http/upload.rb +15 -2
  74. data/lib/action_dispatch/http/url.rb +11 -19
  75. data/lib/action_dispatch/journey/formatter.rb +8 -2
  76. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  79. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  80. data/lib/action_dispatch/journey/path/pattern.rb +36 -27
  81. data/lib/action_dispatch/journey/route.rb +8 -14
  82. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  83. data/lib/action_dispatch/journey/router.rb +10 -9
  84. data/lib/action_dispatch/journey/routes.rb +5 -5
  85. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  86. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  87. data/lib/action_dispatch/log_subscriber.rb +23 -0
  88. data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -7
  89. data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
  90. data/lib/action_dispatch/middleware/callbacks.rb +2 -0
  91. data/lib/action_dispatch/middleware/cookies.rb +97 -107
  92. data/lib/action_dispatch/middleware/debug_exceptions.rb +31 -28
  93. data/lib/action_dispatch/middleware/debug_locks.rb +7 -4
  94. data/lib/action_dispatch/middleware/debug_view.rb +7 -2
  95. data/lib/action_dispatch/middleware/exception_wrapper.rb +190 -27
  96. data/lib/action_dispatch/middleware/executor.rb +3 -0
  97. data/lib/action_dispatch/middleware/flash.rb +24 -18
  98. data/lib/action_dispatch/middleware/host_authorization.rb +19 -20
  99. data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
  100. data/lib/action_dispatch/middleware/reloader.rb +7 -5
  101. data/lib/action_dispatch/middleware/remote_ip.rb +32 -19
  102. data/lib/action_dispatch/middleware/request_id.rb +5 -3
  103. data/lib/action_dispatch/middleware/server_timing.rb +76 -0
  104. data/lib/action_dispatch/middleware/session/abstract_store.rb +6 -1
  105. data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
  106. data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -13
  107. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
  108. data/lib/action_dispatch/middleware/show_exceptions.rb +30 -25
  109. data/lib/action_dispatch/middleware/ssl.rb +18 -6
  110. data/lib/action_dispatch/middleware/stack.rb +34 -11
  111. data/lib/action_dispatch/middleware/static.rb +16 -16
  112. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  113. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +5 -5
  114. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  115. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  116. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  117. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
  118. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
  119. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +9 -9
  120. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  121. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  122. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +45 -18
  123. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -15
  124. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -4
  125. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +6 -6
  126. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +7 -7
  127. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -55
  131. data/lib/action_dispatch/railtie.rb +20 -4
  132. data/lib/action_dispatch/request/session.rb +59 -19
  133. data/lib/action_dispatch/request/utils.rb +8 -3
  134. data/lib/action_dispatch/routing/inspector.rb +55 -7
  135. data/lib/action_dispatch/routing/mapper.rb +117 -107
  136. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
  137. data/lib/action_dispatch/routing/redirection.rb +20 -8
  138. data/lib/action_dispatch/routing/route_set.rb +67 -27
  139. data/lib/action_dispatch/routing/routes_proxy.rb +11 -16
  140. data/lib/action_dispatch/routing/url_for.rb +29 -26
  141. data/lib/action_dispatch/routing.rb +12 -13
  142. data/lib/action_dispatch/system_test_case.rb +8 -8
  143. data/lib/action_dispatch/system_testing/browser.rb +20 -29
  144. data/lib/action_dispatch/system_testing/driver.rb +34 -18
  145. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +35 -20
  146. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
  147. data/lib/action_dispatch/testing/assertion_response.rb +1 -1
  148. data/lib/action_dispatch/testing/assertions/response.rb +14 -7
  149. data/lib/action_dispatch/testing/assertions/routing.rb +70 -30
  150. data/lib/action_dispatch/testing/assertions.rb +3 -4
  151. data/lib/action_dispatch/testing/integration.rb +33 -25
  152. data/lib/action_dispatch/testing/request_encoder.rb +4 -1
  153. data/lib/action_dispatch/testing/test_process.rb +5 -30
  154. data/lib/action_dispatch/testing/test_request.rb +1 -1
  155. data/lib/action_dispatch/testing/test_response.rb +34 -2
  156. data/lib/action_dispatch.rb +38 -4
  157. data/lib/action_pack/gem_version.rb +4 -4
  158. data/lib/action_pack/version.rb +1 -1
  159. data/lib/action_pack.rb +1 -1
  160. metadata +67 -30
@@ -16,12 +16,26 @@ module ActionDispatch
16
16
 
17
17
  included do
18
18
  mattr_accessor :ignore_accept_header, default: false
19
+
20
+ def return_only_media_type_on_content_type=(value)
21
+ ActionDispatch.deprecator.warn(
22
+ "`config.action_dispatch.return_only_request_media_type_on_content_type` is deprecated and will" \
23
+ " be removed in Rails 7.2."
24
+ )
25
+ end
26
+
27
+ def return_only_media_type_on_content_type
28
+ ActionDispatch.deprecator.warn(
29
+ "`config.action_dispatch.return_only_request_media_type_on_content_type` is deprecated and will" \
30
+ " be removed in Rails 7.2."
31
+ )
32
+ end
19
33
  end
20
34
 
21
35
  # The MIME type of the HTTP request, such as Mime[:xml].
22
36
  def content_mime_type
23
37
  fetch_header("action_dispatch.request.content_type") do |k|
24
- v = if get_header("CONTENT_TYPE") =~ /^([^,\;]*)/
38
+ v = if get_header("CONTENT_TYPE") =~ /^([^,;]*)/
25
39
  Mime::Type.lookup($1.strip.downcase)
26
40
  else
27
41
  nil
@@ -32,10 +46,6 @@ module ActionDispatch
32
46
  end
33
47
  end
34
48
 
35
- def content_type
36
- content_mime_type && content_mime_type.to_s
37
- end
38
-
39
49
  def has_content_type? # :nodoc:
40
50
  get_header "CONTENT_TYPE"
41
51
  end
@@ -62,7 +72,7 @@ module ActionDispatch
62
72
  # GET /posts/5.xhtml | request.format => Mime[:html]
63
73
  # GET /posts/5 | request.format => Mime[:html] or Mime[:js], or request.accepts.first
64
74
  #
65
- def format(view_path = [])
75
+ def format(_view_path = nil)
66
76
  formats.first || Mime::NullType.instance
67
77
  end
68
78
 
@@ -71,7 +81,7 @@ module ActionDispatch
71
81
  v = if params_readable?
72
82
  Array(Mime[parameters[:format]])
73
83
  elsif use_accept_header && valid_accept_header
74
- accepts
84
+ accepts.dup
75
85
  elsif extension_format = format_from_path_extension
76
86
  [extension_format]
77
87
  elsif xhr?
@@ -80,7 +90,7 @@ module ActionDispatch
80
90
  [Mime[:html]]
81
91
  end
82
92
 
83
- v = v.select do |format|
93
+ v.select! do |format|
84
94
  format.symbol || format.ref == "*/*"
85
95
  end
86
96
 
@@ -92,7 +102,7 @@ module ActionDispatch
92
102
  def variant=(variant)
93
103
  variant = Array(variant)
94
104
 
95
- if variant.all? { |v| v.is_a?(Symbol) }
105
+ if variant.all?(Symbol)
96
106
  @variant = ActiveSupport::ArrayInquirer.new(variant)
97
107
  else
98
108
  raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols."
@@ -122,8 +132,8 @@ module ActionDispatch
122
132
  # Sets the \formats by string extensions. This differs from #format= by allowing you
123
133
  # to set multiple, ordered formats, which is useful when you want to have a fallback.
124
134
  #
125
- # In this example, the :iphone format will be used if it's available, otherwise it'll fallback
126
- # to the :html format.
135
+ # In this example, the +:iphone+ format will be used if it's available, otherwise it'll fall back
136
+ # to the +:html+ format.
127
137
  #
128
138
  # class ApplicationController < ActionController::Base
129
139
  # before_action :adjust_format_for_iphone_with_html_fallback
@@ -162,22 +172,22 @@ module ActionDispatch
162
172
  # in which case we assume you're a browser and send HTML.
163
173
  BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
164
174
 
165
- def params_readable? # :doc:
175
+ def params_readable?
166
176
  parameters[:format]
167
177
  rescue *RESCUABLE_MIME_FORMAT_ERRORS
168
178
  false
169
179
  end
170
180
 
171
- def valid_accept_header # :doc:
181
+ def valid_accept_header
172
182
  (xhr? && (accept.present? || content_mime_type)) ||
173
183
  (accept.present? && !accept.match?(BROWSER_LIKE_ACCEPTS))
174
184
  end
175
185
 
176
- def use_accept_header # :doc:
186
+ def use_accept_header
177
187
  !self.class.ignore_accept_header
178
188
  end
179
189
 
180
- def format_from_path_extension # :doc:
190
+ def format_from_path_extension
181
191
  path = get_header("action_dispatch.original_path") || get_header("PATH_INFO")
182
192
  if match = path && path.match(/\.(\w+)\z/)
183
193
  Mime[match.captures.first]
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "singleton"
4
- require "active_support/core_ext/symbol/starts_ends_with"
5
4
 
6
5
  module Mime
7
6
  class Mimes
@@ -12,25 +11,34 @@ module Mime
12
11
  def initialize
13
12
  @mimes = []
14
13
  @symbols = []
14
+ @symbols_set = Set.new
15
15
  end
16
16
 
17
- def each
18
- @mimes.each { |x| yield x }
17
+ def each(&block)
18
+ @mimes.each(&block)
19
19
  end
20
20
 
21
21
  def <<(type)
22
22
  @mimes << type
23
- @symbols << type.to_sym
23
+ sym_type = type.to_sym
24
+ @symbols << sym_type
25
+ @symbols_set << sym_type
24
26
  end
25
27
 
26
28
  def delete_if
27
29
  @mimes.delete_if do |x|
28
30
  if yield x
29
- @symbols.delete(x.to_sym)
31
+ sym_type = x.to_sym
32
+ @symbols.delete(sym_type)
33
+ @symbols_set.delete(sym_type)
30
34
  true
31
35
  end
32
36
  end
33
37
  end
38
+
39
+ def valid_symbols?(symbols) # :nodoc
40
+ symbols.all? { |s| @symbols_set.include?(s) }
41
+ end
34
42
  end
35
43
 
36
44
  SET = Mimes.new
@@ -43,9 +51,17 @@ module Mime
43
51
  Type.lookup_by_extension(type)
44
52
  end
45
53
 
46
- def fetch(type)
54
+ def symbols
55
+ SET.symbols
56
+ end
57
+
58
+ def valid_symbols?(symbols) # :nodoc:
59
+ SET.valid_symbols?(symbols)
60
+ end
61
+
62
+ def fetch(type, &block)
47
63
  return type if type.is_a?(Type)
48
- EXTENSION_LOOKUP.fetch(type.to_s) { |k| yield k }
64
+ EXTENSION_LOOKUP.fetch(type.to_s, &block)
49
65
  end
50
66
  end
51
67
 
@@ -68,7 +84,7 @@ module Mime
68
84
  @register_callbacks = []
69
85
 
70
86
  # A simple helper class used in parsing the accept header.
71
- class AcceptItem #:nodoc:
87
+ class AcceptItem # :nodoc:
72
88
  attr_accessor :index, :name, :q
73
89
  alias :to_s :name
74
90
 
@@ -86,7 +102,7 @@ module Mime
86
102
  end
87
103
  end
88
104
 
89
- class AcceptList #:nodoc:
105
+ class AcceptList # :nodoc:
90
106
  def self.sort!(list)
91
107
  list.sort!
92
108
 
@@ -136,14 +152,18 @@ module Mime
136
152
 
137
153
  class << self
138
154
  TRAILING_STAR_REGEXP = /^(text|application)\/\*/
139
- PARAMETER_SEPARATOR_REGEXP = /;\s*\w+="?\w+"?/
155
+ # all media-type parameters need to be before the q-parameter
156
+ # https://www.rfc-editor.org/rfc/rfc7231#section-5.3.2
157
+ PARAMETER_SEPARATOR_REGEXP = /;\s*q="?/
158
+ ACCEPT_HEADER_REGEXP = /[^,\s"](?:[^,"]|"[^"]*")*/
140
159
 
141
160
  def register_callback(&block)
142
161
  @register_callbacks << block
143
162
  end
144
163
 
145
164
  def lookup(string)
146
- LOOKUP[string] || Type.new(string)
165
+ # fallback to the media-type without parameters if it was not found
166
+ LOOKUP[string] || LOOKUP[string.split(";", 2)[0]&.rstrip] || Type.new(string)
147
167
  end
148
168
 
149
169
  def lookup_by_extension(extension)
@@ -172,12 +192,14 @@ module Mime
172
192
 
173
193
  def parse(accept_header)
174
194
  if !accept_header.include?(",")
175
- accept_header = accept_header.split(PARAMETER_SEPARATOR_REGEXP).first
176
- return [] unless accept_header
177
- parse_trailing_star(accept_header) || [Mime::Type.lookup(accept_header)].compact
195
+ if (index = accept_header.index(PARAMETER_SEPARATOR_REGEXP))
196
+ accept_header = accept_header[0, index].strip
197
+ end
198
+ return [] if accept_header.blank?
199
+ parse_trailing_star(accept_header) || Array(Mime::Type.lookup(accept_header))
178
200
  else
179
201
  list, index = [], 0
180
- accept_header.split(",").each do |header|
202
+ accept_header.scan(ACCEPT_HEADER_REGEXP).each do |header|
181
203
  params, q = header.split(PARAMETER_SEPARATOR_REGEXP)
182
204
 
183
205
  next unless params
@@ -200,10 +222,10 @@ module Mime
200
222
  end
201
223
 
202
224
  # For an input of <tt>'text'</tt>, returns <tt>[Mime[:json], Mime[:xml], Mime[:ics],
203
- # Mime[:html], Mime[:css], Mime[:csv], Mime[:js], Mime[:yaml], Mime[:text]</tt>.
225
+ # Mime[:html], Mime[:css], Mime[:csv], Mime[:js], Mime[:yaml], Mime[:text]]</tt>.
204
226
  #
205
227
  # For an input of <tt>'application'</tt>, returns <tt>[Mime[:html], Mime[:js],
206
- # Mime[:xml], Mime[:yaml], Mime[:atom], Mime[:json], Mime[:rss], Mime[:url_encoded_form]</tt>.
228
+ # Mime[:xml], Mime[:yaml], Mime[:atom], Mime[:json], Mime[:rss], Mime[:url_encoded_form]]</tt>.
207
229
  def parse_data_with_trailing_star(type)
208
230
  Mime::SET.select { |m| m.match?(type) }
209
231
  end
@@ -226,10 +248,9 @@ module Mime
226
248
  attr_reader :hash
227
249
 
228
250
  MIME_NAME = "[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}"
229
- MIME_PARAMETER_KEY = "[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}"
230
- MIME_PARAMETER_VALUE = "#{Regexp.escape('"')}?[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}#{Regexp.escape('"')}?"
231
- MIME_PARAMETER = "\s*\;\s*#{MIME_PARAMETER_KEY}(?:\=#{MIME_PARAMETER_VALUE})?"
232
- MIME_REGEXP = /\A(?:\*\/\*|#{MIME_NAME}\/(?:\*|#{MIME_NAME})(?>\s*#{MIME_PARAMETER}\s*)*)\z/
251
+ MIME_PARAMETER_VALUE = "(?:#{MIME_NAME}|\"[^\"\r\\\\]*\")"
252
+ MIME_PARAMETER = "\s*;\s*#{MIME_NAME}(?:=#{MIME_PARAMETER_VALUE})?"
253
+ MIME_REGEXP = /\A(?:\*\/\*|#{MIME_NAME}\/(?:\*|#{MIME_NAME})(?>#{MIME_PARAMETER})*\s*)\z/
233
254
 
234
255
  class InvalidMimeType < StandardError; end
235
256
 
@@ -293,7 +314,7 @@ module Mime
293
314
  end
294
315
 
295
316
  def html?
296
- (symbol == :html) || /html/.match?(@string)
317
+ (symbol == :html) || @string.include?("html")
297
318
  end
298
319
 
299
320
  def all?; false; end
@@ -18,6 +18,7 @@ Mime::Type.register "image/gif", :gif, [], %w(gif)
18
18
  Mime::Type.register "image/bmp", :bmp, [], %w(bmp)
19
19
  Mime::Type.register "image/tiff", :tiff, [], %w(tif tiff)
20
20
  Mime::Type.register "image/svg+xml", :svg
21
+ Mime::Type.register "image/webp", :webp, [], %w(webp)
21
22
 
22
23
  Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe)
23
24
 
@@ -43,7 +44,8 @@ Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
43
44
 
44
45
  # https://www.ietf.org/rfc/rfc4627.txt
45
46
  # http://www.json.org/JSONRequest.html
46
- Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
47
+ # https://www.ietf.org/rfc/rfc7807.txt
48
+ Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest application/problem+json )
47
49
 
48
50
  Mime::Type.register "application/pdf", :pdf, [], %w(pdf)
49
51
  Mime::Type.register "application/zip", :zip, [], %w(zip)
@@ -17,8 +17,8 @@ module ActionDispatch
17
17
  # Raised when raw data from the request cannot be parsed by the parser
18
18
  # defined for request's content MIME type.
19
19
  class ParseError < StandardError
20
- def initialize
21
- super($!.message)
20
+ def initialize(message = $!.message)
21
+ super(message)
22
22
  end
23
23
  end
24
24
 
@@ -62,7 +62,7 @@ module ActionDispatch
62
62
  end
63
63
  alias :params :parameters
64
64
 
65
- def path_parameters=(parameters) #:nodoc:
65
+ def path_parameters=(parameters) # :nodoc:
66
66
  delete_header("action_dispatch.request.parameters")
67
67
 
68
68
  parameters = Request::Utils.set_binary_encoding(self, parameters, parameters[:controller], parameters[:action])
@@ -76,9 +76,9 @@ module ActionDispatch
76
76
  end
77
77
 
78
78
  # Returns a hash with the \parameters used to form the \path of the request.
79
- # Returned hash keys are strings:
79
+ # Returned hash keys are symbols:
80
80
  #
81
- # {'action' => 'my_action', 'controller' => 'my_controller'}
81
+ # { action: "my_action", controller: "my_controller" }
82
82
  def path_parameters
83
83
  get_header(PARAMETERS_KEY) || set_header(PARAMETERS_KEY, {})
84
84
  end
@@ -93,7 +93,7 @@ module ActionDispatch
93
93
  strategy.call(raw_post)
94
94
  rescue # JSON or Ruby code block errors.
95
95
  log_parse_error_once
96
- raise ParseError
96
+ raise ParseError, "Error occurred while parsing request parameters"
97
97
  end
98
98
  end
99
99
 
@@ -2,34 +2,50 @@
2
2
 
3
3
  require "active_support/core_ext/object/deep_dup"
4
4
 
5
- module ActionDispatch #:nodoc:
5
+ module ActionDispatch # :nodoc:
6
+ # = Action Dispatch \PermissionsPolicy
7
+ #
8
+ # Configures the HTTP
9
+ # {Feature-Policy}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy]
10
+ # response header to specify which browser features the current document and
11
+ # its iframes can use.
12
+ #
13
+ # Example global policy:
14
+ #
15
+ # Rails.application.config.permissions_policy do |policy|
16
+ # policy.camera :none
17
+ # policy.gyroscope :none
18
+ # policy.microphone :none
19
+ # policy.usb :none
20
+ # policy.fullscreen :self
21
+ # policy.payment :self, "https://secure.example.com"
22
+ # end
23
+ #
24
+ # The Feature-Policy header has been renamed to Permissions-Policy.
25
+ # The Permissions-Policy requires a different implementation and isn't
26
+ # yet supported by all browsers. To avoid having to rename this
27
+ # middleware in the future we use the new name for the middleware but
28
+ # keep the old header name and implementation for now.
6
29
  class PermissionsPolicy
7
30
  class Middleware
8
- CONTENT_TYPE = "Content-Type"
9
- # The Feature-Policy header has been renamed to Permissions-Policy.
10
- # The Permissions-Policy requires a different implementation and isn't
11
- # yet supported by all browsers. To avoid having to rename this
12
- # middleware in the future we use the new name for the middleware but
13
- # keep the old header name and implementation for now.
14
- POLICY = "Feature-Policy"
15
-
16
31
  def initialize(app)
17
32
  @app = app
18
33
  end
19
34
 
20
35
  def call(env)
21
- request = ActionDispatch::Request.new(env)
22
36
  _, headers, _ = response = @app.call(env)
23
37
 
24
38
  return response unless html_response?(headers)
25
39
  return response if policy_present?(headers)
26
40
 
41
+ request = ActionDispatch::Request.new(env)
42
+
27
43
  if policy = request.permissions_policy
28
- headers[POLICY] = policy.build(request.controller_instance)
44
+ headers[ActionDispatch::Constants::FEATURE_POLICY] = policy.build(request.controller_instance)
29
45
  end
30
46
 
31
47
  if policy_empty?(policy)
32
- headers.delete(POLICY)
48
+ headers.delete(ActionDispatch::Constants::FEATURE_POLICY)
33
49
  end
34
50
 
35
51
  response
@@ -37,13 +53,13 @@ module ActionDispatch #:nodoc:
37
53
 
38
54
  private
39
55
  def html_response?(headers)
40
- if content_type = headers[CONTENT_TYPE]
41
- /html/.match?(content_type)
56
+ if content_type = headers[Rack::CONTENT_TYPE]
57
+ content_type.include?("html")
42
58
  end
43
59
  end
44
60
 
45
61
  def policy_present?(headers)
46
- headers[POLICY]
62
+ headers[ActionDispatch::Constants::FEATURE_POLICY]
47
63
  end
48
64
 
49
65
  def policy_empty?(policy)
@@ -69,7 +85,7 @@ module ActionDispatch #:nodoc:
69
85
  }.freeze
70
86
 
71
87
  # List of available permissions can be found at
72
- # https://github.com/w3c/webappsec-permissions-policy/blob/master/features.md#policy-controlled-features
88
+ # https://github.com/w3c/webappsec-permissions-policy/blob/main/features.md#policy-controlled-features
73
89
  DIRECTIVES = {
74
90
  accelerometer: "accelerometer",
75
91
  ambient_light_sensor: "ambient-light-sensor",
@@ -79,15 +95,18 @@ module ActionDispatch #:nodoc:
79
95
  fullscreen: "fullscreen",
80
96
  geolocation: "geolocation",
81
97
  gyroscope: "gyroscope",
98
+ hid: "hid",
99
+ idle_detection: "idle_detection",
82
100
  magnetometer: "magnetometer",
83
101
  microphone: "microphone",
84
102
  midi: "midi",
85
103
  payment: "payment",
86
104
  picture_in_picture: "picture-in-picture",
87
- speaker: "speaker",
105
+ screen_wake_lock: "screen-wake-lock",
106
+ serial: "serial",
107
+ sync_xhr: "sync-xhr",
88
108
  usb: "usb",
89
- vibrate: "vibrate",
90
- vr: "vr",
109
+ web_share: "web-share",
91
110
  }.freeze
92
111
 
93
112
  private_constant :MAPPINGS, :DIRECTIVES
@@ -113,6 +132,25 @@ module ActionDispatch #:nodoc:
113
132
  end
114
133
  end
115
134
 
135
+ %w[speaker vibrate vr].each do |directive|
136
+ define_method(directive) do |*sources|
137
+ ActionDispatch.deprecator.warn(<<~MSG)
138
+ The `#{directive}` permissions policy directive is deprecated
139
+ and will be removed in Rails 7.2.
140
+
141
+ There is no browser support for this directive, and no plan
142
+ for browser support in the future. You can just remove this
143
+ directive from your application.
144
+ MSG
145
+
146
+ if sources.first
147
+ @directives[directive] = apply_mappings(sources)
148
+ else
149
+ @directives.delete(directive)
150
+ end
151
+ end
152
+ end
153
+
116
154
  def build(context = nil)
117
155
  build_directives(context).compact.join("; ")
118
156
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :enddoc:
4
+
3
5
  require "rack/cache"
4
6
  require "rack/cache/context"
5
7
  require "active_support/cache"