actionpack 5.2.4.rc1 → 6.0.0.rc2

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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +179 -335
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -2
  5. data/lib/abstract_controller/base.rb +4 -2
  6. data/lib/abstract_controller/caching/fragments.rb +6 -22
  7. data/lib/abstract_controller/callbacks.rb +12 -0
  8. data/lib/abstract_controller/collector.rb +1 -1
  9. data/lib/abstract_controller/helpers.rb +2 -2
  10. data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
  11. data/lib/abstract_controller/translation.rb +1 -0
  12. data/lib/action_controller.rb +1 -0
  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 -1
  16. data/lib/action_controller/log_subscriber.rb +8 -5
  17. data/lib/action_controller/metal.rb +1 -1
  18. data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
  19. data/lib/action_controller/metal/conditional_get.rb +9 -3
  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 +22 -1
  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 -5
  31. data/lib/action_controller/metal/live.rb +29 -27
  32. data/lib/action_controller/metal/mime_responds.rb +13 -2
  33. data/lib/action_controller/metal/params_wrapper.rb +17 -13
  34. data/lib/action_controller/metal/redirecting.rb +5 -5
  35. data/lib/action_controller/metal/renderers.rb +1 -1
  36. data/lib/action_controller/metal/rendering.rb +2 -2
  37. data/lib/action_controller/metal/request_forgery_protection.rb +23 -12
  38. data/lib/action_controller/metal/strong_parameters.rb +63 -44
  39. data/lib/action_controller/metal/url_for.rb +1 -1
  40. data/lib/action_controller/railties/helpers.rb +1 -1
  41. data/lib/action_controller/renderer.rb +16 -3
  42. data/lib/action_controller/template_assertions.rb +1 -1
  43. data/lib/action_controller/test_case.rb +3 -7
  44. data/lib/action_dispatch.rb +4 -1
  45. data/lib/action_dispatch/http/cache.rb +14 -10
  46. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  47. data/lib/action_dispatch/http/content_security_policy.rb +28 -16
  48. data/lib/action_dispatch/http/filter_parameters.rb +8 -6
  49. data/lib/action_dispatch/http/filter_redirect.rb +1 -1
  50. data/lib/action_dispatch/http/headers.rb +1 -1
  51. data/lib/action_dispatch/http/mime_negotiation.rb +7 -5
  52. data/lib/action_dispatch/http/mime_type.rb +14 -6
  53. data/lib/action_dispatch/http/parameter_filter.rb +5 -79
  54. data/lib/action_dispatch/http/parameters.rb +13 -3
  55. data/lib/action_dispatch/http/request.rb +10 -13
  56. data/lib/action_dispatch/http/response.rb +33 -19
  57. data/lib/action_dispatch/http/upload.rb +9 -1
  58. data/lib/action_dispatch/http/url.rb +81 -81
  59. data/lib/action_dispatch/journey/formatter.rb +2 -2
  60. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
  61. data/lib/action_dispatch/journey/nodes/node.rb +9 -8
  62. data/lib/action_dispatch/journey/path/pattern.rb +6 -2
  63. data/lib/action_dispatch/journey/route.rb +5 -4
  64. data/lib/action_dispatch/journey/router.rb +0 -3
  65. data/lib/action_dispatch/journey/router/utils.rb +10 -10
  66. data/lib/action_dispatch/journey/routes.rb +0 -1
  67. data/lib/action_dispatch/journey/scanner.rb +11 -4
  68. data/lib/action_dispatch/journey/visitors.rb +1 -1
  69. data/lib/action_dispatch/middleware/actionable_exceptions.rb +39 -0
  70. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  71. data/lib/action_dispatch/middleware/cookies.rb +46 -72
  72. data/lib/action_dispatch/middleware/debug_exceptions.rb +39 -59
  73. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  74. data/lib/action_dispatch/middleware/debug_view.rb +68 -0
  75. data/lib/action_dispatch/middleware/exception_wrapper.rb +49 -15
  76. data/lib/action_dispatch/middleware/flash.rb +1 -1
  77. data/lib/action_dispatch/middleware/host_authorization.rb +103 -0
  78. data/lib/action_dispatch/middleware/public_exceptions.rb +6 -2
  79. data/lib/action_dispatch/middleware/remote_ip.rb +6 -8
  80. data/lib/action_dispatch/middleware/request_id.rb +2 -2
  81. data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -6
  82. data/lib/action_dispatch/middleware/show_exceptions.rb +1 -1
  83. data/lib/action_dispatch/middleware/ssl.rb +8 -8
  84. data/lib/action_dispatch/middleware/stack.rb +33 -1
  85. data/lib/action_dispatch/middleware/static.rb +5 -6
  86. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  87. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  88. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
  89. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  90. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  91. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  92. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  93. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  94. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -4
  95. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  96. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +7 -4
  97. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -2
  98. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -0
  99. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  101. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  102. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  103. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
  104. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -0
  105. data/lib/action_dispatch/railtie.rb +3 -0
  106. data/lib/action_dispatch/request/session.rb +8 -0
  107. data/lib/action_dispatch/routing.rb +21 -20
  108. data/lib/action_dispatch/routing/inspector.rb +99 -50
  109. data/lib/action_dispatch/routing/mapper.rb +60 -38
  110. data/lib/action_dispatch/routing/polymorphic_routes.rb +3 -4
  111. data/lib/action_dispatch/routing/route_set.rb +24 -27
  112. data/lib/action_dispatch/routing/url_for.rb +1 -0
  113. data/lib/action_dispatch/system_test_case.rb +23 -2
  114. data/lib/action_dispatch/system_testing/browser.rb +38 -7
  115. data/lib/action_dispatch/system_testing/driver.rb +10 -1
  116. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +6 -5
  117. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +7 -5
  118. data/lib/action_dispatch/testing/assertions.rb +1 -1
  119. data/lib/action_dispatch/testing/assertions/response.rb +2 -3
  120. data/lib/action_dispatch/testing/assertions/routing.rb +15 -3
  121. data/lib/action_dispatch/testing/integration.rb +12 -5
  122. data/lib/action_dispatch/testing/request_encoder.rb +2 -2
  123. data/lib/action_dispatch/testing/test_process.rb +2 -2
  124. data/lib/action_dispatch/testing/test_response.rb +4 -32
  125. data/lib/action_pack.rb +1 -1
  126. data/lib/action_pack/gem_version.rb +4 -4
  127. metadata +24 -13
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2004-2018 David Heinemeier Hansson
4
+ # Copyright (c) 2004-2019 David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -49,11 +49,14 @@ module ActionDispatch
49
49
  end
