actionpack 5.2.8.1 → 6.0.0.beta1

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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +109 -482
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/abstract_controller/base.rb +4 -2
  6. data/lib/abstract_controller/caching/fragments.rb +6 -21
  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/action_controller/api.rb +2 -1
  12. data/lib/action_controller/base.rb +2 -7
  13. data/lib/action_controller/caching.rb +1 -1
  14. data/lib/action_controller/log_subscriber.rb +8 -5
  15. data/lib/action_controller/metal/conditional_get.rb +9 -3
  16. data/lib/action_controller/metal/data_streaming.rb +5 -6
  17. data/lib/action_controller/metal/default_headers.rb +17 -0
  18. data/lib/action_controller/metal/exceptions.rb +22 -1
  19. data/lib/action_controller/metal/flash.rb +5 -5
  20. data/lib/action_controller/metal/force_ssl.rb +17 -57
  21. data/lib/action_controller/metal/head.rb +1 -1
  22. data/lib/action_controller/metal/helpers.rb +1 -2
  23. data/lib/action_controller/metal/http_authentication.rb +21 -22
  24. data/lib/action_controller/metal/implicit_render.rb +2 -12
  25. data/lib/action_controller/metal/instrumentation.rb +3 -5
  26. data/lib/action_controller/metal/live.rb +28 -26
  27. data/lib/action_controller/metal/mime_responds.rb +13 -2
  28. data/lib/action_controller/metal/params_wrapper.rb +18 -14
  29. data/lib/action_controller/metal/redirecting.rb +32 -11
  30. data/lib/action_controller/metal/rendering.rb +1 -1
  31. data/lib/action_controller/metal/request_forgery_protection.rb +32 -97
  32. data/lib/action_controller/metal/strong_parameters.rb +57 -34
  33. data/lib/action_controller/metal/url_for.rb +1 -1
  34. data/lib/action_controller/metal.rb +2 -2
  35. data/lib/action_controller/railties/helpers.rb +1 -1
  36. data/lib/action_controller/renderer.rb +15 -2
  37. data/lib/action_controller/test_case.rb +5 -9
  38. data/lib/action_controller.rb +1 -0
  39. data/lib/action_dispatch/http/cache.rb +14 -10
  40. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  41. data/lib/action_dispatch/http/content_security_policy.rb +17 -8
  42. data/lib/action_dispatch/http/filter_parameters.rb +8 -6
  43. data/lib/action_dispatch/http/filter_redirect.rb +1 -1
  44. data/lib/action_dispatch/http/headers.rb +1 -1
  45. data/lib/action_dispatch/http/mime_negotiation.rb +7 -10
  46. data/lib/action_dispatch/http/mime_type.rb +1 -5
  47. data/lib/action_dispatch/http/parameter_filter.rb +5 -79
  48. data/lib/action_dispatch/http/parameters.rb +13 -3
  49. data/lib/action_dispatch/http/request.rb +10 -13
  50. data/lib/action_dispatch/http/response.rb +14 -14
  51. data/lib/action_dispatch/http/upload.rb +5 -0
  52. data/lib/action_dispatch/http/url.rb +81 -81
  53. data/lib/action_dispatch/journey/formatter.rb +1 -1
  54. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
  55. data/lib/action_dispatch/journey/nodes/node.rb +9 -8
  56. data/lib/action_dispatch/journey/path/pattern.rb +3 -4
  57. data/lib/action_dispatch/journey/router/utils.rb +10 -10
  58. data/lib/action_dispatch/journey/router.rb +0 -3
  59. data/lib/action_dispatch/journey/scanner.rb +11 -4
  60. data/lib/action_dispatch/journey/visitors.rb +1 -1
  61. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  62. data/lib/action_dispatch/middleware/cookies.rb +49 -70
  63. data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -58
  64. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  65. data/lib/action_dispatch/middleware/debug_view.rb +50 -0
  66. data/lib/action_dispatch/middleware/exception_wrapper.rb +36 -7
  67. data/lib/action_dispatch/middleware/executor.rb +1 -1
  68. data/lib/action_dispatch/middleware/flash.rb +1 -1
  69. data/lib/action_dispatch/middleware/host_authorization.rb +103 -0
  70. data/lib/action_dispatch/middleware/remote_ip.rb +6 -8
  71. data/lib/action_dispatch/middleware/request_id.rb +2 -2
  72. data/lib/action_dispatch/middleware/session/abstract_store.rb +0 -14
  73. data/lib/action_dispatch/middleware/session/cache_store.rb +6 -11
  74. data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -27
  75. data/lib/action_dispatch/middleware/ssl.rb +8 -8
  76. data/lib/action_dispatch/middleware/stack.rb +2 -2
  77. data/lib/action_dispatch/middleware/static.rb +5 -6
  78. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  79. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  80. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  81. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  82. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +20 -2
  83. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +4 -4
  84. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +2 -2
  85. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  86. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  87. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  88. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  89. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
  90. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -0
  91. data/lib/action_dispatch/railtie.rb +1 -0
  92. data/lib/action_dispatch/request/session.rb +8 -6
  93. data/lib/action_dispatch/routing/inspector.rb +99 -50
  94. data/lib/action_dispatch/routing/mapper.rb +36 -29
  95. data/lib/action_dispatch/routing/polymorphic_routes.rb +7 -12
  96. data/lib/action_dispatch/routing/route_set.rb +11 -12
  97. data/lib/action_dispatch/routing/url_for.rb +1 -0
  98. data/lib/action_dispatch/routing.rb +3 -2
  99. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +3 -3
  100. data/lib/action_dispatch/testing/assertions/response.rb +2 -3
  101. data/lib/action_dispatch/testing/assertions/routing.rb +7 -2
  102. data/lib/action_dispatch/testing/integration.rb +11 -5
  103. data/lib/action_dispatch/testing/test_process.rb +2 -2
  104. data/lib/action_dispatch/testing/test_response.rb +4 -32
  105. data/lib/action_dispatch.rb +7 -6
  106. data/lib/action_pack/gem_version.rb +4 -4
  107. data/lib/action_pack.rb +1 -1
  108. metadata +25 -23
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/hash/indifferent_access"
4
- require "active_support/core_ext/hash/transform_values"
5
4
  require "active_support/core_ext/array/wrap"
