actionpack 5.2.8.1 → 6.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +270 -347
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/abstract_controller/base.rb +4 -3
  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 +12 -0
  9. data/lib/abstract_controller/collector.rb +1 -2
  10. data/lib/abstract_controller/helpers.rb +7 -6
  11. data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
  12. data/lib/abstract_controller/translation.rb +4 -4
  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 -2
  16. data/lib/action_controller/log_subscriber.rb +8 -5
  17. data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
  18. data/lib/action_controller/metal/conditional_get.rb +9 -3
  19. data/lib/action_controller/metal/content_security_policy.rb +0 -1
  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 +23 -2
  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 -6
  31. data/lib/action_controller/metal/live.rb +29 -31
  32. data/lib/action_controller/metal/mime_responds.rb +13 -2
  33. data/lib/action_controller/metal/params_wrapper.rb +18 -14
  34. data/lib/action_controller/metal/redirecting.rb +5 -5
  35. data/lib/action_controller/metal/renderers.rb +4 -4
  36. data/lib/action_controller/metal/rendering.rb +2 -3
  37. data/lib/action_controller/metal/request_forgery_protection.rb +25 -48
  38. data/lib/action_controller/metal/streaming.rb +0 -1
  39. data/lib/action_controller/metal/strong_parameters.rb +65 -44
  40. data/lib/action_controller/metal/url_for.rb +1 -1
  41. data/lib/action_controller/metal.rb +8 -6
  42. data/lib/action_controller/railties/helpers.rb +1 -1
  43. data/lib/action_controller/renderer.rb +17 -3
  44. data/lib/action_controller/template_assertions.rb +1 -1
  45. data/lib/action_controller/test_case.rb +7 -8
  46. data/lib/action_controller.rb +5 -1
  47. data/lib/action_dispatch/http/cache.rb +14 -11
  48. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  49. data/lib/action_dispatch/http/content_security_policy.rb +28 -17
  50. data/lib/action_dispatch/http/filter_parameters.rb +8 -7
  51. data/lib/action_dispatch/http/filter_redirect.rb +1 -2
  52. data/lib/action_dispatch/http/headers.rb +1 -2
  53. data/lib/action_dispatch/http/mime_negotiation.rb +13 -6
  54. data/lib/action_dispatch/http/mime_type.rb +14 -8
  55. data/lib/action_dispatch/http/parameter_filter.rb +5 -79
  56. data/lib/action_dispatch/http/parameters.rb +15 -6
  57. data/lib/action_dispatch/http/request.rb +21 -14
  58. data/lib/action_dispatch/http/response.rb +40 -21
  59. data/lib/action_dispatch/http/upload.rb +9 -1
  60. data/lib/action_dispatch/http/url.rb +81 -82
  61. data/lib/action_dispatch/journey/formatter.rb +2 -3
  62. data/lib/action_dispatch/journey/gtg/builder.rb +0 -1
  63. data/lib/action_dispatch/journey/gtg/transition_table.rb +0 -1
  64. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
  65. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -1
  66. data/lib/action_dispatch/journey/nodes/node.rb +9 -8
  67. data/lib/action_dispatch/journey/path/pattern.rb +6 -3
  68. data/lib/action_dispatch/journey/route.rb +5 -4
  69. data/lib/action_dispatch/journey/router/utils.rb +10 -10
  70. data/lib/action_dispatch/journey/router.rb +0 -4
  71. data/lib/action_dispatch/journey/routes.rb +0 -2
  72. data/lib/action_dispatch/journey/scanner.rb +10 -4
  73. data/lib/action_dispatch/journey/visitors.rb +1 -4
  74. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  75. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  76. data/lib/action_dispatch/middleware/cookies.rb +62 -78
  77. data/lib/action_dispatch/middleware/debug_exceptions.rb +45 -61
  78. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  79. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  80. data/lib/action_dispatch/middleware/exception_wrapper.rb +49 -16
  81. data/lib/action_dispatch/middleware/flash.rb +1 -1
  82. data/lib/action_dispatch/middleware/host_authorization.rb +121 -0
  83. data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
  84. data/lib/action_dispatch/middleware/remote_ip.rb +9 -12
  85. data/lib/action_dispatch/middleware/request_id.rb +2 -2
  86. data/lib/action_dispatch/middleware/session/abstract_store.rb +0 -1
  87. data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -7
  88. data/lib/action_dispatch/middleware/show_exceptions.rb +1 -2
  89. data/lib/action_dispatch/middleware/ssl.rb +8 -8
  90. data/lib/action_dispatch/middleware/stack.rb +38 -2
  91. data/lib/action_dispatch/middleware/static.rb +6 -7
  92. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  93. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  94. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
  95. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  96. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  97. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  98. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  99. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -4
  101. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  102. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +7 -4
  103. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +5 -2
  104. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -0
  105. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  106. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  107. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  108. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  109. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
  110. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -0
  111. data/lib/action_dispatch/railtie.rb +7 -2
  112. data/lib/action_dispatch/request/session.rb +9 -2
  113. data/lib/action_dispatch/routing/inspector.rb +97 -50
  114. data/lib/action_dispatch/routing/mapper.rb +63 -42
  115. data/lib/action_dispatch/routing/polymorphic_routes.rb +3 -6
  116. data/lib/action_dispatch/routing/route_set.rb +25 -31
  117. data/lib/action_dispatch/routing/url_for.rb +2 -2
  118. data/lib/action_dispatch/routing.rb +21 -20
  119. data/lib/action_dispatch/system_test_case.rb +44 -6
  120. data/lib/action_dispatch/system_testing/browser.rb +38 -7
  121. data/lib/action_dispatch/system_testing/driver.rb +11 -2
  122. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +6 -5
  123. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +7 -6
  124. data/lib/action_dispatch/testing/assertion_response.rb +0 -1
  125. data/lib/action_dispatch/testing/assertions/response.rb +2 -3
  126. data/lib/action_dispatch/testing/assertions/routing.rb +15 -3
  127. data/lib/action_dispatch/testing/assertions.rb +1 -1
  128. data/lib/action_dispatch/testing/integration.rb +33 -12
  129. data/lib/action_dispatch/testing/request_encoder.rb +2 -2
  130. data/lib/action_dispatch/testing/test_process.rb +2 -2
  131. data/lib/action_dispatch/testing/test_response.rb +4 -32
  132. data/lib/action_dispatch.rb +7 -2
  133. data/lib/action_pack/gem_version.rb +4 -4
  134. data/lib/action_pack.rb +1 -1
  135. metadata +29 -15
  136. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -1,11 +1,9 @@
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"
8
- require "active_support/rescuable"
9
7
  require "action_dispatch/http/upload"
