actionpack 5.2.6 → 6.1.4.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +327 -335
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/abstract_controller/base.rb +38 -4
  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 +14 -2
  9. data/lib/abstract_controller/collector.rb +1 -2
  10. data/lib/abstract_controller/helpers.rb +106 -90
  11. data/lib/abstract_controller/railties/routes_helpers.rb +17 -1
  12. data/lib/abstract_controller/rendering.rb +9 -9
  13. data/lib/abstract_controller/translation.rb +11 -5
  14. data/lib/abstract_controller.rb +1 -0
  15. data/lib/action_controller/api.rb +4 -3
  16. data/lib/action_controller/base.rb +6 -9
  17. data/lib/action_controller/caching.rb +1 -3
  18. data/lib/action_controller/log_subscriber.rb +10 -7
  19. data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
  20. data/lib/action_controller/metal/conditional_get.rb +19 -5
  21. data/lib/action_controller/metal/content_security_policy.rb +1 -2
  22. data/lib/action_controller/metal/cookies.rb +3 -1
  23. data/lib/action_controller/metal/data_streaming.rb +6 -7
  24. data/lib/action_controller/metal/default_headers.rb +17 -0
  25. data/lib/action_controller/metal/etag_with_template_digest.rb +4 -6
  26. data/lib/action_controller/metal/exceptions.rb +56 -2
  27. data/lib/action_controller/metal/flash.rb +5 -5
  28. data/lib/action_controller/metal/head.rb +7 -4
  29. data/lib/action_controller/metal/helpers.rb +14 -5
  30. data/lib/action_controller/metal/http_authentication.rb +24 -23
  31. data/lib/action_controller/metal/implicit_render.rb +5 -15
  32. data/lib/action_controller/metal/instrumentation.rb +13 -14
  33. data/lib/action_controller/metal/live.rb +39 -32
  34. data/lib/action_controller/metal/logging.rb +20 -0
  35. data/lib/action_controller/metal/mime_responds.rb +19 -4
  36. data/lib/action_controller/metal/parameter_encoding.rb +35 -4
  37. data/lib/action_controller/metal/params_wrapper.rb +32 -22
  38. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  39. data/lib/action_controller/metal/redirecting.rb +6 -6
  40. data/lib/action_controller/metal/renderers.rb +4 -4
  41. data/lib/action_controller/metal/rendering.rb +8 -3
  42. data/lib/action_controller/metal/request_forgery_protection.rb +26 -49
  43. data/lib/action_controller/metal/rescue.rb +1 -1
  44. data/lib/action_controller/metal/streaming.rb +0 -1
  45. data/lib/action_controller/metal/strong_parameters.rb +167 -58
  46. data/lib/action_controller/metal/url_for.rb +1 -1
  47. data/lib/action_controller/metal.rb +10 -8
  48. data/lib/action_controller/railties/helpers.rb +1 -1
  49. data/lib/action_controller/renderer.rb +37 -13
  50. data/lib/action_controller/template_assertions.rb +1 -1
  51. data/lib/action_controller/test_case.rb +71 -63
  52. data/lib/action_controller.rb +7 -4
  53. data/lib/action_dispatch/http/cache.rb +31 -27
  54. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  55. data/lib/action_dispatch/http/content_security_policy.rb +33 -19
  56. data/lib/action_dispatch/http/filter_parameters.rb +9 -8
  57. data/lib/action_dispatch/http/filter_redirect.rb +2 -3
  58. data/lib/action_dispatch/http/headers.rb +4 -4
  59. data/lib/action_dispatch/http/mime_negotiation.rb +26 -13
  60. data/lib/action_dispatch/http/mime_type.rb +43 -24
  61. data/lib/action_dispatch/http/parameters.rb +14 -23
  62. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  63. data/lib/action_dispatch/http/request.rb +45 -22
  64. data/lib/action_dispatch/http/response.rb +45 -25
  65. data/lib/action_dispatch/http/upload.rb +9 -1
  66. data/lib/action_dispatch/http/url.rb +82 -82
  67. data/lib/action_dispatch/journey/formatter.rb +55 -31
  68. data/lib/action_dispatch/journey/gtg/builder.rb +22 -37
  69. data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
  70. data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -5
  71. data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
  72. data/lib/action_dispatch/journey/nodes/node.rb +13 -11
  73. data/lib/action_dispatch/journey/parser.rb +13 -13
  74. data/lib/action_dispatch/journey/parser.y +1 -1
  75. data/lib/action_dispatch/journey/path/pattern.rb +19 -21
  76. data/lib/action_dispatch/journey/route.rb +10 -20
  77. data/lib/action_dispatch/journey/router/utils.rb +14 -12
  78. data/lib/action_dispatch/journey/router.rb +26 -34
  79. data/lib/action_dispatch/journey/routes.rb +0 -2
  80. data/lib/action_dispatch/journey/scanner.rb +10 -4
  81. data/lib/action_dispatch/journey/visitors.rb +1 -4
  82. data/lib/action_dispatch/journey.rb +0 -2
  83. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  84. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  85. data/lib/action_dispatch/middleware/cookies.rb +128 -109
  86. data/lib/action_dispatch/middleware/debug_exceptions.rb +43 -66
  87. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  88. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  89. data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -30
  90. data/lib/action_dispatch/middleware/flash.rb +1 -1
  91. data/lib/action_dispatch/middleware/host_authorization.rb +141 -0
  92. data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
  93. data/lib/action_dispatch/middleware/remote_ip.rb +14 -16
  94. data/lib/action_dispatch/middleware/request_id.rb +5 -6
  95. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -3
  96. data/lib/action_dispatch/middleware/session/cookie_store.rb +3 -9
  97. data/lib/action_dispatch/middleware/show_exceptions.rb +3 -2
  98. data/lib/action_dispatch/middleware/ssl.rb +20 -15
  99. data/lib/action_dispatch/middleware/stack.rb +56 -2
  100. data/lib/action_dispatch/middleware/static.rb +153 -93
  101. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  102. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  103. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  104. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
  105. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  106. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  107. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  108. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  109. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  110. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
  111. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  112. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +6 -3
  113. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
  114. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +104 -8
  115. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  116. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  118. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  119. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
  120. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  121. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +24 -1
  122. data/lib/action_dispatch/railtie.rb +8 -2
  123. data/lib/action_dispatch/request/session.rb +11 -10
  124. data/lib/action_dispatch/request/utils.rb +26 -2
  125. data/lib/action_dispatch/routing/inspector.rb +100 -52
  126. data/lib/action_dispatch/routing/mapper.rb +155 -103
  127. data/lib/action_dispatch/routing/polymorphic_routes.rb +13 -15
  128. data/lib/action_dispatch/routing/redirection.rb +4 -4
  129. data/lib/action_dispatch/routing/route_set.rb +71 -69
  130. data/lib/action_dispatch/routing/url_for.rb +2 -2
  131. data/lib/action_dispatch/routing.rb +21 -20
  132. data/lib/action_dispatch/system_test_case.rb +54 -11
  133. data/lib/action_dispatch/system_testing/browser.rb +53 -16
  134. data/lib/action_dispatch/system_testing/driver.rb +11 -3
  135. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +49 -7
  136. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +8 -10
  137. data/lib/action_dispatch/testing/assertion_response.rb +0 -1
  138. data/lib/action_dispatch/testing/assertions/response.rb +4 -7
  139. data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
  140. data/lib/action_dispatch/testing/assertions.rb +1 -1
  141. data/lib/action_dispatch/testing/integration.rb +60 -28
  142. data/lib/action_dispatch/testing/request_encoder.rb +2 -2
  143. data/lib/action_dispatch/testing/test_process.rb +29 -4
  144. data/lib/action_dispatch/testing/test_request.rb +3 -3
  145. data/lib/action_dispatch/testing/test_response.rb +4 -32
  146. data/lib/action_dispatch.rb +9 -3
  147. data/lib/action_pack/gem_version.rb +4 -4
  148. data/lib/action_pack.rb +1 -1
  149. metadata +35 -23
  150. data/lib/action_controller/metal/force_ssl.rb +0 -99
  151. data/lib/action_dispatch/http/parameter_filter.rb +0 -86
  152. data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
  153. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
  154. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
  155. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "action_dispatch/http/parameter_filter"