6
5
  require "active_support/core_ext/string/filters"
7
6
  require "active_support/core_ext/object/to_query"
@@ -59,7 +58,7 @@ module ActionController
59
58
 
60
59
  # == Action Controller \Parameters
61
60
  #
62
- # Allows you to choose which attributes should be whitelisted for mass updating
61
+ # Allows you to choose which attributes should be permitted for mass updating
63
62
  # and thus prevent accidentally exposing that which shouldn't be exposed.
64
63
  # Provides two methods for this purpose: #require and #permit. The former is
65
64
  # used to mark parameters as required. The latter is used to set the parameter
@@ -133,6 +132,15 @@ module ActionController
133
132
  #
134
133
  # Returns a hash that can be used as the JSON representation for the parameters.
135
134
 
135
+ ##
136
+ # :method: each_key
137
+ #
138
+ # :call-seq:
139
+ # each_key()
140
+ #
141
+ # Calls block once for each key in the parameters, passing the key.
142
+ # If no block is given, an enumerator is returned instead.
143
+
136
144
  ##
137
145
  # :method: empty?
138
146
  #
@@ -205,7 +213,7 @@ module ActionController
205
213
  #
206
214
  # Returns a new array of the values of the parameters.
207
215
  delegate :keys, :key?, :has_key?, :values, :has_value?, :value?, :empty?, :include?,
208
- :as_json, :to_s, to: :@parameters
216
+ :as_json, :to_s, :each_key, to: :@parameters
209
217
 
210
218
  # By default, never raise an UnpermittedParameters exception if these
211
219
  # params are present. The default includes both 'controller' and 'action'