50
50
 
51
51
  autoload_under "middleware" do
52
+ autoload :HostAuthorization
52
53
  autoload :RequestId
53
54
  autoload :Callbacks
54
55
  autoload :Cookies
56
+ autoload :ActionableExceptions
55
57
  autoload :DebugExceptions
56
58
  autoload :DebugLocks
59
+ autoload :DebugView
57
60
  autoload :ExceptionWrapper
58
61
  autoload :Executor
59
62
  autoload :Flash
@@ -4,8 +4,8 @@ module ActionDispatch
4
4
  module Http
5
5
  module Cache
6
6
  module Request
7
- HTTP_IF_MODIFIED_SINCE = "HTTP_IF_MODIFIED_SINCE".freeze
8
- HTTP_IF_NONE_MATCH = "HTTP_IF_NONE_MATCH".freeze
7
+ HTTP_IF_MODIFIED_SINCE = "HTTP_IF_MODIFIED_SINCE"
8
+ HTTP_IF_NONE_MATCH = "HTTP_IF_NONE_MATCH"
9
9
 
10
10
  def if_modified_since
11
11
  if since = get_header(HTTP_IF_MODIFIED_SINCE)
@@ -124,8 +124,8 @@ module ActionDispatch
124
124
 
125
125
  private
126
126
 