10
8
  require "rack/test"
11
9
  require "stringio"
@@ -59,7 +57,7 @@ module ActionController
59
57
 
60
58
  # == Action Controller \Parameters
61
59
  #
62
- # Allows you to choose which attributes should be whitelisted for mass updating
60
+ # Allows you to choose which attributes should be permitted for mass updating
63
61
  # and thus prevent accidentally exposing that which shouldn't be exposed.
64
62
  # Provides two methods for this purpose: #require and #permit. The former is
65
63
  # used to mark parameters as required. The latter is used to set the parameter
@@ -133,6 +131,15 @@ module ActionController
133
131
  #
134
132
  # Returns a hash that can be used as the JSON representation for the parameters.
135
133
 
134
+ ##
135
+ # :method: each_key
136
+ #
137
+ # :call-seq:
138
+ # each_key()
139
+ #
140
+ # Calls block once for each key in the parameters, passing the key.
141
+ # If no block is given, an enumerator is returned instead.
142
+
136
143
  ##
137
144
  # :method: empty?
138
145
  #
@@ -205,7 +212,7 @@ module ActionController
205
212
  #
206
213
  # Returns a new array of the values of the parameters.
207
214
  delegate :keys, :key?, :has_key?, :values, :has_value?, :value?, :empty?, :include?,
208
- :as_json, :to_s, to: :@parameters
215
+ :as_json, :to_s, :each_key, to: :@parameters
209
216
 
210
217
  # By default, never raise an UnpermittedParameters exception if these
211
218
  # params are present. The default includes both 'controller' and 'action'
@@ -342,6 +349,16 @@ module ActionController
342
349
  end
343
350
  alias_method :each, :each_pair
344
351
 
352
+ # Convert all hashes in values into parameters, then yield each value in
353
+ # the same way as <tt>Hash#each_value</tt>.
354
+ def each_value(&block)
355
+ @parameters.each_pair do |key, value|
356
+ yield convert_hashes_to_parameters(key, value)
357
+ end
358
+
359
+ self
360
+ end
361
+
345
362
  # Attribute that keeps track of converted arrays, if any, to avoid double
346
363
  # looping in the common use case permit + mass-assignment. Defined in a