@@ -337,11 +345,17 @@ module ActionController
337
345
  @parameters.each_pair do |key, value|
338
346
  yield [key, convert_hashes_to_parameters(key, value)]
339
347
  end
340
-
341
- self
342
348
  end
343
349
  alias_method :each, :each_pair
344
350
 
351
+ # Convert all hashes in values into parameters, then yield each value in
352
+ # the same way as <tt>Hash#each_value</tt>.
353
+ def each_value(&block)
354
+ @parameters.each_pair do |key, value|
355
+ yield convert_hashes_to_parameters(key, value)
356
+ end
357
+ end
358
+
345
359
  # Attribute that keeps track of converted arrays, if any, to avoid double
346
360
  # looping in the common use case permit + mass-assignment. Defined in a
347
361
  # method to instantiate it only if needed.
@@ -508,7 +522,7 @@ module ActionController
508
522
  #
509
523
  # Note that if you use +permit+ in a key that points to a hash,
510
524
  # it won't allow all the hash. You also need to specify which
511
- # attributes inside the hash should be whitelisted.
525
+ # attributes inside the hash should be permitted.
512
526
  #
513
527
  # params = ActionController::Parameters.new({
514
528
  # person: {
@@ -585,20 +599,18 @@ module ActionController
585
599
  )
586
600
  end
587
601
 
588
- if Hash.method_defined?(:dig)
589
- # Extracts the nested parameter from the given +keys+ by calling +dig+
590
- # at each step. Returns +nil+ if any intermediate step is +nil+.
591
- #
592
- # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } })
593
- # params.dig(:foo, :bar, :baz) # => 1
594
- # params.dig(:foo, :zot, :xyz) # => nil
595
- #
596
- # params2 = ActionController::Parameters.new(foo: [10, 11, 12])
597
- # params2.dig(:foo, 1) # => 11
598
- def dig(*keys)
599
- convert_hashes_to_parameters(keys.first, @parameters[keys.first])
600
- @parameters.dig(*keys)
601
- end
602
+ # Extracts the nested parameter from the given +keys+ by calling +dig+
603
+ # at each step. Returns +nil+ if any intermediate step is +nil+.
604
+ #
605
+ # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } })
606
+ # params.dig(:foo, :bar, :baz) # => 1
607
+ # params.dig(:foo, :zot, :xyz) # => nil
608
+ #
609
+ # params2 = ActionController::Parameters.new(foo: [10, 11, 12])
610
+ # params2.dig(:foo, 1) # => 11
611
+ def dig(*keys)
612
+ convert_hashes_to_parameters(keys.first, @parameters[keys.first])
613
+ @parameters.dig(*keys)
602
614
  end
603
615
 
604
616
  # Returns a new <tt>ActionController::Parameters</tt> instance that
@@ -798,9 +810,7 @@ module ActionController
798
810
  protected
799
811
  attr_reader :parameters
800
812
 
801
- def permitted=(new_permitted)
802
- @permitted = new_permitted
803
- end
813
+ attr_writer :permitted
804
814
 
805
815
  def fields_for_style?
806
816
  @parameters.all? { |k, v| k =~ /\A-?\d+\z/ && (v.is_a?(Hash) || v.is_a?(Parameters)) }
@@ -911,15 +921,28 @@ module ActionController
911
921
  PERMITTED_SCALAR_TYPES.any? { |type| value.is_a?(type) }
912
922
  end
913
923
 
914
- def permitted_scalar_filter(params, key)
915
- if has_key?(key) && permitted_scalar?(self[key])
916
- params[key] = self[key]
924
+ # Adds existing keys to the params if their values are scalar.
925
+ #
926
+ # For example:
927
+ #
928
+ # puts self.keys #=> ["zipcode(90210i)"]
929
+ # params = {}
930
+ #
931
+ # permitted_scalar_filter(params, "zipcode")
932
+ #
933
+ # puts params.keys # => ["zipcode"]
934
+ def permitted_scalar_filter(params, permitted_key)
935
+ permitted_key = permitted_key.to_s
936
+
937
+ if has_key?(permitted_key) && permitted_scalar?(self[permitted_key])
938
+ params[permitted_key] = self[permitted_key]
917
939
  end