127
- DATE = "Date".freeze
128
- LAST_MODIFIED = "Last-Modified".freeze
127
+ DATE = "Date"
128
+ LAST_MODIFIED = "Last-Modified"
129
129
  SPECIAL_KEYS = Set.new(%w[extras no-cache max-age public private must-revalidate])
130
130
 
131
131
  def generate_weak_etag(validators)
@@ -166,11 +166,11 @@ module ActionDispatch
166
166
  @cache_control = cache_control_headers
167
167
  end
168
168
 
169
- DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
170
- NO_CACHE = "no-cache".freeze
171
- PUBLIC = "public".freeze
172
- PRIVATE = "private".freeze
173
- MUST_REVALIDATE = "must-revalidate".freeze
169
+ DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
170
+ NO_CACHE = "no-cache"
171
+ PUBLIC = "public"
172
+ PRIVATE = "private"
173
+ MUST_REVALIDATE = "must-revalidate"
174
174
 
175
175
  def handle_conditional_get!
176
176
  # Normally default cache control setting is handled by ETag
@@ -204,13 +204,17 @@ module ActionDispatch
204
204
 
205
205
  self._cache_control = options.join(", ")
206
206
  else
207
- extras = control[:extras]
207
+ extras = control[:extras]
208
208
  max_age = control[:max_age]
209
+ stale_while_revalidate = control[:stale_while_revalidate]
210
+ stale_if_error = control[:stale_if_error]
209
211
 
210
212
  options = []
211
213
  options << "max-age=#{max_age.to_i}" if max_age
212
214
  options << (control[:public] ? PUBLIC : PRIVATE)
213
215
  options << MUST_REVALIDATE if control[:must_revalidate]
216
+ options << "stale-while-revalidate=#{stale_while_revalidate.to_i}" if stale_while_revalidate
217
+ options << "stale-if-error=#{stale_if_error.to_i}" if stale_if_error
214
218
  options.concat(extras) if extras
215
219
 