347
364
  # method to instantiate it only if needed.
@@ -508,7 +525,7 @@ module ActionController
508
525
  #
509
526
  # Note that if you use +permit+ in a key that points to a hash,
510
527
  # it won't allow all the hash. You also need to specify which
511
- # attributes inside the hash should be whitelisted.
528
+ # attributes inside the hash should be permitted.
512
529
  #
513
530
  # params = ActionController::Parameters.new({
514
531
  # person: {
@@ -585,20 +602,18 @@ module ActionController
585
602
  )
586
603
  end
587
604
 
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
605
+ # Extracts the nested parameter from the given +keys+ by calling +dig+
606
+ # at each step. Returns +nil+ if any intermediate step is +nil+.
607
+ #
608
+ # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } })
609
+ # params.dig(:foo, :bar, :baz) # => 1
610
+ # params.dig(:foo, :zot, :xyz) # => nil
611
+ #
612
+ # params2 = ActionController::Parameters.new(foo: [10, 11, 12])
613
+ # params2.dig(:foo, 1) # => 11
614
+ def dig(*keys)
615
+ convert_hashes_to_parameters(keys.first, @parameters[keys.first])
616
+ @parameters.dig(*keys)
602
617
  end
603
618
 
604
619
  # Returns a new <tt>ActionController::Parameters</tt> instance that
@@ -662,18 +677,16 @@ module ActionController
662
677
  # Returns a new <tt>ActionController::Parameters</tt> instance with the
663
678
  # results of running +block+ once for every key. The values are unchanged.
664
679
  def transform_keys(&block)
665
- if block
666
- new_instance_with_inherited_permitted_status(
667
- @parameters.transform_keys(&block)
668
- )
669
- else
670
- @parameters.transform_keys
671
- end
680
+ return to_enum(:transform_keys) unless block_given?
681
+ new_instance_with_inherited_permitted_status(
682
+ @parameters.transform_keys(&block)
683
+ )
672
684
  end
673
685
 
674
686
  # Performs keys transformation and returns the altered
675
687
  # <tt>ActionController::Parameters</tt> instance.
676
688
  def transform_keys!(&block)
689
+ return to_enum(:transform_keys!) unless block_given?
677
690
  @parameters.transform_keys!(&block)
678
691
  self
679
692
  end
@@ -783,7 +796,7 @@ module ActionController
783
796
  @permitted = coder.map["ivars"][:@permitted]
784
797
  when "!ruby/object:ActionController::Parameters"
785
798
  # YAML's Object format. Only needed because of the format
786
- # backwardscompability above, otherwise equivalent to YAML's initialization.
799
+ # backwards compatibility above, otherwise equivalent to YAML's initialization.
787
800
  @parameters, @permitted = coder.map["parameters"], coder.map["permitted"]
788
801
  end
789
802
  end
@@ -798,9 +811,7 @@ module ActionController
798
811
  protected
799
812
  attr_reader :parameters
800
813
 
801
- def permitted=(new_permitted)
802
- @permitted = new_permitted
803
- end
814
+ attr_writer :permitted
804
815
 
805
816
  def fields_for_style?
806
817
  @parameters.all? { |k, v| k =~ /\A-?\d+\z/ && (v.is_a?(Hash) || v.is_a?(Parameters)) }
@@ -911,15 +922,28 @@ module ActionController
911
922
  PERMITTED_SCALAR_TYPES.any? { |type| value.is_a?(type) }
912
923
  end
913
924
 
914
- def permitted_scalar_filter(params, key)
915
- if has_key?(key) && permitted_scalar?(self[key])
916
- params[key] = self[key]
925
+ # Adds existing keys to the params if their values are scalar.
926
+ #
927
+ # For example:
928
+ #
929
+ # puts self.keys #=> ["zipcode(90210i)"]
930
+ # params = {}
931
+ #
932
+ # permitted_scalar_filter(params, "zipcode")
933
+ #
934
+ # puts params.keys # => ["zipcode"]
935
+ def permitted_scalar_filter(params, permitted_key)
936
+ permitted_key = permitted_key.to_s
937
+
938
+ if has_key?(permitted_key) && permitted_scalar?(self[permitted_key])
939
+ params[permitted_key] = self[permitted_key]
917
940
  end
918
941
 
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
942
+ each_key do |key|
943
+ next unless key =~ /\(\d+[if]?\)\z/
944
+ next unless $~.pre_match == permitted_key
945
+
946
+ params[key] = self[key] if permitted_scalar?(self[key])
923
947
  end