918
940
 
919
- keys.grep(/\A#{Regexp.escape(key)}\(\d+[if]?\)\z/) do |k|
920
- if permitted_scalar?(self[k])
921
- params[k] = self[k]
922
- end
941
+ each_key do |key|
942
+ next unless key =~ /\(\d+[if]?\)\z/
943
+ next unless $~.pre_match == permitted_key
944
+
945
+ params[key] = self[key] if permitted_scalar?(self[key])
923
946
  end
924
947
  end
925
948
 
@@ -1004,8 +1027,8 @@ module ActionController
1004
1027
  #
1005
1028
  # It provides an interface for protecting attributes from end-user
1006
1029
  # assignment. This makes Action Controller parameters forbidden
1007
- # to be used in Active Model mass assignment until they have been
1008
- # whitelisted.
1030
+ # to be used in Active Model mass assignment until they have been explicitly
1031
+ # enumerated.
1009
1032
  #
1010
1033
  # In addition, parameters can be marked as required and flow through a
1011
1034
  # predefined raise/rescue flow to end up as a <tt>400 Bad Request</tt> with no
@@ -1041,7 +1064,7 @@ module ActionController
1041
1064
  # end
1042
1065
  #
1043
1066
  # In order to use <tt>accepts_nested_attributes_for</tt> with Strong \Parameters, you
1044
- # will need to specify which nested attributes should be whitelisted. You might want
1067
+ # will need to specify which nested attributes should be permitted. You might want
1045
1068
  # to allow +:id+ and +:_destroy+, see ActiveRecord::NestedAttributes for more information.
1046
1069
  #
1047
1070
  # class Person
@@ -1059,7 +1082,7 @@ module ActionController
1059
1082
  # private
1060
1083
  #
1061
1084
  # def person_params
1062
- # # It's mandatory to specify the nested attributes that should be whitelisted.
1085
+ # # It's mandatory to specify the nested attributes that should be permitted.
1063
1086
  # # If you use `permit` with just the key that points to the nested attributes hash,
1064
1087
  # # it will return an empty hash.
1065
1088
  # params.require(:person).permit(:name, :age, pets_attributes: [ :id, :name, :category ])
@@ -44,7 +44,7 @@ module ActionController
44
44
  options[:original_script_name] = original_script_name
45
45
  else
46
46
  if same_origin
47
- options[:script_name] = request.script_name.empty? ? "".freeze : request.script_name.dup
47
+ options[:script_name] = request.script_name.empty? ? "" : request.script_name.dup
48
48
  else
49
49
  options[:script_name] = script_name
50
50
  end
@@ -26,10 +26,10 @@ module ActionController
26
26
  end
27
27
  end
28
28
 
29
- def build(action, app = nil, &block)
29
+ def build(action, app = Proc.new)
30
30
  action = action.to_s
31
31
 
32
- middlewares.reverse.inject(app || block) do |a, middleware|
32
+ middlewares.reverse.inject(app) do |a, middleware|
33
33
  middleware.valid?(action) ? middleware.build(a) : a
34
34
  end
35
35
  end
@@ -7,7 +7,7 @@ module ActionController
7
7
  super
8
8
  return unless klass.respond_to?(:helpers_path=)
9
9
 
10
- if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_helpers_paths) }
10
+ if namespace = klass.module_parents.detect { |m| m.respond_to?(:railtie_helpers_paths) }
11
11
  paths = namespace.railtie_helpers_paths
12
12
  else
13
13
  paths = ActionController::Helpers.helpers_path
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/hash/keys"
4
-
5
3
  module ActionController
6
4
  # ActionController::Renderer allows you to render arbitrary templates
7
5
  # without requirement of being in controller actions.
@@ -71,6 +69,21 @@ module ActionController
71
69
  end
72
70
 
