actionpack 6.1.7.5 → 7.1.3.1

Sign up to get free protection for your applications and to get access to all the features.
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"