216
220
  self._cache_control = options.join(", ")
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module Http
5
+ class ContentDisposition # :nodoc:
6
+ def self.format(disposition:, filename:)
7
+ new(disposition: disposition, filename: filename).to_s
8
+ end
9
+
10
+ attr_reader :disposition, :filename
11
+
12
+ def initialize(disposition:, filename:)
13
+ @disposition = disposition
14
+ @filename = filename
15
+ end
16
+
17
+ TRADITIONAL_ESCAPED_CHAR = /[^ A-Za-z0-9!#$+.^_`|~-]/
18
+
19
+ def ascii_filename
20
+ 'filename="' + percent_escape(I18n.transliterate(filename), TRADITIONAL_ESCAPED_CHAR) + '"'
21
+ end
22
+
23
+ RFC_5987_ESCAPED_CHAR = /[^A-Za-z0-9!#$&+.^_`|~-]/
24
+
25
+ def utf8_filename
26
+ "filename*=UTF-8''" + percent_escape(filename, RFC_5987_ESCAPED_CHAR)
27
+ end
28
+
29
+ def to_s
30
+ if filename
31
+ "#{disposition}; #{ascii_filename}; #{utf8_filename}"
32
+ else
33
+ "#{disposition}"
34
+ end
35
+ end
36
+
37
+ private
38
+ def percent_escape(string, pattern)
39
+ string.gsub(pattern) do |char|
40
+ char.bytes.map { |byte| "%%%02X" % byte }.join
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -5,9 +5,9 @@ require "active_support/core_ext/object/deep_dup"
5
5
  module ActionDispatch #:nodoc:
6
6
  class ContentSecurityPolicy
7
7
  class Middleware
8
- CONTENT_TYPE = "Content-Type".freeze
9
- POLICY = "Content-Security-Policy".freeze
10
- POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only".freeze
8
+ CONTENT_TYPE = "Content-Type"
9
+ POLICY = "Content-Security-Policy"
10
+ POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only"
11
11
 
12
12
  def initialize(app)
13
13
  @app = app
@@ -22,8 +22,9 @@ module ActionDispatch #:nodoc:
22
22
 
23
23
  if policy = request.content_security_policy
24
24
  nonce = request.content_security_policy_nonce
25
+ nonce_directives = request.content_security_policy_nonce_directives
25
26
  context = request.controller_instance || request
26
- headers[header_name(request)] = policy.build(context, nonce)
27
+ headers[header_name(request)] = policy.build(context, nonce, nonce_directives)
27
28
  end
28
29
 
29
30
  response
@@ -51,10 +52,11 @@ module ActionDispatch #:nodoc:
51
52
  end
52
53
 
53
54
  module Request
54
- POLICY = "action_dispatch.content_security_policy".freeze
55
- POLICY_REPORT_ONLY = "action_dispatch.content_security_policy_report_only".freeze
56
- NONCE_GENERATOR = "action_dispatch.content_security_policy_nonce_generator".freeze
57
- NONCE = "action_dispatch.content_security_policy_nonce".freeze
55
+ POLICY = "action_dispatch.content_security_policy"
56
+ POLICY_REPORT_ONLY = "action_dispatch.content_security_policy_report_only"
57
+ NONCE_GENERATOR = "action_dispatch.content_security_policy_nonce_generator"
58
+ NONCE = "action_dispatch.content_security_policy_nonce"
59
+ NONCE_DIRECTIVES = "action_dispatch.content_security_policy_nonce_directives"
58
60
 
59
61
  def content_security_policy
60
62
  get_header(POLICY)
@@ -80,6 +82,14 @@ module ActionDispatch #:nodoc:
80
82
  set_header(NONCE_GENERATOR, generator)
81
83
  end
82
84
 
85
+ def content_security_policy_nonce_directives
86
+ get_header(NONCE_DIRECTIVES)
87
+ end
88
+
89
+ def content_security_policy_nonce_directives=(generator)
90
+ set_header(NONCE_DIRECTIVES, generator)
91
+ end
92
+
83
93
  def content_security_policy_nonce
84
94
  if content_security_policy_nonce_generator
85
95
  if nonce = get_header(NONCE)
@@ -127,14 +137,15 @@ module ActionDispatch #:nodoc:
127
137
  manifest_src: "manifest-src",
128
138
  media_src: "media-src",
129
139
  object_src: "object-src",
140
+ prefetch_src: "prefetch-src",
130
141
  script_src: "script-src",
131
142
  style_src: "style-src",
132
143
  worker_src: "worker-src"
133
144
  }.freeze
134
145
 
135
- NONCE_DIRECTIVES = %w[script-src].freeze
146
+ DEFAULT_NONCE_DIRECTIVES = %w[script-src style-src].freeze
136
147
 
137
- private_constant :MAPPINGS, :DIRECTIVES, :NONCE_DIRECTIVES
148
+ private_constant :MAPPINGS, :DIRECTIVES, :DEFAULT_NONCE_DIRECTIVES
138
149
 
139
150
  attr_reader :directives
140
151
 
@@ -203,8 +214,9 @@ module ActionDispatch #:nodoc:
203
214
  end
204
215
  end
205
216
 
206
- def build(context = nil, nonce = nil)
207
- build_directives(context, nonce).compact.join("; ")
217
+ def build(context = nil, nonce = nil, nonce_directives = nil)
218
+ nonce_directives = DEFAULT_NONCE_DIRECTIVES if nonce_directives.nil?
219
+ build_directives(context, nonce, nonce_directives).compact.join("; ")
208
220
  end
209
221
 
210
222
  private
@@ -227,10 +239,10 @@ module ActionDispatch #:nodoc:
227
239
  end
228
240
  end
229
241
 
230
- def build_directives(context, nonce)
242
+ def build_directives(context, nonce, nonce_directives)
231
243
  @directives.map do |directive, sources|
232
244
  if sources.is_a?(Array)
233
- if nonce && nonce_directive?(directive)
245
+ if nonce && nonce_directive?(directive, nonce_directives)
234
246
  "#{directive} #{build_directive(sources, context).join(' ')} 'nonce-#{nonce}'"
235
247
  else
236
248
  "#{directive} #{build_directive(sources, context).join(' ')}"
@@ -265,8 +277,8 @@ module ActionDispatch #:nodoc:
265
277
  end
266
278
  end
267
279
 
268
- def nonce_directive?(directive)
269
- NONCE_DIRECTIVES.include?(directive)
280
+ def nonce_directive?(directive, nonce_directives)
281
+ nonce_directives.include?(directive)
270
282
  end
271
283
  end
272
284
  end
@@ -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]"
@@ -28,8 +28,8 @@ module ActionDispatch
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.
@@ -69,7 +71,7 @@ module ActionDispatch
69
71
  end