924
948
  end
925
949
 
@@ -1004,8 +1028,8 @@ module ActionController
1004
1028
  #
1005
1029
  # It provides an interface for protecting attributes from end-user
1006
1030
  # assignment. This makes Action Controller parameters forbidden
1007
- # to be used in Active Model mass assignment until they have been
1008
- # whitelisted.
1031
+ # to be used in Active Model mass assignment until they have been explicitly
1032
+ # enumerated.
1009
1033
  #
1010
1034
  # In addition, parameters can be marked as required and flow through a
1011
1035
  # predefined raise/rescue flow to end up as a <tt>400 Bad Request</tt> with no
@@ -1041,7 +1065,7 @@ module ActionController
1041
1065
  # end
1042
1066
  #
1043
1067
  # 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
1068
+ # will need to specify which nested attributes should be permitted. You might want
1045
1069
  # to allow +:id+ and +:_destroy+, see ActiveRecord::NestedAttributes for more information.
1046
1070
  #
1047
1071
  # class Person
@@ -1059,7 +1083,7 @@ module ActionController
1059
1083
  # private
1060
1084
  #
1061
1085
  # def person_params
1062
- # # It's mandatory to specify the nested attributes that should be whitelisted.
1086
+ # # It's mandatory to specify the nested attributes that should be permitted.
1063
1087
  # # If you use `permit` with just the key that points to the nested attributes hash,
1064
1088
  # # it will return an empty hash.
1065
1089
  # params.require(:person).permit(:name, :age, pets_attributes: [ :id, :name, :category ])
@@ -1069,9 +1093,6 @@ module ActionController
1069
1093
  # See ActionController::Parameters.require and ActionController::Parameters.permit
1070
1094
  # for more information.
1071
1095
  module StrongParameters
1072
- extend ActiveSupport::Concern
1073
- include ActiveSupport::Rescuable
1074
-
1075
1096
  # Returns a new ActionController::Parameters object that
1076
1097
  # has been instantiated with the <tt>request.parameters</tt>.
1077
1098
  def params
@@ -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
@@ -35,7 +35,6 @@ module ActionController
35
35
  end
36
36
 
37
37
  private
38
-
39
38
  INCLUDE = ->(list, action) { list.include? action }
40
39
  EXCLUDE = ->(list, action) { !list.include? action }
41
40
  NULL = ->(list, action) { true }
@@ -148,7 +147,7 @@ module ActionController
148
147
  attr_internal :response, :request
149
148
  delegate :session, to: "@_request"
150
149
  delegate :headers, :status=, :location=, :content_type=,
151
- :status, :location, :content_type, to: "@_response"
150
+ :status, :location, :content_type, :media_type, to: "@_response"
152
151
 
153
152
  def initialize
154
153
  @_request = nil
@@ -217,10 +216,13 @@ module ActionController
217
216
  super
218
217
  end
219
218
 
220
- # Pushes the given Rack middleware and its arguments to the bottom of the
221
- # middleware stack.
222
- def self.use(*args, &block)
223
- middleware_stack.use(*args, &block)
219
+ class << self
220
+ # Pushes the given Rack middleware and its arguments to the bottom of the
221
+ # middleware stack.
222
+ def use(*args, &block)
223
+ middleware_stack.use(*args, &block)
224
+ end
225
+ ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
224
226
  end
225
227
 
226
228
  # Alias for +middleware_stack+.
@@ -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 an 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
 
@@ -82,6 +95,7 @@ module ActionController
82
95
  instance.set_response! controller.make_response!(request)
83
96
  instance.render_to_string(*args)
84
97
  end
98
+ alias_method :render_to_string, :render # :nodoc:
85
99
 
86
100
  private
87
101
  def normalize_keys(env)
@@ -103,7 +117,7 @@ module ActionController
103
117
 
104
118
  RACK_VALUE_TRANSLATION = {
105
119
  https: ->(v) { v ? "on" : "off" },
106
- method: ->(v) { v.upcase },
120
+ method: ->(v) { -v.upcase },
107
121
  }
108
122
 
109
123
  def rack_key_for(key)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionController
4
- module TemplateAssertions
4
+ module TemplateAssertions # :nodoc:
5
5
  def assert_template(options = {}, message = nil)
6
6
  raise NoMethodError,
7
7
  "assert_template has been extracted to a gem. To continue using it,
@@ -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
@@ -158,7 +158,6 @@ module ActionController
158
158
  end.new
