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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +270 -347
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/abstract_controller/base.rb +4 -3
- data/lib/abstract_controller/caching/fragments.rb +6 -22
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +12 -0
- data/lib/abstract_controller/collector.rb +1 -2
- data/lib/abstract_controller/helpers.rb +7 -6
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
- data/lib/abstract_controller/translation.rb +4 -4
- data/lib/action_controller/api.rb +2 -1
- data/lib/action_controller/base.rb +2 -7
- data/lib/action_controller/caching.rb +1 -2
- data/lib/action_controller/log_subscriber.rb +8 -5
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +9 -3
- data/lib/action_controller/metal/content_security_policy.rb +0 -1
- data/lib/action_controller/metal/data_streaming.rb +5 -6
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +23 -2
- data/lib/action_controller/metal/flash.rb +5 -5
- data/lib/action_controller/metal/force_ssl.rb +15 -56
- data/lib/action_controller/metal/head.rb +1 -1
- data/lib/action_controller/metal/helpers.rb +3 -4
- data/lib/action_controller/metal/http_authentication.rb +20 -21
- data/lib/action_controller/metal/implicit_render.rb +4 -14
- data/lib/action_controller/metal/instrumentation.rb +3 -6
- data/lib/action_controller/metal/live.rb +29 -31
- data/lib/action_controller/metal/mime_responds.rb +13 -2
- data/lib/action_controller/metal/params_wrapper.rb +18 -14
- data/lib/action_controller/metal/redirecting.rb +5 -5
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +2 -3
- data/lib/action_controller/metal/request_forgery_protection.rb +25 -48
- data/lib/action_controller/metal/streaming.rb +0 -1
- data/lib/action_controller/metal/strong_parameters.rb +65 -44
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +8 -6
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +17 -3
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +7 -8
- data/lib/action_controller.rb +5 -1
- data/lib/action_dispatch/http/cache.rb +14 -11
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +28 -17
- data/lib/action_dispatch/http/filter_parameters.rb +8 -7
- data/lib/action_dispatch/http/filter_redirect.rb +1 -2
- data/lib/action_dispatch/http/headers.rb +1 -2
- data/lib/action_dispatch/http/mime_negotiation.rb +13 -6
- data/lib/action_dispatch/http/mime_type.rb +14 -8
- data/lib/action_dispatch/http/parameter_filter.rb +5 -79
- data/lib/action_dispatch/http/parameters.rb +15 -6
- data/lib/action_dispatch/http/request.rb +21 -14
- data/lib/action_dispatch/http/response.rb +40 -21
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +81 -82
- data/lib/action_dispatch/journey/formatter.rb +2 -3
- data/lib/action_dispatch/journey/gtg/builder.rb +0 -1
- data/lib/action_dispatch/journey/gtg/transition_table.rb +0 -1
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -1
- data/lib/action_dispatch/journey/nodes/node.rb +9 -8
- data/lib/action_dispatch/journey/path/pattern.rb +6 -3
- data/lib/action_dispatch/journey/route.rb +5 -4
- data/lib/action_dispatch/journey/router/utils.rb +10 -10
- data/lib/action_dispatch/journey/router.rb +0 -4
- data/lib/action_dispatch/journey/routes.rb +0 -2
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +62 -78
- data/lib/action_dispatch/middleware/debug_exceptions.rb +45 -61
- data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +49 -16
- data/lib/action_dispatch/middleware/flash.rb +1 -1
- data/lib/action_dispatch/middleware/host_authorization.rb +121 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +9 -12
- data/lib/action_dispatch/middleware/request_id.rb +2 -2
- data/lib/action_dispatch/middleware/session/abstract_store.rb +0 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -7
- data/lib/action_dispatch/middleware/show_exceptions.rb +1 -2
- data/lib/action_dispatch/middleware/ssl.rb +8 -8
- data/lib/action_dispatch/middleware/stack.rb +38 -2
- data/lib/action_dispatch/middleware/static.rb +6 -7
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +7 -4
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +5 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -0
- data/lib/action_dispatch/railtie.rb +7 -2
- data/lib/action_dispatch/request/session.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +97 -50
- data/lib/action_dispatch/routing/mapper.rb +63 -42
- data/lib/action_dispatch/routing/polymorphic_routes.rb +3 -6
- data/lib/action_dispatch/routing/route_set.rb +25 -31
- data/lib/action_dispatch/routing/url_for.rb +2 -2
- data/lib/action_dispatch/routing.rb +21 -20
- data/lib/action_dispatch/system_test_case.rb +44 -6
- data/lib/action_dispatch/system_testing/browser.rb +38 -7
- data/lib/action_dispatch/system_testing/driver.rb +11 -2
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +6 -5
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +7 -6
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions/response.rb +2 -3
- data/lib/action_dispatch/testing/assertions/routing.rb +15 -3
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +33 -12
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +2 -2
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_dispatch.rb +7 -2
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +29 -15
- 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
|
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
|
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
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
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
|
-
|
666
|
-
|
667
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
|
915
|
-
|
916
|
-
|
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
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
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
|
-
#
|
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
|
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
|
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? ? ""
|
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
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
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.
|
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
|
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
|
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/ }
|
data/lib/action_controller.rb
CHANGED
@@ -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"
|
8
|
-
HTTP_IF_NONE_MATCH = "HTTP_IF_NONE_MATCH"
|
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
|
-
|
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"
|
170
|
-
NO_CACHE = "no-cache"
|
171
|
-
PUBLIC = "public"
|
172
|
-
PRIVATE = "private"
|
173
|
-
MUST_REVALIDATE = "must-revalidate"
|
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
|
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"
|
9
|
-
POLICY = "Content-Security-Policy"
|
10
|
-
POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only"
|
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"
|
47
|
-
POLICY_REPORT_ONLY = "action_dispatch.content_security_policy_report_only"
|
48
|
-
NONCE_GENERATOR = "action_dispatch.content_security_policy_nonce_generator"
|
49
|
-
NONCE = "action_dispatch.content_security_policy_nonce"
|
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
|
-
|
137
|
+
DEFAULT_NONCE_DIRECTIVES = %w[script-src style-src].freeze
|
128
138
|
|
129
|
-
private_constant :MAPPINGS, :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
|
-
|
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
|
-
|
271
|
+
def nonce_directive?(directive, nonce_directives)
|
272
|
+
nonce_directives.include?(directive)
|
262
273
|
end
|
263
274
|
end
|
264
275
|
end
|