73
71
  # Render templates with any options from ActionController::Base#render_to_string.
72
+ #
73
+ # The primary options are:
74
+ # * <tt>:partial</tt> - See <tt>ActionView::PartialRenderer</tt> for details.
75
+ # * <tt>:file</tt> - Renders an explicit template file. Add <tt>:locals</tt> to pass in, if so desired.
76
+ # It shouldn’t be used directly with unsanitized user input due to lack of validation.
77
+ # * <tt>:inline</tt> - Renders a ERB template string.
78
+ # * <tt>:plain</tt> - Renders provided text and sets the content type as <tt>text/plain</tt>.
79
+ # * <tt>:html</tt> - Renders the provided HTML safe string, otherwise
80
+ # performs HTML escape on the string first. Sets the content type as <tt>text/html</tt>.
81
+ # * <tt>:json</tt> - Renders the provided hash or object in JSON. You don't
82
+ # need to call <tt>.to_json</tt> on the object you want to render.
83
+ # * <tt>:body</tt> - Renders provided text and sets content type of <tt>text/plain</tt>.
84
+ #
85
+ # If no <tt>options</tt> hash is passed or if <tt>:update</tt> is specified, the default is
86
+ # to render a partial and use the second parameter as the locals hash.
74
87
  def render(*args)
75
88
  raise "missing controller" unless controller
76
89
 
@@ -26,7 +26,7 @@ module ActionController
26
26
  end
27
27
  end
28
28
 
29
- # ActionController::TestCase will be deprecated and moved to a gem in Rails 5.1.
29
+ # ActionController::TestCase will be deprecated and moved to a gem in the future.
30
30
  # Please use ActionDispatch::IntegrationTest going forward.
31
31
  class TestRequest < ActionDispatch::TestRequest #:nodoc:
32
32
  DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
@@ -177,12 +177,12 @@ module ActionController
177
177
 
178
178
  # Methods #destroy and #load! are overridden to avoid calling methods on the
179
179
  # @store object, which does not exist for the TestSession class.
180
- class TestSession < Rack::Session::Abstract::PersistedSecure::SecureSessionHash #:nodoc:
180
+ class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
181
181
  DEFAULT_OPTIONS = Rack::Session::Abstract::Persisted::DEFAULT_OPTIONS
182
182
 
183
183
  def initialize(session = {})
184
184
  super(nil, nil)
185
- @id = Rack::Session::SessionId.new(SecureRandom.hex(16))
185
+ @id = SecureRandom.hex(16)
186
186
  @data = stringify_keys(session)
187
187
  @loaded = true
188
188
  end
@@ -276,9 +276,6 @@ module ActionController
276
276
  # after calling +post+. If the various assert methods are not sufficient, then you
277
277
  # may use this object to inspect the HTTP response in detail.
278
278
  #
279
- # (Earlier versions of \Rails required each functional test to subclass
280
- # Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
281
- #
282
279
  # == Controller is automatically inferred
283
280
  #
284
281
  # ActionController::TestCase will automatically infer the controller under test
@@ -460,7 +457,6 @@ module ActionController
460
457
  def process(action, method: "GET", params: nil, session: nil, body: nil, flash: {}, format: nil, xhr: false, as: nil)
461
458
  check_required_ivars
462
459
 
463
- action = action.to_s.dup
464
460
  http_method = method.to_s.upcase
465
461
 
466
462
  @html_document = nil
@@ -492,11 +488,11 @@ module ActionController
492
488
  parameters[:format] = format
493
489
  end
494
490
 
495
- generated_extras = @routes.generate_extras(parameters.merge(controller: controller_class_name, action: action))
491
+ generated_extras = @routes.generate_extras(parameters.merge(controller: controller_class_name, action: action.to_s))
496
492
  generated_path = generated_path(generated_extras)
497
493
  query_string_keys = query_parameter_names(generated_extras)
498
494
 
499
- @request.assign_parameters(@routes, controller_class_name, action, parameters, generated_path, query_string_keys)
495
+ @request.assign_parameters(@routes, controller_class_name, action.to_s, parameters, generated_path, query_string_keys)
500
496
 