159
159
 
160
160
  private
161
-
162
161
  def params_parsers
163
162
  super.merge @custom_param_parsers
164
163
  end
@@ -203,12 +202,16 @@ module ActionController
203
202
  clear
204
203
  end
205
204
 
205
+ def dig(*keys)
206
+ keys = keys.map.with_index { |key, i| i.zero? ? key.to_s : key }
207
+ @data.dig(*keys)
208
+ end
209
+
206
210
  def fetch(key, *args, &block)
207
211
  @data.fetch(key.to_s, *args, &block)
208
212
  end
209
213
 
210
214
  private
211
-
212
215
  def load!
213
216
  @id
214
217
  end
@@ -276,9 +279,6 @@ module ActionController
276
279
  # after calling +post+. If the various assert methods are not sufficient, then you
277
280
  # may use this object to inspect the HTTP response in detail.
278
281
  #
279
- # (Earlier versions of \Rails required each functional test to subclass
280
- # Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
281
- #
282
282
  # == Controller is automatically inferred
283
283
  #
284
284
  # ActionController::TestCase will automatically infer the controller under test
@@ -460,7 +460,7 @@ module ActionController
460
460
  def process(action, method: "GET", params: nil, session: nil, body: nil, flash: {}, format: nil, xhr: false, as: nil)
461
461
  check_required_ivars
462
462
 
463
- action = action.to_s.dup
463
+ action = +action.to_s
464
464
  http_method = method.to_s.upcase
465
465
 
466
466
  @html_document = nil
@@ -598,7 +598,6 @@ module ActionController
598
598
  end
599
599
 
600
600
  private
601
-
602
601
  def scrub_env!(env)
603
602
  env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
604
603
  env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
@@ -3,7 +3,6 @@
3
3
  require "active_support/rails"
4
4
  require "abstract_controller"
5
5
  require "action_dispatch"
6
- require "action_controller/metal/live"
7
6
  require "action_controller/metal/strong_parameters"
8
7
 
9
8
  module ActionController
@@ -21,10 +20,15 @@ module ActionController
21
20
  end
22
21
 
23
22
  autoload_under "metal" do
23
+ eager_autoload do
24
+ autoload :Live
25
+ end
26
+
24
27
  autoload :ConditionalGet
25
28
  autoload :ContentSecurityPolicy
26
29
  autoload :Cookies
27
30
  autoload :DataStreaming
31
+ autoload :DefaultHeaders
28
32
  autoload :EtagWithTemplateDigest
29
33
  autoload :EtagWithFlash
30
34
  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)
@@ -123,9 +123,8 @@ module ActionDispatch
123
123
  end
124
124
 
125
125
  private
126
-
127
- DATE = "Date".freeze
128
- LAST_MODIFIED = "Last-Modified".freeze
126
+ DATE = "Date"
127
+ LAST_MODIFIED = "Last-Modified"
129
128
  SPECIAL_KEYS = Set.new(%w[extras no-cache max-age public private must-revalidate])
130
129
 
131
130
  def generate_weak_etag(validators)
@@ -166,11 +165,11 @@ module ActionDispatch
166
165
  @cache_control = cache_control_headers
167
166
  end
168
167
 
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
168
+ DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
169
+ NO_CACHE = "no-cache"
170
+ PUBLIC = "public"
171
+ PRIVATE = "private"
172
+ MUST_REVALIDATE = "must-revalidate"
174
173
 
175
174
  def handle_conditional_get!
176
175
  # Normally default cache control setting is handled by ETag
@@ -204,13 +203,17 @@ module ActionDispatch
204
203
 
205
204
  self._cache_control = options.join(", ")
206
205
  else
207
- extras = control[:extras]
206
+ extras = control[:extras]
208
207
  max_age = control[:max_age]
208
+ stale_while_revalidate = control[:stale_while_revalidate]
209
+ stale_if_error = control[:stale_if_error]
209
210
 
210
211
  options = []
211
212
  options << "max-age=#{max_age.to_i}" if max_age
212
213
  options << (control[:public] ? PUBLIC : PRIVATE)
213
214
  options << MUST_REVALIDATE if control[:must_revalidate]
215
+ options << "stale-while-revalidate=#{stale_while_revalidate.to_i}" if stale_while_revalidate
216
+ options << "stale-if-error=#{stale_if_error.to_i}" if stale_if_error
214
217
  options.concat(extras) if extras
215
218
 
216
219
  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
