actionpack 5.2.4.5 → 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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -384
  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.rb +1 -0
  12. data/lib/action_controller/api.rb +2 -1
  13. data/lib/action_controller/base.rb +2 -7
  14. data/lib/action_controller/caching.rb +1 -1
  15. data/lib/action_controller/log_subscriber.rb +8 -5
  16. data/lib/action_controller/metal.rb +2 -2
  17. data/lib/action_controller/metal/conditional_get.rb +9 -3
  18. data/lib/action_controller/metal/data_streaming.rb +5 -6
  19. data/lib/action_controller/metal/default_headers.rb +17 -0
  20. data/lib/action_controller/metal/exceptions.rb +22 -1
  21. data/lib/action_controller/metal/flash.rb +5 -5
  22. data/lib/action_controller/metal/force_ssl.rb +17 -57
  23. data/lib/action_controller/metal/head.rb +1 -1
  24. data/lib/action_controller/metal/helpers.rb +1 -2
  25. data/lib/action_controller/metal/http_authentication.rb +20 -21
  26. data/lib/action_controller/metal/implicit_render.rb +2 -12
  27. data/lib/action_controller/metal/instrumentation.rb +3 -5
  28. data/lib/action_controller/metal/live.rb +28 -26
  29. data/lib/action_controller/metal/mime_responds.rb +13 -2
  30. data/lib/action_controller/metal/params_wrapper.rb +18 -14
  31. data/lib/action_controller/metal/redirecting.rb +32 -11
  32. data/lib/action_controller/metal/rendering.rb +1 -1
  33. data/lib/action_controller/metal/request_forgery_protection.rb +26 -40
  34. data/lib/action_controller/metal/strong_parameters.rb +57 -34
  35. data/lib/action_controller/metal/url_for.rb +1 -1
  36. data/lib/action_controller/railties/helpers.rb +1 -1
  37. data/lib/action_controller/renderer.rb +15 -2
  38. data/lib/action_controller/test_case.rb +3 -7
  39. data/lib/action_dispatch.rb +7 -6
  40. data/lib/action_dispatch/http/cache.rb +14 -10
  41. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  42. data/lib/action_dispatch/http/content_security_policy.rb +9 -8
  43. data/lib/action_dispatch/http/filter_parameters.rb +8 -6
  44. data/lib/action_dispatch/http/filter_redirect.rb +1 -1
  45. data/lib/action_dispatch/http/headers.rb +1 -1
  46. data/lib/action_dispatch/http/mime_negotiation.rb +7 -10
  47. data/lib/action_dispatch/http/mime_type.rb +1 -5
  48. data/lib/action_dispatch/http/parameter_filter.rb +5 -79
  49. data/lib/action_dispatch/http/parameters.rb +13 -3
  50. data/lib/action_dispatch/http/request.rb +10 -13
  51. data/lib/action_dispatch/http/response.rb +14 -14
  52. data/lib/action_dispatch/http/upload.rb +5 -0
  53. data/lib/action_dispatch/http/url.rb +81 -81
  54. data/lib/action_dispatch/journey/formatter.rb +1 -1
  55. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
  56. data/lib/action_dispatch/journey/nodes/node.rb +9 -8
  57. data/lib/action_dispatch/journey/path/pattern.rb +3 -4
  58. data/lib/action_dispatch/journey/router.rb +0 -3
  59. data/lib/action_dispatch/journey/router/utils.rb +10 -10
  60. data/lib/action_dispatch/journey/scanner.rb +11 -4
  61. data/lib/action_dispatch/journey/visitors.rb +1 -1
  62. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  63. data/lib/action_dispatch/middleware/cookies.rb +49 -70
  64. data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -58
  65. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  66. data/lib/action_dispatch/middleware/debug_view.rb +50 -0
  67. data/lib/action_dispatch/middleware/exception_wrapper.rb +36 -7
  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.rb +3 -2
  94. data/lib/action_dispatch/routing/inspector.rb +99 -50
  95. data/lib/action_dispatch/routing/mapper.rb +36 -29
  96. data/lib/action_dispatch/routing/polymorphic_routes.rb +3 -4
  97. data/lib/action_dispatch/routing/route_set.rb +11 -12
  98. data/lib/action_dispatch/routing/url_for.rb +1 -0
  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 -4
  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_pack.rb +1 -1
  106. data/lib/action_pack/gem_version.rb +4 -4
  107. metadata +22 -20
@@ -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
@@ -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
@@ -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 || {})
@@ -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,13 @@ 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
55
56
  autoload :DebugExceptions
56
57
  autoload :DebugLocks
58
+ autoload :DebugView
57
59
  autoload :ExceptionWrapper
58
60
  autoload :Executor
59
61
  autoload :Flash
@@ -83,11 +85,10 @@ module ActionDispatch
83
85
  end
84
86
 
85
87
  module Session
86
- autoload :AbstractStore, "action_dispatch/middleware/session/abstract_store"
87
- autoload :AbstractSecureStore, "action_dispatch/middleware/session/abstract_store"
88
- autoload :CookieStore, "action_dispatch/middleware/session/cookie_store"
89
- autoload :MemCacheStore, "action_dispatch/middleware/session/mem_cache_store"
90
- autoload :CacheStore, "action_dispatch/middleware/session/cache_store"
88
+ autoload :AbstractStore, "action_dispatch/middleware/session/abstract_store"
89
+ autoload :CookieStore, "action_dispatch/middleware/session/cookie_store"
90
+ autoload :MemCacheStore, "action_dispatch/middleware/session/mem_cache_store"
91
+ autoload :CacheStore, "action_dispatch/middleware/session/cache_store"
91
92
  end
92
93
 
93
94
  mattr_accessor :test_app
@@ -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
@@ -51,10 +51,10 @@ module ActionDispatch #:nodoc:
51
51
  end
52
52
 
53
53
  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
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"
58
58
 
59
59
  def content_security_policy
60
60
  get_header(POLICY)
@@ -127,12 +127,13 @@ module ActionDispatch #:nodoc:
127
127
  manifest_src: "manifest-src",
128
128
  media_src: "media-src",
129
129
  object_src: "object-src",
130
+ prefetch_src: "prefetch-src",
130
131
  script_src: "script-src",
131
132
  style_src: "style-src",
132
133
  worker_src: "worker-src"
133
134
  }.freeze
134
135
 
135
- NONCE_DIRECTIVES = %w[script-src].freeze
136
+ NONCE_DIRECTIVES = %w[script-src style-src].freeze
136
137
 
137
138
  private_constant :MAPPINGS, :DIRECTIVES, :NONCE_DIRECTIVES
138
139