501
497
  @request.session.update(session) if session
502
498
  @request.flash.update(flash || {})
@@ -25,6 +25,7 @@ module ActionController
25
25
  autoload :ContentSecurityPolicy
26
26
  autoload :Cookies
27
27
  autoload :DataStreaming
28
+ autoload :DefaultHeaders
28
29
  autoload :EtagWithTemplateDigest
29
30
  autoload :EtagWithFlash
30
31
  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
@@ -17,6 +17,7 @@ module ActionDispatch #:nodoc:
17
17
  request = ActionDispatch::Request.new env
18
18
  _, headers, _ = response = @app.call(env)
19
19
 
20
+ return response unless html_response?(headers)
20
21
  return response if policy_present?(headers)
21
22
 
22
23
  if policy = request.content_security_policy
@@ -29,6 +30,13 @@ module ActionDispatch #:nodoc:
29
30
  end
30
31
 
31
32
  private
33
+
34
+ def html_response?(headers)
35
+ if content_type = headers[CONTENT_TYPE]
36
+ content_type =~ /html/
37
+ end
38
+ end
39
+
32
40
  def header_name(request)
33
41
  if request.content_security_policy_report_only
34
42
  POLICY_REPORT_ONLY
@@ -43,10 +51,10 @@ module ActionDispatch #:nodoc:
43
51
  end
44
52
 
45
53
  module Request
46
- POLICY = "action_dispatch.content_security_policy".freeze
47
- POLICY_REPORT_ONLY = "action_dispatch.content_security_policy_report_only".freeze
48
- NONCE_GENERATOR = "action_dispatch.content_security_policy_nonce_generator".freeze
49
- NONCE = "action_dispatch.content_security_policy_nonce".freeze
54
+ POLICY = "action_dispatch.content_security_policy"
55
+ POLICY_REPORT_ONLY = "action_dispatch.content_security_policy_report_only"
56
+ NONCE_GENERATOR = "action_dispatch.content_security_policy_nonce_generator"
57
+ NONCE = "action_dispatch.content_security_policy_nonce"
50
58
 
51
59
  def content_security_policy
52
60
  get_header(POLICY)
@@ -119,12 +127,13 @@ module ActionDispatch #:nodoc:
119
127
  manifest_src: "manifest-src",
120
128
  media_src: "media-src",
121
129
  object_src: "object-src",
130
+ prefetch_src: "prefetch-src",
122
131
  script_src: "script-src",
123
132
  style_src: "style-src",
124
133
  worker_src: "worker-src"
125
134
  }.freeze
126
135
 
127
- NONCE_DIRECTIVES = %w[script-src].freeze
136
+ NONCE_DIRECTIVES = %w[script-src style-src].freeze
128
137
 
129
138
  private_constant :MAPPINGS, :DIRECTIVES, :NONCE_DIRECTIVES
130
139
 
@@ -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
 
@@ -74,11 +79,6 @@ module ActionDispatch
74
79
  else
75
80
  [Mime[:html]]
76
81
  end
77
-
78
- v = v.select do |format|
79
- format.symbol || format.ref == "*/*"
80
- end
81
-
82
82
  set_header k, v
83
83
  end
84
84
  end
@@ -90,10 +90,7 @@ module ActionDispatch
90
90
  if variant.all? { |v| v.is_a?(Symbol) }
91
91
  @variant = ActiveSupport::ArrayInquirer.new(variant)
92
92
  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'"
93
+ raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols."
97
94
  end
98
95
  end
99
96
 
@@ -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
 
@@ -279,8 +277,6 @@ module Mime
279
277
 
280
278
  def all?; false; end
281
279
 
282
- # TODO Change this to private once we've dropped Ruby 2.2 support.
283
- # Workaround for Ruby 2.2 "private attribute?" warning.
284
280
  protected
285
281
 
286
282
  attr_reader :string, :synonyms