70
72
 
71
73
  def parameter_filter_for(filters) # :doc:
72
- ParameterFilter.new(filters)
74
+ ActiveSupport::ParameterFilter.new(filters)
73
75
  end
74
76
 
75
77
  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?
@@ -121,7 +121,7 @@ module ActionDispatch
121
121
  # not contained within the headers hash.
122
122
  def env_name(key)
123
123
  key = key.to_s
124
- if key =~ HTTP_HEADER
124
+ if HTTP_HEADER.match?(key)
125
125
  key = key.upcase.tr("-", "_")
126
126
  key = "HTTP_" + key unless CGI_VARIABLES.include?(key)
127
127
  end
@@ -7,6 +7,11 @@ module ActionDispatch
7
7
  module MimeNegotiation
8
8
  extend ActiveSupport::Concern
9
9
 
10
+ RESCUABLE_MIME_FORMAT_ERRORS = [
11
+ ActionController::BadRequest,
12
+ ActionDispatch::Http::Parameters::ParseError,
13
+ ]
14
+
10
15
  included do
11
16
  mattr_accessor :ignore_accept_header, default: false
12
17
  end
@@ -59,7 +64,7 @@ module ActionDispatch
59
64
  fetch_header("action_dispatch.request.formats") do |k|
60
65
  params_readable = begin
61
66
  parameters[:format]
62
- rescue ActionController::BadRequest
67
+ rescue *RESCUABLE_MIME_FORMAT_ERRORS
63
68
  false
64
69
  end
65
70
 
@@ -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
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # -*- frozen-string-literal: true -*-
4
-
5
3
  require "singleton"
6
4
  require "active_support/core_ext/string/starts_ends_with"
7
5
 
@@ -74,7 +72,7 @@ module Mime
74
72
  def initialize(index, name, q = nil)
75
73
  @index = index
76
74
  @name = name
77
- q ||= 0.0 if @name == "*/*".freeze # Default wildcard match to end of list.
75
+ q ||= 0.0 if @name == "*/*" # Default wildcard match to end of list.
78
76
  @q = ((q || 1.0).to_f * 100).to_i
79
77
  end
80
78
 
@@ -172,6 +170,7 @@ module Mime
172
170
  def parse(accept_header)
173
171
  if !accept_header.include?(",")
174
172
  accept_header = accept_header.split(PARAMETER_SEPARATOR_REGEXP).first
173
+ return [] unless accept_header
175
174
  parse_trailing_star(accept_header) || [Mime::Type.lookup(accept_header)].compact
176
175
  else
177
176
  list, index = [], 0
@@ -223,7 +222,18 @@ module Mime
223
222
 
224
223
  attr_reader :hash
225
224
 