@@ -21,8 +21,9 @@ module ActionDispatch #:nodoc:
21
21
 
22
22
  if policy = request.content_security_policy
23
23
  nonce = request.content_security_policy_nonce
24
+ nonce_directives = request.content_security_policy_nonce_directives
24
25
  context = request.controller_instance || request
25
- headers[header_name(request)] = policy.build(context, nonce)
26
+ headers[header_name(request)] = policy.build(context, nonce, nonce_directives)
26
27
  end
27
28
 
28
29
  response
@@ -43,10 +44,11 @@ module ActionDispatch #:nodoc:
43
44
  end
44
45
 
45
46
  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
47
+ POLICY = "action_dispatch.content_security_policy"
48
+ POLICY_REPORT_ONLY = "action_dispatch.content_security_policy_report_only"
49
+ NONCE_GENERATOR = "action_dispatch.content_security_policy_nonce_generator"
50
+ NONCE = "action_dispatch.content_security_policy_nonce"
51
+ NONCE_DIRECTIVES = "action_dispatch.content_security_policy_nonce_directives"
50
52
 
51
53
  def content_security_policy
52
54
  get_header(POLICY)
@@ -72,6 +74,14 @@ module ActionDispatch #:nodoc:
72
74
  set_header(NONCE_GENERATOR, generator)
73
75
  end
74
76
 
77
+ def content_security_policy_nonce_directives
78
+ get_header(NONCE_DIRECTIVES)
79
+ end
80
+
81
+ def content_security_policy_nonce_directives=(generator)
82
+ set_header(NONCE_DIRECTIVES, generator)
83
+ end
84
+
75
85
  def content_security_policy_nonce
76
86
  if content_security_policy_nonce_generator
77
87
  if nonce = get_header(NONCE)
@@ -83,7 +93,6 @@ module ActionDispatch #:nodoc:
83
93
  end
84
94
 
85
95
  private
86
-
87
96
  def generate_content_security_policy_nonce
88
97
  content_security_policy_nonce_generator.call(self)
89
98
  end
@@ -119,14 +128,15 @@ module ActionDispatch #:nodoc:
119
128
  manifest_src: "manifest-src",
120
129
  media_src: "media-src",
121
130
  object_src: "object-src",
131
+ prefetch_src: "prefetch-src",
122
132
  script_src: "script-src",
123
133
  style_src: "style-src",
124
134
  worker_src: "worker-src"
125
135
  }.freeze
126
136
 
127
- NONCE_DIRECTIVES = %w[script-src].freeze
137
+ DEFAULT_NONCE_DIRECTIVES = %w[script-src style-src].freeze
128
138
 
129
- private_constant :MAPPINGS, :DIRECTIVES, :NONCE_DIRECTIVES
139
+ private_constant :MAPPINGS, :DIRECTIVES, :DEFAULT_NONCE_DIRECTIVES
130
140
 
131
141
  attr_reader :directives
132
142
 
@@ -195,8 +205,9 @@ module ActionDispatch #:nodoc:
195
205
  end
196
206
  end
197
207
 
198
- def build(context = nil, nonce = nil)
199
- build_directives(context, nonce).compact.join("; ")
208
+ def build(context = nil, nonce = nil, nonce_directives = nil)
209
+ nonce_directives = DEFAULT_NONCE_DIRECTIVES if nonce_directives.nil?
210
+ build_directives(context, nonce, nonce_directives).compact.join("; ")
200
211
  end
201
212
 
202
213
  private
@@ -219,10 +230,10 @@ module ActionDispatch #:nodoc:
219
230
  end
220
231
  end
221
232
 
222
- def build_directives(context, nonce)
233
+ def build_directives(context, nonce, nonce_directives)
223
234
  @directives.map do |directive, sources|
224
235
  if sources.is_a?(Array)
225
- if nonce && nonce_directive?(directive)
236
+ if nonce && nonce_directive?(directive, nonce_directives)
226
237
  "#{directive} #{build_directive(sources, context).join(' ')} 'nonce-#{nonce}'"
227
238
  else
228
239
  "#{directive} #{build_directive(sources, context).join(' ')}"
@@ -257,8 +268,8 @@ module ActionDispatch #:nodoc:
257
268
  end
258
269
  end
259
270
 
260
- def nonce_directive?(directive)
261
- NONCE_DIRECTIVES.include?(directive)
271
+ def nonce_directive?(directive, nonce_directives)
272
+ nonce_directives.include?(directive)
262
273
  end
263
274
  end
264
275
  end