3
+ require "active_support/parameter_filter"
4
4
 
5
5
  module ActionDispatch
6
6
  module Http
@@ -9,8 +9,8 @@ module ActionDispatch
9
9
  # sub-hashes of the params hash to filter. Filtering only certain sub-keys
10
10
  # from a hash is possible by using the dot notation: 'credit_card.number'.
11
11
  # If a block is given, each key and value of the params hash and all
12
- # sub-hashes is passed to it, where the value or the key can be replaced using
13
- # String#replace or similar method.
12
+ # sub-hashes are passed to it, where the value or the key can be replaced using
13
+ # String#replace or similar methods.
14
14
  #
15
15
  # env["action_dispatch.parameter_filter"] = [:password]
16
16
  # => replaces the value to all keys matching /password/i with "[FILTERED]"
@@ -23,13 +23,13 @@ module ActionDispatch
23
23
  # change { file: { code: "xxxx"} }
24
24
  #
25
25
  # env["action_dispatch.parameter_filter"] = -> (k, v) do
26
- # v.reverse! if k =~ /secret/i
26
+ # v.reverse! if k.match?(/secret/i)
27
27
  # end
28
28
  # => reverses the value to all keys matching /secret/i
29
29
  module FilterParameters
30
30
  ENV_MATCH = [/RAW_POST_DATA/, "rack.request.form_vars"] # :nodoc:
31
- NULL_PARAM_FILTER = ParameterFilter.new # :nodoc:
32
- NULL_ENV_FILTER = ParameterFilter.new ENV_MATCH # :nodoc:
31
+ NULL_PARAM_FILTER = ActiveSupport::ParameterFilter.new # :nodoc:
32
+ NULL_ENV_FILTER = ActiveSupport::ParameterFilter.new ENV_MATCH # :nodoc:
33
33
 
34
34
  def initialize
35
35
  super
@@ -41,6 +41,8 @@ module ActionDispatch
41
41
  # Returns a hash of parameters with all sensitive data replaced.
42
42
  def filtered_parameters
43
43
  @filtered_parameters ||= parameter_filter.filter(parameters)
44
+ rescue ActionDispatch::Http::Parameters::ParseError
45
+ @filtered_parameters = {}
44
46
  end
45
47
 
46
48
  # Returns a hash of request.env with all sensitive data replaced.
@@ -54,7 +56,6 @@ module ActionDispatch
54
56
  end
55
57
 
56
58
  private
57
-
58
59
  def parameter_filter # :doc:
59
60
  parameter_filter_for fetch_header("action_dispatch.parameter_filter") {
60
61
  return NULL_PARAM_FILTER
@@ -69,7 +70,7 @@ module ActionDispatch
69
70
  end
70
71
 
71
72
  def parameter_filter_for(filters) # :doc:
72
- ParameterFilter.new(filters)
73
+ ActiveSupport::ParameterFilter.new(filters)
73
74
  end
74
75
 
75
76
  KV_RE = "[^&;=]+"
@@ -3,7 +3,7 @@
3
3
  module ActionDispatch
4
4
  module Http
5
5
  module FilterRedirect
6
- FILTERED = "[FILTERED]".freeze # :nodoc:
6
+ FILTERED = "[FILTERED]" # :nodoc:
7
7
 
8
8
  def filtered_location # :nodoc:
9
9
  if location_filter_match?
@@ -14,7 +14,6 @@ module ActionDispatch
14
14
  end
15
15
 
16
16
  private
17
-
18
17
  def location_filters
19
18
  if request
20
19
  request.get_header("action_dispatch.redirect_filter") || []
@@ -28,7 +27,7 @@ module ActionDispatch
28
27
  if String === filter
29
28
  location.include?(filter)
30
29
  elsif Regexp === filter
31
- location =~ filter
30
+ location.match?(filter)
32
31
  end
33
32
  end
34
33
  end
@@ -116,14 +116,14 @@ module ActionDispatch
116
116
  def env; @req.env.dup; end
117
117
 
118
118
  private
119
-
120
119
  # Converts an HTTP header name to an environment variable name if it is
121
120
  # not contained within the headers hash.
122
121
  def env_name(key)
123
122
  key = key.to_s
124
- if key =~ HTTP_HEADER
125
- key = key.upcase.tr("-", "_")
126
- key = "HTTP_" + key unless CGI_VARIABLES.include?(key)
123
+ if HTTP_HEADER.match?(key)
124
+ key = key.upcase
125
+ key.tr!("-", "_")
126
+ key.prepend("HTTP_") unless CGI_VARIABLES.include?(key)
127
127
  end
128
128
  key
129
129
  end
@@ -7,6 +7,13 @@ module ActionDispatch
7
7
  module MimeNegotiation
8
8
  extend ActiveSupport::Concern
9
9
 
10
+ class InvalidType < ::Mime::Type::InvalidMimeType; end
11
+
12
+ RESCUABLE_MIME_FORMAT_ERRORS = [
13
+ ActionController::BadRequest,
14
+ ActionDispatch::Http::Parameters::ParseError,
15
+ ]
16
+
10
17
  included do
11
18
  mattr_accessor :ignore_accept_header, default: false
12
19
  end
@@ -20,6 +27,8 @@ module ActionDispatch
20
27
  nil
21
28
  end
22
29
  set_header k, v
30
+ rescue ::Mime::Type::InvalidMimeType => e
31
+ raise InvalidType, e.message
23
32
  end
24
33
  end
25
34
 
@@ -42,6 +51,8 @@ module ActionDispatch
42
51
  Mime::Type.parse(header)
43
52
  end
44
53
  set_header k, v
54
+ rescue ::Mime::Type::InvalidMimeType => e
55
+ raise InvalidType, e.message
45
56
  end
46
57
  end
47
58
 
@@ -57,13 +68,7 @@ module ActionDispatch
57
68
 
58
69
  def formats
59
70
  fetch_header("action_dispatch.request.formats") do |k|
60
- params_readable = begin
61
- parameters[:format]
62
- rescue ActionController::BadRequest
63
- false
64
- end
65
-
66
- v = if params_readable
71
+ v = if params_readable?
67
72
  Array(Mime[parameters[:format]])
68
73
  elsif use_accept_header && valid_accept_header
69
74
  accepts
@@ -90,10 +95,7 @@ module ActionDispatch
90
95
  if variant.all? { |v| v.is_a?(Symbol) }
91
96
  @variant = ActiveSupport::ArrayInquirer.new(variant)
92
97
  else
93
- raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols. " \
94
- "For security reasons, never directly set the variant to a user-provided value, " \
95
- "like params[:variant].to_sym. Check user-provided value against a whitelist first, " \
96
- "then set the variant: request.variant = :tablet if params[:variant] == 'tablet'"
98
+ raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols."
97
99
  end
98
100
  end
99
101
 
@@ -151,13 +153,24 @@ module ActionDispatch
151
153
  order.include?(Mime::ALL) ? format : nil
152
154
  end
153
155
 
154
- private
156
+ def should_apply_vary_header?
157
+ !params_readable? && use_accept_header && valid_accept_header
158
+ end
155
159
 
160
+ private
161
+ # We use normal content negotiation unless you include */* in your list,
162
+ # in which case we assume you're a browser and send HTML.
156
163
  BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
157
164
 
165
+ def params_readable? # :doc:
166
+ parameters[:format]
167
+ rescue *RESCUABLE_MIME_FORMAT_ERRORS
168
+ false
169
+ end
170
+
158
171
  def valid_accept_header # :doc:
159
172
  (xhr? && (accept.present? || content_mime_type)) ||
160
- (accept.present? && accept !~ BROWSER_LIKE_ACCEPTS)
173
+ (accept.present? && !accept.match?(BROWSER_LIKE_ACCEPTS))
161
174
  end
162
175
 
163
176
  def use_accept_header # :doc:
@@ -1,17 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # -*- frozen-string-literal: true -*-
4
-
5
3
  require "singleton"
6
- require "active_support/core_ext/string/starts_ends_with"
4
+ require "active_support/core_ext/symbol/starts_ends_with"
7
5
 
8
6
  module Mime
9
7
  class Mimes
8
+ attr_reader :symbols
9
+
10
10
  include Enumerable
11
11
 
12
12
  def initialize
13
13
  @mimes = []
14
- @symbols = nil
14
+ @symbols = []
15
15
  end
16
16
 
17
17
  def each
@@ -20,15 +20,16 @@ module Mime
20
20
 
21
21
  def <<(type)
22
22
  @mimes << type
23
- @symbols = nil
23
+ @symbols << type.to_sym
24
24
  end
25
25
 
26
26
  def delete_if
27
- @mimes.delete_if { |x| yield x }.tap { @symbols = nil }
28
- end
29
-
30
- def symbols
31
- @symbols ||= map(&:to_sym)
27
+ @mimes.delete_if do |x|
28
+ if yield x
29
+ @symbols.delete(x.to_sym)
30
+ true
31
+ end
32
+ end
32
33
  end
33
34
  end
34
35
 
@@ -74,7 +75,7 @@ module Mime
74
75
  def initialize(index, name, q = nil)
75
76
  @index = index
76
77
  @name = name
77
- q ||= 0.0 if @name == "*/*".freeze # Default wildcard match to end of list.
78
+ q ||= 0.0 if @name == "*/*" # Default wildcard match to end of list.
78
79
  @q = ((q || 1.0).to_f * 100).to_i
79
80
  end
80
81
 
@@ -116,7 +117,7 @@ module Mime
116
117
  type = list[idx]
117
118
  break if type.q < app_xml.q
118
119
 
119
- if type.name.ends_with? "+xml"
120
+ if type.name.end_with? "+xml"
120
121
  list[app_xml_idx], list[idx] = list[idx], app_xml
121
122
  app_xml_idx = idx
122
123
  end
@@ -172,6 +173,7 @@ module Mime
172
173
  def parse(accept_header)
173
174
  if !accept_header.include?(",")
174
175
  accept_header = accept_header.split(PARAMETER_SEPARATOR_REGEXP).first
176
+ return [] unless accept_header
175
177
  parse_trailing_star(accept_header) || [Mime::Type.lookup(accept_header)].compact
176
178
  else
177
179
  list, index = [], 0
@@ -203,7 +205,7 @@ module Mime
203
205
  # For an input of <tt>'application'</tt>, returns <tt>[Mime[:html], Mime[:js],
204
206
  # Mime[:xml], Mime[:yaml], Mime[:atom], Mime[:json], Mime[:rss], Mime[:url_encoded_form]</tt>.
205
207
  def parse_data_with_trailing_star(type)
206
- Mime::SET.select { |m| m =~ type }
208
+ Mime::SET.select { |m| m.match?(type) }
207
209
  end
208
210
 
209
211
  # This method is opposite of register method.
@@ -223,7 +225,18 @@ module Mime
223
225
 
224
226
  attr_reader :hash
225
227
 
228
+ 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/
233
+
234
+ class InvalidMimeType < StandardError; end
235
+
226
236
  def initialize(string, symbol = nil, synonyms = [])
237
+ unless MIME_REGEXP.match?(string)
238
+ raise InvalidMimeType, "#{string.inspect} is not a valid MIME type"
239
+ end
227
240
  @symbol, @synonyms = symbol, synonyms
228
241
  @string = string
229
242
  @hash = [@string, @synonyms, @symbol].hash
@@ -273,25 +286,27 @@ module Mime
273
286
  @synonyms.any? { |synonym| synonym.to_s =~ regexp } || @string =~ regexp
274
287
  end
275
288
 
289
+ def match?(mime_type)
290
+ return false unless mime_type
291
+ regexp = Regexp.new(Regexp.quote(mime_type.to_s))
292
+ @synonyms.any? { |synonym| synonym.to_s.match?(regexp) } || @string.match?(regexp)
293
+ end
294
+
276
295
  def html?
277
- symbol == :html || @string =~ /html/
296
+ (symbol == :html) || /html/.match?(@string)
278
297
  end
279
298
 
280
299
  def all?; false; end
281
300
 
282
- # TODO Change this to private once we've dropped Ruby 2.2 support.
283
- # Workaround for Ruby 2.2 "private attribute?" warning.
284
301
  protected
285
-
286
302
  attr_reader :string, :synonyms
287
303
 
288
304
  private
289
-
290
305
  def to_ary; end
291
306
  def to_a; end
292
307
 
293
308
  def method_missing(method, *args)
294
- if method.to_s.ends_with? "?"
309
+ if method.end_with?("?")
295
310
  method[0..-2].downcase.to_sym == to_sym
296
311
  else
297
312
  super
@@ -299,7 +314,7 @@ module Mime
299
314
  end
300
315
 
301
316
  def respond_to_missing?(method, include_private = false)
302
- (method.to_s.ends_with? "?") || super
317
+ method.end_with?("?") || super
303
318
  end
304
319
  end
305
320
 
@@ -307,7 +322,7 @@ module Mime
307
322
  include Singleton
308
323
 
309
324
  def initialize
310
- super "*/*", :all
325
+ super "*/*", nil
311
326
  end
312
327
 
313
328
  def all?; true; end
@@ -315,7 +330,7 @@ module Mime
315
330
  end
316
331
 
317
332
  # ALL isn't a real MIME type, so we don't register it for lookup with the
318
- # other concrete types. It's a wildcard match that we use for `respond_to`
333
+ # other concrete types. It's a wildcard match that we use for +respond_to+
319
334
  # negotiation internals.
320
335
  ALL = AllType.instance
321
336
 
@@ -326,15 +341,19 @@ module Mime
326
341
  true
327
342
  end
328
343
 
344
+ def to_s
345
+ ""
346
+ end
347
+
329
348
  def ref; end
330
349
 
331
350
  private
332
351
  def respond_to_missing?(method, _)
333
- method.to_s.ends_with? "?"
352
+ method.end_with?("?")
334
353
  end
335
354
 
336
355
  def method_missing(method, *args)
337
- false if method.to_s.ends_with? "?"
356
+ false if method.end_with?("?")
338
357
  end
339
358
  end
340
359
  end
@@ -57,7 +57,6 @@ module ActionDispatch
57
57
  query_parameters.dup
58
58
  end
59
59
  params.merge!(path_parameters)
60
- params = set_binary_encoding(params, params[:controller], params[:action])
61
60
  set_header("action_dispatch.request.parameters", params)
62
61
  params
63
62
  end
@@ -66,7 +65,7 @@ module ActionDispatch
66
65
  def path_parameters=(parameters) #:nodoc:
67
66
  delete_header("action_dispatch.request.parameters")
68
67
 
69
- parameters = set_binary_encoding(parameters, parameters[:controller], parameters[:action])
68
+ parameters = Request::Utils.set_binary_encoding(self, parameters, parameters[:controller], parameters[:action])
70
69
  # If any of the path parameters has an invalid encoding then
71
70
  # raise since it's likely to trigger errors further on.
72
71
  Request::Utils.check_param_encoding(parameters)
@@ -85,24 +84,6 @@ module ActionDispatch
85
84
  end
86
85
 
87
86
  private
88
-
89
- def set_binary_encoding(params, controller, action)
90
- return params unless controller && controller.valid_encoding?
91
-
92
- if binary_params_for?(controller, action)
93
- ActionDispatch::Request::Utils.each_param_value(params) do |param|
94
- param.force_encoding ::Encoding::ASCII_8BIT
95
- end
96
- end
97
- params
98
- end
99
-
100
- def binary_params_for?(controller, action)
101
- controller_class_for(controller).binary_params_for?(action)
102
- rescue NameError
103
- false
104
- end
105
-
106
87
  def parse_formatted_parameters(parsers)
107
88
  return yield if content_length.zero? || content_mime_type.nil?
108
89
 
@@ -111,13 +92,23 @@ module ActionDispatch
111
92
  begin
112
93
  strategy.call(raw_post)
113
94
  rescue # JSON or Ruby code block errors.
114
- my_logger = logger || ActiveSupport::Logger.new($stderr)
115
- my_logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{raw_post}"
116
-
95
+ log_parse_error_once
117
96
  raise ParseError
118
97
  end
119
98
  end
120
99
 
100
+ def log_parse_error_once
101
+ @parse_error_logged ||= begin
102
+ parse_logger = logger || ActiveSupport::Logger.new($stderr)
103
+ parse_logger.debug <<~MSG.chomp
104
+ Error occurred while parsing request parameters.
105
+ Contents:
106
+
107
+ #{raw_post}
108
+ MSG
109
+ end
110
+ end
111
+
121
112
  def params_parsers
122
113
  ActionDispatch::Request.parameter_parsers
123
114
  end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/deep_dup"
4
+
5
+ module ActionDispatch #:nodoc:
6
+ class PermissionsPolicy
7
+ 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
+ def initialize(app)
17
+ @app = app
18
+ end
19
+
20
+ def call(env)
21
+ request = ActionDispatch::Request.new(env)
22
+ _, headers, _ = response = @app.call(env)
23
+
24
+ return response unless html_response?(headers)
25
+ return response if policy_present?(headers)
26
+
27
+ if policy = request.permissions_policy
28
+ headers[POLICY] = policy.build(request.controller_instance)
29
+ end
30
+
31
+ if policy_empty?(policy)
32
+ headers.delete(POLICY)
33
+ end
34
+
35
+ response
36
+ end
37
+
38
+ private
39
+ def html_response?(headers)
40
+ if content_type = headers[CONTENT_TYPE]
41
+ /html/.match?(content_type)
42
+ end
43
+ end
44
+
45
+ def policy_present?(headers)
46
+ headers[POLICY]
47
+ end
48
+
49
+ def policy_empty?(policy)
50
+ policy&.directives&.empty?
51
+ end
52
+ end
53
+
54
+ module Request
55
+ POLICY = "action_dispatch.permissions_policy"
56
+
57
+ def permissions_policy
58
+ get_header(POLICY)
59
+ end
60
+
61
+ def permissions_policy=(policy)
62
+ set_header(POLICY, policy)
63
+ end
64
+ end
65
+
66
+ MAPPINGS = {
67
+ self: "'self'",
68
+ none: "'none'",
69
+ }.freeze
70
+
71
+ # List of available permissions can be found at
72
+ # https://github.com/w3c/webappsec-permissions-policy/blob/master/features.md#policy-controlled-features
73
+ DIRECTIVES = {
74
+ accelerometer: "accelerometer",
75
+ ambient_light_sensor: "ambient-light-sensor",
76
+ autoplay: "autoplay",
77
+ camera: "camera",
78
+ encrypted_media: "encrypted-media",
79
+ fullscreen: "fullscreen",
80
+ geolocation: "geolocation",
81
+ gyroscope: "gyroscope",
82
+ magnetometer: "magnetometer",
83
+ microphone: "microphone",
84
+ midi: "midi",
85
+ payment: "payment",
86
+ picture_in_picture: "picture-in-picture",
87
+ speaker: "speaker",
88
+ usb: "usb",
89
+ vibrate: "vibrate",
90
+ vr: "vr",
91
+ }.freeze
92
+
93
+ private_constant :MAPPINGS, :DIRECTIVES
94
+
95
+ attr_reader :directives
96
+
97
+ def initialize
98
+ @directives = {}
99
+ yield self if block_given?
100
+ end
101
+
102
+ def initialize_copy(other)
103
+ @directives = other.directives.deep_dup
104
+ end
105
+
106
+ DIRECTIVES.each do |name, directive|
107
+ define_method(name) do |*sources|
108
+ if sources.first
109
+ @directives[directive] = apply_mappings(sources)
110
+ else
111
+ @directives.delete(directive)
112
+ end
113
+ end
114
+ end
115
+
116
+ def build(context = nil)
117
+ build_directives(context).compact.join("; ")
118
+ end
119
+
120
+ private
121
+ def apply_mappings(sources)
122
+ sources.map do |source|
123
+ case source
124
+ when Symbol
125
+ apply_mapping(source)
126
+ when String, Proc
127
+ source
128
+ else
129
+ raise ArgumentError, "Invalid HTTP permissions policy source: #{source.inspect}"
130
+ end
131
+ end
132
+ end
133
+
134
+ def apply_mapping(source)
135
+ MAPPINGS.fetch(source) do
136
+ raise ArgumentError, "Unknown HTTP permissions policy source mapping: #{source.inspect}"
137
+ end
138
+ end
139
+
140
+ def build_directives(context)
141
+ @directives.map do |directive, sources|
142
+ if sources.is_a?(Array)
143
+ "#{directive} #{build_directive(sources, context).join(' ')}"
144
+ elsif sources
145
+ directive
146
+ else
147
+ nil
148
+ end
149
+ end
150
+ end
151
+
152
+ def build_directive(sources, context)
153
+ sources.map { |source| resolve_source(source, context) }
154
+ end
155
+
156
+ def resolve_source(source, context)
157
+ case source
158
+ when String
159
+ source
160
+ when Symbol
161
+ source.to_s
162
+ when Proc
163
+ if context.nil?
164
+ raise RuntimeError, "Missing context for the dynamic permissions policy source: #{source.inspect}"
165
+ else
166
+ context.instance_exec(&source)
167
+ end
168
+ else
169
+ raise RuntimeError, "Unexpected permissions policy source: #{source.inspect}"
170
+ end
171
+ end
172
+ end
173
+ end