225
+ MIME_NAME = "[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}"
226
+ MIME_PARAMETER_KEY = "[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}"
227
+ MIME_PARAMETER_VALUE = "#{Regexp.escape('"')}?[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}#{Regexp.escape('"')}?"
228
+ MIME_PARAMETER = "\s*\;\s+#{MIME_PARAMETER_KEY}(?:\=#{MIME_PARAMETER_VALUE})?"
229
+ MIME_REGEXP = /\A(?:\*\/\*|#{MIME_NAME}\/(?:\*|#{MIME_NAME})(?:\s*#{MIME_PARAMETER}\s*)*)\z/
230
+
231
+ class InvalidMimeType < StandardError; end
232
+
226
233
  def initialize(string, symbol = nil, synonyms = [])
234
+ unless MIME_REGEXP.match?(string)
235
+ raise InvalidMimeType, "#{string.inspect} is not a valid MIME type"
236
+ end
227
237
  @symbol, @synonyms = symbol, synonyms
228
238
  @string = string
229
239
  @hash = [@string, @synonyms, @symbol].hash
@@ -279,8 +289,6 @@ module Mime
279
289
 
280
290
  def all?; false; end
281
291
 
282
- # TODO Change this to private once we've dropped Ruby 2.2 support.
283
- # Workaround for Ruby 2.2 "private attribute?" warning.
284
292
  protected
285
293
 
286
294
  attr_reader :string, :synonyms
@@ -307,7 +315,7 @@ module Mime
307
315
  include Singleton
308
316
 
309
317
  def initialize
310
- super "*/*", :all
318
+ super "*/*", nil
311
319
  end
312
320
 
313
321
  def all?; true; end
@@ -1,86 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/object/duplicable"
3
+ require "active_support/deprecation/constant_accessor"
4
+ require "active_support/parameter_filter"
4
5
 
5
6
  module ActionDispatch
6
7
  module Http
7
- class ParameterFilter
8
- FILTERED = "[FILTERED]".freeze # :nodoc:
9
-
10
- def initialize(filters = [])
11
- @filters = filters
12
- end
13
-
14
- def filter(params)
15
- compiled_filter.call(params)
16
- end
17
-
18
- private
19
-
20
- def compiled_filter
21
- @compiled_filter ||= CompiledFilter.compile(@filters)
22
- end
23
-
24
- class CompiledFilter # :nodoc:
25
- def self.compile(filters)
26
- return lambda { |params| params.dup } if filters.empty?
27
-
28
- strings, regexps, blocks = [], [], []
29
-
30
- filters.each do |item|
31
- case item
32
- when Proc
33
- blocks << item
34
- when Regexp
35
- regexps << item
36
- else
37
- strings << Regexp.escape(item.to_s)
38
- end
39
- end
40
-
41
- deep_regexps, regexps = regexps.partition { |r| r.to_s.include?("\\.".freeze) }
42
- deep_strings, strings = strings.partition { |s| s.include?("\\.".freeze) }
43
-
44
- regexps << Regexp.new(strings.join("|".freeze), true) unless strings.empty?
45
- deep_regexps << Regexp.new(deep_strings.join("|".freeze), true) unless deep_strings.empty?
46
-
47
- new regexps, deep_regexps, blocks
48
- end
49
-
50
- attr_reader :regexps, :deep_regexps, :blocks
51
-
52
- def initialize(regexps, deep_regexps, blocks)
53
- @regexps = regexps
54
- @deep_regexps = deep_regexps.any? ? deep_regexps : nil
55
- @blocks = blocks
56
- end
57
-
58
- def call(original_params, parents = [])
59
- filtered_params = original_params.class.new
60
-
61
- original_params.each do |key, value|
62
- parents.push(key) if deep_regexps
63
- if regexps.any? { |r| key =~ r }
64
- value = FILTERED
65
- elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| joined =~ r }
66
- value = FILTERED
67
- elsif value.is_a?(Hash)
68
- value = call(value, parents)
69
- elsif value.is_a?(Array)
70
- value = value.map { |v| v.is_a?(Hash) ? call(v, parents) : v }
71
- elsif blocks.any?
72
- key = key.dup if key.duplicable?
73
- value = value.dup if value.duplicable?
74
- blocks.each { |b| b.call(key, value) }
75
- end
76
- parents.pop if deep_regexps
77
-
78
- filtered_params[key] = value
79
- end
80
-
81
- filtered_params
82
- end
83
- end
84
- end
8
+ include ActiveSupport::Deprecation::DeprecatedConstantAccessor
9
+ deprecate_constant "ParameterFilter", "ActiveSupport::ParameterFilter",
10
+ message: "ActionDispatch::Http::ParameterFilter is deprecated and will be removed from Rails 6.1. Use ActiveSupport::ParameterFilter instead."
85
11
  end
86
12
  end