actionpack 5.2.7.1 → 6.1.7.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +427 -338
- data/MIT-LICENSE +1 -2
- data/README.rdoc +4 -3
- data/lib/abstract_controller/base.rb +38 -4
- data/lib/abstract_controller/caching/fragments.rb +6 -22
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +14 -2
- data/lib/abstract_controller/collector.rb +5 -4
- data/lib/abstract_controller/helpers.rb +106 -90
- data/lib/abstract_controller/railties/routes_helpers.rb +17 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +11 -5
- data/lib/abstract_controller.rb +1 -0
- data/lib/action_controller/api.rb +4 -3
- data/lib/action_controller/base.rb +6 -9
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/log_subscriber.rb +10 -7
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +19 -5
- data/lib/action_controller/metal/content_security_policy.rb +1 -2
- data/lib/action_controller/metal/cookies.rb +3 -1
- data/lib/action_controller/metal/data_streaming.rb +6 -7
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +4 -6
- data/lib/action_controller/metal/exceptions.rb +56 -2
- data/lib/action_controller/metal/flash.rb +5 -5
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +14 -5
- data/lib/action_controller/metal/http_authentication.rb +25 -23
- data/lib/action_controller/metal/implicit_render.rb +5 -15
- data/lib/action_controller/metal/instrumentation.rb +13 -14
- data/lib/action_controller/metal/live.rb +39 -32
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +19 -4
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +32 -22
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +26 -7
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +8 -3
- data/lib/action_controller/metal/request_forgery_protection.rb +26 -49
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/streaming.rb +0 -1
- data/lib/action_controller/metal/strong_parameters.rb +168 -59
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +10 -8
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +37 -13
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +71 -63
- data/lib/action_controller.rb +7 -4
- data/lib/action_dispatch/http/cache.rb +32 -28
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +34 -18
- data/lib/action_dispatch/http/filter_parameters.rb +9 -8
- data/lib/action_dispatch/http/filter_redirect.rb +2 -3
- data/lib/action_dispatch/http/headers.rb +4 -4
- data/lib/action_dispatch/http/mime_negotiation.rb +26 -13
- data/lib/action_dispatch/http/mime_type.rb +43 -24
- data/lib/action_dispatch/http/parameters.rb +14 -23
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +45 -22
- data/lib/action_dispatch/http/response.rb +45 -25
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +82 -82
- data/lib/action_dispatch/journey/formatter.rb +55 -31
- data/lib/action_dispatch/journey/gtg/builder.rb +22 -37
- data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -5
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +13 -11
- data/lib/action_dispatch/journey/parser.rb +13 -13
- data/lib/action_dispatch/journey/parser.y +1 -1
- data/lib/action_dispatch/journey/path/pattern.rb +19 -21
- data/lib/action_dispatch/journey/route.rb +10 -20
- data/lib/action_dispatch/journey/router/utils.rb +14 -12
- data/lib/action_dispatch/journey/router.rb +26 -34
- 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/journey.rb +0 -2
- 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 +150 -123
- data/lib/action_dispatch/middleware/debug_exceptions.rb +43 -66
- 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 +75 -30
- data/lib/action_dispatch/middleware/flash.rb +1 -1
- data/lib/action_dispatch/middleware/host_authorization.rb +170 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +14 -16
- data/lib/action_dispatch/middleware/request_id.rb +5 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -3
- data/lib/action_dispatch/middleware/session/cookie_store.rb +3 -9
- data/lib/action_dispatch/middleware/show_exceptions.rb +13 -2
- data/lib/action_dispatch/middleware/ssl.rb +20 -15
- data/lib/action_dispatch/middleware/stack.rb +56 -2
- data/lib/action_dispatch/middleware/static.rb +153 -93
- 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/_message_and_suggestions.html.erb +22 -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 +23 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +6 -3
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +104 -8
- 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/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +24 -1
- data/lib/action_dispatch/railtie.rb +8 -2
- data/lib/action_dispatch/request/session.rb +11 -10
- data/lib/action_dispatch/request/utils.rb +26 -2
- data/lib/action_dispatch/routing/inspector.rb +100 -52
- data/lib/action_dispatch/routing/mapper.rb +155 -103
- data/lib/action_dispatch/routing/polymorphic_routes.rb +13 -15
- data/lib/action_dispatch/routing/redirection.rb +4 -4
- data/lib/action_dispatch/routing/route_set.rb +71 -69
- 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 +60 -11
- data/lib/action_dispatch/system_testing/browser.rb +53 -16
- data/lib/action_dispatch/system_testing/driver.rb +11 -3
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +49 -7
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +8 -10
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions/response.rb +4 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +60 -28
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +32 -4
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_dispatch.rb +9 -3
- data/lib/action_pack/gem_version.rb +3 -3
- data/lib/action_pack.rb +1 -1
- metadata +36 -23
- data/lib/action_controller/metal/force_ssl.rb +0 -99
- data/lib/action_dispatch/http/parameter_filter.rb +0 -86
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -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.
|
@@ -67,10 +65,29 @@ module ActionController
|
|
67
65
|
def initialize(controller, env, defaults)
|
68
66
|
@controller = controller
|
69
67
|
@defaults = defaults
|
70
|
-
@env = normalize_keys defaults
|
68
|
+
@env = normalize_keys defaults, env
|
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, then:
|
86
|
+
#
|
87
|
+
# If an object responding to +render_in+ is passed, +render_in+ is called on the object,
|
88
|
+
# passing in the current view context.
|
89
|
+
#
|
90
|
+
# Otherwise, a partial is rendered using the second parameter as the locals hash.
|
74
91
|
def render(*args)
|
75
92
|
raise "missing controller" unless controller
|
76
93
|
|
@@ -82,11 +99,18 @@ module ActionController
|
|
82
99
|
instance.set_response! controller.make_response!(request)
|
83
100
|
instance.render_to_string(*args)
|
84
101
|
end
|
102
|
+
alias_method :render_to_string, :render # :nodoc:
|
85
103
|
|
86
104
|
private
|
87
|
-
def normalize_keys(env)
|
105
|
+
def normalize_keys(defaults, env)
|
88
106
|
new_env = {}
|
89
107
|
env.each_pair { |k, v| new_env[rack_key_for(k)] = rack_value_for(k, v) }
|
108
|
+
|
109
|
+
defaults.each_pair do |k, v|
|
110
|
+
key = rack_key_for(k)
|
111
|
+
new_env[key] = rack_value_for(k, v) unless new_env.key?(key)
|
112
|
+
end
|
113
|
+
|
90
114
|
new_env["rack.url_scheme"] = new_env["HTTPS"] == "on" ? "https" : "http"
|
91
115
|
new_env
|
92
116
|
end
|
@@ -99,19 +123,19 @@ module ActionController
|
|
99
123
|
input: "rack.input"
|
100
124
|
}
|
101
125
|
|
102
|
-
IDENTITY = ->(_) { _ }
|
103
|
-
|
104
|
-
RACK_VALUE_TRANSLATION = {
|
105
|
-
https: ->(v) { v ? "on" : "off" },
|
106
|
-
method: ->(v) { v.upcase },
|
107
|
-
}
|
108
|
-
|
109
126
|
def rack_key_for(key)
|
110
|
-
RACK_KEY_TRANSLATION
|
127
|
+
RACK_KEY_TRANSLATION[key] || key.to_s
|
111
128
|
end
|
112
129
|
|
113
130
|
def rack_value_for(key, value)
|
114
|
-
|
131
|
+
case key
|
132
|
+
when :https
|
133
|
+
value ? "on" : "off"
|
134
|
+
when :method
|
135
|
+
-value.upcase
|
136
|
+
else
|
137
|
+
value
|
138
|
+
end
|
115
139
|
end
|
116
140
|
end
|
117
141
|
end
|
@@ -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,
|
@@ -24,9 +24,12 @@ module ActionController
|
|
24
24
|
def new_controller_thread # :nodoc:
|
25
25
|
yield
|
26
26
|
end
|
27
|
+
|
28
|
+
# Avoid a deadlock from the queue filling up
|
29
|
+
Buffer.queue_size = nil
|
27
30
|
end
|
28
31
|
|
29
|
-
# ActionController::TestCase will be deprecated and moved to a gem in
|
32
|
+
# ActionController::TestCase will be deprecated and moved to a gem in the future.
|
30
33
|
# Please use ActionDispatch::IntegrationTest going forward.
|
31
34
|
class TestRequest < ActionDispatch::TestRequest #:nodoc:
|
32
35
|
DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
|
@@ -84,7 +87,7 @@ module ActionController
|
|
84
87
|
value = value.to_param
|
85
88
|
end
|
86
89
|
|
87
|
-
path_parameters[key] = value
|
90
|
+
path_parameters[key.to_sym] = value
|
88
91
|
end
|
89
92
|
end
|
90
93
|
|
@@ -158,7 +161,6 @@ module ActionController
|
|
158
161
|
end.new
|
159
162
|
|
160
163
|
private
|
161
|
-
|
162
164
|
def params_parsers
|
163
165
|
super.merge @custom_param_parsers
|
164
166
|
end
|
@@ -203,12 +205,16 @@ module ActionController
|
|
203
205
|
clear
|
204
206
|
end
|
205
207
|
|
208
|
+
def dig(*keys)
|
209
|
+
keys = keys.map.with_index { |key, i| i.zero? ? key.to_s : key }
|
210
|
+
@data.dig(*keys)
|
211
|
+
end
|
212
|
+
|
206
213
|
def fetch(key, *args, &block)
|
207
214
|
@data.fetch(key.to_s, *args, &block)
|
208
215
|
end
|
209
216
|
|
210
217
|
private
|
211
|
-
|
212
218
|
def load!
|
213
219
|
@id
|
214
220
|
end
|
@@ -276,9 +282,6 @@ module ActionController
|
|
276
282
|
# after calling +post+. If the various assert methods are not sufficient, then you
|
277
283
|
# may use this object to inspect the HTTP response in detail.
|
278
284
|
#
|
279
|
-
# (Earlier versions of \Rails required each functional test to subclass
|
280
|
-
# Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
|
281
|
-
#
|
282
285
|
# == Controller is automatically inferred
|
283
286
|
#
|
284
287
|
# ActionController::TestCase will automatically infer the controller under test
|
@@ -460,7 +463,7 @@ module ActionController
|
|
460
463
|
def process(action, method: "GET", params: nil, session: nil, body: nil, flash: {}, format: nil, xhr: false, as: nil)
|
461
464
|
check_required_ivars
|
462
465
|
|
463
|
-
action = action.to_s
|
466
|
+
action = +action.to_s
|
464
467
|
http_method = method.to_s.upcase
|
465
468
|
|
466
469
|
@html_document = nil
|
@@ -492,57 +495,8 @@ module ActionController
|
|
492
495
|
parameters[:format] = format
|
493
496
|
end
|
494
497
|
|
495
|
-
|
496
|
-
|
497
|
-
query_string_keys = query_parameter_names(generated_extras)
|
498
|
-
|
499
|
-
@request.assign_parameters(@routes, controller_class_name, action, parameters, generated_path, query_string_keys)
|
500
|
-
|
501
|
-
@request.session.update(session) if session
|
502
|
-
@request.flash.update(flash || {})
|
503
|
-
|
504
|
-
if xhr
|
505
|
-
@request.set_header "HTTP_X_REQUESTED_WITH", "XMLHttpRequest"
|
506
|
-
@request.fetch_header("HTTP_ACCEPT") do |k|
|
507
|
-
@request.set_header k, [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
|
508
|
-
end
|
509
|
-
end
|
510
|
-
|
511
|
-
@request.fetch_header("SCRIPT_NAME") do |k|
|
512
|
-
@request.set_header k, @controller.config.relative_url_root
|
513
|
-
end
|
514
|
-
|
515
|
-
begin
|
516
|
-
@controller.recycle!
|
517
|
-
@controller.dispatch(action, @request, @response)
|
518
|
-
ensure
|
519
|
-
@request = @controller.request
|
520
|
-
@response = @controller.response
|
521
|
-
|
522
|
-
if @request.have_cookie_jar?
|
523
|
-
unless @request.cookie_jar.committed?
|
524
|
-
@request.cookie_jar.write(@response)
|
525
|
-
cookies.update(@request.cookie_jar.instance_variable_get(:@cookies))
|
526
|
-
end
|
527
|
-
end
|
528
|
-
@response.prepare!
|
529
|
-
|
530
|
-
if flash_value = @request.flash.to_session_value
|
531
|
-
@request.session["flash"] = flash_value
|
532
|
-
else
|
533
|
-
@request.session.delete("flash")
|
534
|
-
end
|
535
|
-
|
536
|
-
if xhr
|
537
|
-
@request.delete_header "HTTP_X_REQUESTED_WITH"
|
538
|
-
@request.delete_header "HTTP_ACCEPT"
|
539
|
-
end
|
540
|
-
@request.query_string = ""
|
541
|
-
|
542
|
-
@response.sent!
|
543
|
-
end
|
544
|
-
|
545
|
-
@response
|
498
|
+
setup_request(controller_class_name, action, parameters, session, flash, xhr)
|
499
|
+
process_controller_response(action, cookies, xhr)
|
546
500
|
end
|
547
501
|
|
548
502
|
def controller_class_name
|
@@ -598,12 +552,66 @@ module ActionController
|
|
598
552
|
end
|
599
553
|
|
600
554
|
private
|
555
|
+
def setup_request(controller_class_name, action, parameters, session, flash, xhr)
|
556
|
+
generated_extras = @routes.generate_extras(parameters.merge(controller: controller_class_name, action: action))
|
557
|
+
generated_path = generated_path(generated_extras)
|
558
|
+
query_string_keys = query_parameter_names(generated_extras)
|
559
|
+
|
560
|
+
@request.assign_parameters(@routes, controller_class_name, action, parameters, generated_path, query_string_keys)
|
561
|
+
|
562
|
+
@request.session.update(session) if session
|
563
|
+
@request.flash.update(flash || {})
|
564
|
+
|
565
|
+
if xhr
|
566
|
+
@request.set_header "HTTP_X_REQUESTED_WITH", "XMLHttpRequest"
|
567
|
+
@request.fetch_header("HTTP_ACCEPT") do |k|
|
568
|
+
@request.set_header k, [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
@request.fetch_header("SCRIPT_NAME") do |k|
|
573
|
+
@request.set_header k, @controller.config.relative_url_root
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
def process_controller_response(action, cookies, xhr)
|
578
|
+
begin
|
579
|
+
@controller.recycle!
|
580
|
+
@controller.dispatch(action, @request, @response)
|
581
|
+
ensure
|
582
|
+
@request = @controller.request
|
583
|
+
@response = @controller.response
|
584
|
+
|
585
|
+
if @request.have_cookie_jar?
|
586
|
+
unless @request.cookie_jar.committed?
|
587
|
+
@request.cookie_jar.write(@response)
|
588
|
+
cookies.update(@request.cookie_jar.instance_variable_get(:@cookies))
|
589
|
+
end
|
590
|
+
end
|
591
|
+
@response.prepare!
|
592
|
+
|
593
|
+
if flash_value = @request.flash.to_session_value
|
594
|
+
@request.session["flash"] = flash_value
|
595
|
+
else
|
596
|
+
@request.session.delete("flash")
|
597
|
+
end
|
598
|
+
|
599
|
+
if xhr
|
600
|
+
@request.delete_header "HTTP_X_REQUESTED_WITH"
|
601
|
+
@request.delete_header "HTTP_ACCEPT"
|
602
|
+
end
|
603
|
+
@request.query_string = ""
|
604
|
+
|
605
|
+
@response.sent!
|
606
|
+
end
|
607
|
+
|
608
|
+
@response
|
609
|
+
end
|
601
610
|
|
602
611
|
def scrub_env!(env)
|
603
|
-
env.delete_if
|
604
|
-
|
605
|
-
|
606
|
-
env.delete "action_dispatch.request.request_parameters"
|
612
|
+
env.delete_if do |k, _|
|
613
|
+
k.start_with?("rack.request", "action_dispatch.request", "action_dispatch.rescue")
|
614
|
+
end
|
607
615
|
env["rack.input"] = StringIO.new
|
608
616
|
env.delete "CONTENT_LENGTH"
|
609
617
|
env.delete "RAW_POST_DATA"
|
data/lib/action_controller.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/rails"
|
4
3
|
require "abstract_controller"
|
5
4
|
require "action_dispatch"
|
6
|
-
require "action_controller/metal/live"
|
7
5
|
require "action_controller/metal/strong_parameters"
|
8
6
|
|
9
7
|
module ActionController
|
@@ -12,7 +10,6 @@ module ActionController
|
|
12
10
|
autoload :API
|
13
11
|
autoload :Base
|
14
12
|
autoload :Metal
|
15
|
-
autoload :Middleware
|
16
13
|
autoload :Renderer
|
17
14
|
autoload :FormBuilder
|
18
15
|
|
@@ -21,20 +18,26 @@ module ActionController
|
|
21
18
|
end
|
22
19
|
|
23
20
|
autoload_under "metal" do
|
21
|
+
eager_autoload do
|
22
|
+
autoload :Live
|
23
|
+
end
|
24
|
+
|
24
25
|
autoload :ConditionalGet
|
25
26
|
autoload :ContentSecurityPolicy
|
26
27
|
autoload :Cookies
|
27
28
|
autoload :DataStreaming
|
29
|
+
autoload :DefaultHeaders
|
28
30
|
autoload :EtagWithTemplateDigest
|
29
31
|
autoload :EtagWithFlash
|
32
|
+
autoload :PermissionsPolicy
|
30
33
|
autoload :Flash
|
31
|
-
autoload :ForceSSL
|
32
34
|
autoload :Head
|
33
35
|
autoload :Helpers
|
34
36
|
autoload :HttpAuthentication
|
35
37
|
autoload :BasicImplicitRender
|
36
38
|
autoload :ImplicitRender
|
37
39
|
autoload :Instrumentation
|
40
|
+
autoload :Logging
|
38
41
|
autoload :MimeResponds
|
39
42
|
autoload :ParamsWrapper
|
40
43
|
autoload :Redirecting
|
@@ -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)
|
@@ -18,7 +18,7 @@ module ActionDispatch
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def if_none_match_etags
|
21
|
-
if_none_match ? if_none_match.split(
|
21
|
+
if_none_match ? if_none_match.split(",").each(&:strip!) : []
|
22
22
|
end
|
23
23
|
|
24
24
|
def not_modified?(modified_at)
|
@@ -114,7 +114,7 @@ module ActionDispatch
|
|
114
114
|
|
115
115
|
# True if an ETag is set and it's a weak validator (preceded with W/)
|
116
116
|
def weak_etag?
|
117
|
-
etag? && etag.
|
117
|
+
etag? && etag.start_with?('W/"')
|
118
118
|
end
|
119
119
|
|
120
120
|
# True if an ETag is set and it isn't a weak validator (not preceded with W/)
|
@@ -123,10 +123,9 @@ module ActionDispatch
|
|
123
123
|
end
|
124
124
|
|
125
125
|
private
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
SPECIAL_KEYS = Set.new(%w[extras no-cache max-age public private must-revalidate])
|
126
|
+
DATE = "Date"
|
127
|
+
LAST_MODIFIED = "Last-Modified"
|
128
|
+
SPECIAL_KEYS = Set.new(%w[extras no-store no-cache max-age public private must-revalidate])
|
130
129
|
|
131
130
|
def generate_weak_etag(validators)
|
132
131
|
"W/#{generate_strong_etag(validators)}"
|
@@ -151,8 +150,8 @@ module ActionDispatch
|
|
151
150
|
directive, argument = segment.split("=", 2)
|
152
151
|
|
153
152
|
if SPECIAL_KEYS.include? directive
|
154
|
-
|
155
|
-
cache_control[
|
153
|
+
directive.tr!("-", "_")
|
154
|
+
cache_control[directive.to_sym] = argument || true
|
156
155
|
else
|
157
156
|
cache_control[:extras] ||= []
|
158
157
|
cache_control[:extras] << segment
|
@@ -166,11 +165,12 @@ 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
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
168
|
+
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
|
169
|
+
NO_STORE = "no-store"
|
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
|
@@ -183,38 +183,42 @@ module ActionDispatch
|
|
183
183
|
end
|
184
184
|
|
185
185
|
def merge_and_normalize_cache_control!(cache_control)
|
186
|
-
control =
|
187
|
-
|
188
|
-
if
|
186
|
+
control = cache_control_headers
|
187
|
+
|
188
|
+
return if control.empty? && cache_control.empty? # Let middleware handle default behavior
|
189
|
+
|
190
|
+
if extras = control.delete(:extras)
|
189
191
|
cache_control[:extras] ||= []
|
190
192
|
cache_control[:extras] += extras
|
191
193
|
cache_control[:extras].uniq!
|
192
194
|
end
|
193
195
|
|
194
|
-
control.merge! cc_headers
|
195
196
|
control.merge! cache_control
|
196
197
|
|
197
|
-
|
198
|
-
|
198
|
+
options = []
|
199
|
+
|
200
|
+
if control[:no_store]
|
201
|
+
options << PRIVATE if control[:private]
|
202
|
+
options << NO_STORE
|
199
203
|
elsif control[:no_cache]
|
200
|
-
options = []
|
201
204
|
options << PUBLIC if control[:public]
|
202
205
|
options << NO_CACHE
|
203
206
|
options.concat(control[:extras]) if control[:extras]
|
204
|
-
|
205
|
-
self._cache_control = options.join(", ")
|
206
207
|
else
|
207
|
-
extras
|
208
|
+
extras = control[:extras]
|
208
209
|
max_age = control[:max_age]
|
210
|
+
stale_while_revalidate = control[:stale_while_revalidate]
|
211
|
+
stale_if_error = control[:stale_if_error]
|
209
212
|
|
210
|
-
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
|
-
|
216
|
-
self._cache_control = options.join(", ")
|
217
219
|
end
|
220
|
+
|
221
|
+
self._cache_control = options.join(", ")
|
218
222
|
end
|
219
223
|
end
|
220
224
|
end
|
@@ -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
|
@@ -1,13 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/object/deep_dup"
|
4
|
+
require "active_support/core_ext/array/wrap"
|
4
5
|
|
5
6
|
module ActionDispatch #:nodoc:
|
6
7
|
class ContentSecurityPolicy
|
7
8
|
class Middleware
|
8
|
-
CONTENT_TYPE = "Content-Type"
|
9
|
-
POLICY = "Content-Security-Policy"
|
10
|
-
POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only"
|
9
|
+
CONTENT_TYPE = "Content-Type"
|
10
|
+
POLICY = "Content-Security-Policy"
|
11
|
+
POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only"
|
11
12
|
|
12
13
|
def initialize(app)
|
13
14
|
@app = app
|
@@ -21,8 +22,9 @@ module ActionDispatch #:nodoc:
|
|
21
22
|
|
22
23
|
if policy = request.content_security_policy
|
23
24
|
nonce = request.content_security_policy_nonce
|
25
|
+
nonce_directives = request.content_security_policy_nonce_directives
|
24
26
|
context = request.controller_instance || request
|
25
|
-
headers[header_name(request)] = policy.build(context, nonce)
|
27
|
+
headers[header_name(request)] = policy.build(context, nonce, nonce_directives)
|
26
28
|
end
|
27
29
|
|
28
30
|
response
|
@@ -43,10 +45,11 @@ module ActionDispatch #:nodoc:
|
|
43
45
|
end
|
44
46
|
|
45
47
|
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"
|
48
|
+
POLICY = "action_dispatch.content_security_policy"
|
49
|
+
POLICY_REPORT_ONLY = "action_dispatch.content_security_policy_report_only"
|
50
|
+
NONCE_GENERATOR = "action_dispatch.content_security_policy_nonce_generator"
|
51
|
+
NONCE = "action_dispatch.content_security_policy_nonce"
|
52
|
+
NONCE_DIRECTIVES = "action_dispatch.content_security_policy_nonce_directives"
|
50
53
|
|
51
54
|
def content_security_policy
|
52
55
|
get_header(POLICY)
|
@@ -72,6 +75,14 @@ module ActionDispatch #:nodoc:
|
|
72
75
|
set_header(NONCE_GENERATOR, generator)
|
73
76
|
end
|
74
77
|
|
78
|
+
def content_security_policy_nonce_directives
|
79
|
+
get_header(NONCE_DIRECTIVES)
|
80
|
+
end
|
81
|
+
|
82
|
+
def content_security_policy_nonce_directives=(generator)
|
83
|
+
set_header(NONCE_DIRECTIVES, generator)
|
84
|
+
end
|
85
|
+
|
75
86
|
def content_security_policy_nonce
|
76
87
|
if content_security_policy_nonce_generator
|
77
88
|
if nonce = get_header(NONCE)
|
@@ -83,7 +94,6 @@ module ActionDispatch #:nodoc:
|
|
83
94
|
end
|
84
95
|
|
85
96
|
private
|
86
|
-
|
87
97
|
def generate_content_security_policy_nonce
|
88
98
|
content_security_policy_nonce_generator.call(self)
|
89
99
|
end
|
@@ -119,14 +129,19 @@ module ActionDispatch #:nodoc:
|
|
119
129
|
manifest_src: "manifest-src",
|
120
130
|
media_src: "media-src",
|
121
131
|
object_src: "object-src",
|
132
|
+
prefetch_src: "prefetch-src",
|
122
133
|
script_src: "script-src",
|
134
|
+
script_src_attr: "script-src-attr",
|
135
|
+
script_src_elem: "script-src-elem",
|
123
136
|
style_src: "style-src",
|
137
|
+
style_src_attr: "style-src-attr",
|
138
|
+
style_src_elem: "style-src-elem",
|
124
139
|
worker_src: "worker-src"
|
125
140
|
}.freeze
|
126
141
|
|
127
|
-
|
142
|
+
DEFAULT_NONCE_DIRECTIVES = %w[script-src style-src].freeze
|
128
143
|
|
129
|
-
private_constant :MAPPINGS, :DIRECTIVES, :
|
144
|
+
private_constant :MAPPINGS, :DIRECTIVES, :DEFAULT_NONCE_DIRECTIVES
|
130
145
|
|
131
146
|
attr_reader :directives
|
132
147
|
|
@@ -195,8 +210,9 @@ module ActionDispatch #:nodoc:
|
|
195
210
|
end
|
196
211
|
end
|
197
212
|
|
198
|
-
def build(context = nil, nonce = nil)
|
199
|
-
|
213
|
+
def build(context = nil, nonce = nil, nonce_directives = nil)
|
214
|
+
nonce_directives = DEFAULT_NONCE_DIRECTIVES if nonce_directives.nil?
|
215
|
+
build_directives(context, nonce, nonce_directives).compact.join("; ")
|
200
216
|
end
|
201
217
|
|
202
218
|
private
|
@@ -219,10 +235,10 @@ module ActionDispatch #:nodoc:
|
|
219
235
|
end
|
220
236
|
end
|
221
237
|
|
222
|
-
def build_directives(context, nonce)
|
238
|
+
def build_directives(context, nonce, nonce_directives)
|
223
239
|
@directives.map do |directive, sources|
|
224
240
|
if sources.is_a?(Array)
|
225
|
-
if nonce && nonce_directive?(directive)
|
241
|
+
if nonce && nonce_directive?(directive, nonce_directives)
|
226
242
|
"#{directive} #{build_directive(sources, context).join(' ')} 'nonce-#{nonce}'"
|
227
243
|
else
|
228
244
|
"#{directive} #{build_directive(sources, context).join(' ')}"
|
@@ -250,15 +266,15 @@ module ActionDispatch #:nodoc:
|
|
250
266
|
raise RuntimeError, "Missing context for the dynamic content security policy source: #{source.inspect}"
|
251
267
|
else
|
252
268
|
resolved = context.instance_exec(&source)
|
253
|
-
|
269
|
+
apply_mappings(Array.wrap(resolved))
|
254
270
|
end
|
255
271
|
else
|
256
272
|
raise RuntimeError, "Unexpected content security policy source: #{source.inspect}"
|
257
273
|
end
|
258
274
|
end
|
259
275
|
|
260
|
-
def nonce_directive?(directive)
|
261
|
-
|
276
|
+
def nonce_directive?(directive, nonce_directives)
|
277
|
+
nonce_directives.include?(directive)
|
262
278
|
end
|
263
279
|
end
|
264
280
|
end
|