actionpack 6.1.7.5 → 7.1.3.1
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 +355 -435
- data/MIT-LICENSE +2 -1
- data/README.rdoc +6 -7
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +33 -37
- data/lib/abstract_controller/caching/fragments.rb +4 -2
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +50 -11
- data/lib/abstract_controller/collector.rb +2 -2
- data/lib/abstract_controller/deprecator.rb +7 -0
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +78 -30
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
- data/lib/abstract_controller/rendering.rb +12 -14
- data/lib/abstract_controller/translation.rb +26 -7
- data/lib/abstract_controller/url_for.rb +6 -6
- data/lib/abstract_controller.rb +6 -0
- data/lib/action_controller/api.rb +12 -10
- data/lib/action_controller/base.rb +8 -21
- data/lib/action_controller/caching.rb +2 -0
- data/lib/action_controller/deprecator.rb +7 -0
- data/lib/action_controller/form_builder.rb +4 -2
- data/lib/action_controller/log_subscriber.rb +20 -7
- data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +137 -102
- data/lib/action_controller/metal/content_security_policy.rb +37 -3
- data/lib/action_controller/metal/cookies.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +25 -31
- data/lib/action_controller/metal/default_headers.rb +2 -0
- data/lib/action_controller/metal/etag_with_flash.rb +3 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
- data/lib/action_controller/metal/exceptions.rb +27 -30
- data/lib/action_controller/metal/flash.rb +6 -2
- data/lib/action_controller/metal/head.rb +9 -7
- data/lib/action_controller/metal/helpers.rb +5 -16
- data/lib/action_controller/metal/http_authentication.rb +78 -42
- data/lib/action_controller/metal/implicit_render.rb +5 -3
- data/lib/action_controller/metal/instrumentation.rb +62 -50
- data/lib/action_controller/metal/live.rb +67 -2
- data/lib/action_controller/metal/mime_responds.rb +5 -5
- data/lib/action_controller/metal/params_wrapper.rb +24 -13
- data/lib/action_controller/metal/permissions_policy.rb +20 -29
- data/lib/action_controller/metal/redirecting.rb +96 -23
- data/lib/action_controller/metal/renderers.rb +14 -15
- data/lib/action_controller/metal/rendering.rb +121 -16
- data/lib/action_controller/metal/request_forgery_protection.rb +208 -68
- data/lib/action_controller/metal/rescue.rb +7 -4
- data/lib/action_controller/metal/streaming.rb +74 -36
- data/lib/action_controller/metal/strong_parameters.rb +254 -151
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +10 -5
- data/lib/action_controller/metal.rb +89 -34
- data/lib/action_controller/railtie.rb +66 -9
- data/lib/action_controller/renderer.rb +99 -85
- data/lib/action_controller/test_case.rb +42 -11
- data/lib/action_controller.rb +10 -6
- data/lib/action_dispatch/constants.rb +32 -0
- data/lib/action_dispatch/deprecator.rb +7 -0
- data/lib/action_dispatch/http/cache.rb +21 -16
- data/lib/action_dispatch/http/content_security_policy.rb +122 -44
- data/lib/action_dispatch/http/filter_parameters.rb +14 -23
- data/lib/action_dispatch/http/headers.rb +3 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +25 -15
- data/lib/action_dispatch/http/mime_type.rb +43 -22
- data/lib/action_dispatch/http/mime_types.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +6 -6
- data/lib/action_dispatch/http/permissions_policy.rb +57 -19
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +75 -51
- data/lib/action_dispatch/http/response.rb +81 -77
- data/lib/action_dispatch/http/upload.rb +15 -2
- data/lib/action_dispatch/http/url.rb +11 -19
- data/lib/action_dispatch/journey/formatter.rb +8 -2
- data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
- data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
- data/lib/action_dispatch/journey/nodes/node.rb +70 -5
- data/lib/action_dispatch/journey/path/pattern.rb +36 -27
- data/lib/action_dispatch/journey/route.rb +8 -14
- data/lib/action_dispatch/journey/router/utils.rb +2 -2
- data/lib/action_dispatch/journey/router.rb +10 -9
- data/lib/action_dispatch/journey/routes.rb +5 -5
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/log_subscriber.rb +23 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -7
- data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -0
- data/lib/action_dispatch/middleware/cookies.rb +97 -107
- data/lib/action_dispatch/middleware/debug_exceptions.rb +31 -28
- data/lib/action_dispatch/middleware/debug_locks.rb +7 -4
- data/lib/action_dispatch/middleware/debug_view.rb +7 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +190 -27
- data/lib/action_dispatch/middleware/executor.rb +3 -0
- data/lib/action_dispatch/middleware/flash.rb +24 -18
- data/lib/action_dispatch/middleware/host_authorization.rb +19 -20
- data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
- data/lib/action_dispatch/middleware/reloader.rb +7 -5
- data/lib/action_dispatch/middleware/remote_ip.rb +32 -19
- data/lib/action_dispatch/middleware/request_id.rb +5 -3
- data/lib/action_dispatch/middleware/server_timing.rb +76 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +6 -1
- data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -13
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +30 -25
- data/lib/action_dispatch/middleware/ssl.rb +18 -6
- data/lib/action_dispatch/middleware/stack.rb +34 -11
- data/lib/action_dispatch/middleware/static.rb +16 -16
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +9 -9
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +45 -18
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +6 -6
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -55
- data/lib/action_dispatch/railtie.rb +20 -4
- data/lib/action_dispatch/request/session.rb +59 -19
- data/lib/action_dispatch/request/utils.rb +8 -3
- data/lib/action_dispatch/routing/inspector.rb +55 -7
- data/lib/action_dispatch/routing/mapper.rb +117 -107
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
- data/lib/action_dispatch/routing/redirection.rb +20 -8
- data/lib/action_dispatch/routing/route_set.rb +67 -27
- data/lib/action_dispatch/routing/routes_proxy.rb +11 -16
- data/lib/action_dispatch/routing/url_for.rb +29 -26
- data/lib/action_dispatch/routing.rb +12 -13
- data/lib/action_dispatch/system_test_case.rb +8 -8
- data/lib/action_dispatch/system_testing/browser.rb +20 -29
- data/lib/action_dispatch/system_testing/driver.rb +34 -18
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +35 -20
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
- data/lib/action_dispatch/testing/assertion_response.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +14 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +70 -30
- data/lib/action_dispatch/testing/assertions.rb +3 -4
- data/lib/action_dispatch/testing/integration.rb +33 -25
- data/lib/action_dispatch/testing/request_encoder.rb +4 -1
- data/lib/action_dispatch/testing/test_process.rb +5 -30
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +34 -2
- data/lib/action_dispatch.rb +38 -4
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_pack.rb +1 -1
- metadata +67 -30
@@ -31,7 +31,7 @@ module ActionController
|
|
31
31
|
|
32
32
|
# ActionController::TestCase will be deprecated and moved to a gem in the future.
|
33
33
|
# Please use ActionDispatch::IntegrationTest going forward.
|
34
|
-
class TestRequest < ActionDispatch::TestRequest
|
34
|
+
class TestRequest < ActionDispatch::TestRequest # :nodoc:
|
35
35
|
DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
|
36
36
|
DEFAULT_ENV.delete "PATH_INFO"
|
37
37
|
|
@@ -127,6 +127,9 @@ module ActionController
|
|
127
127
|
fetch_header("PATH_INFO") do |k|
|
128
128
|
set_header k, generated_path
|
129
129
|
end
|
130
|
+
fetch_header("ORIGINAL_FULLPATH") do |k|
|
131
|
+
set_header k, fullpath
|
132
|
+
end
|
130
133
|
path_parameters[:controller] = controller_path
|
131
134
|
path_parameters[:action] = action
|
132
135
|
|
@@ -179,14 +182,15 @@ module ActionController
|
|
179
182
|
|
180
183
|
# Methods #destroy and #load! are overridden to avoid calling methods on the
|
181
184
|
# @store object, which does not exist for the TestSession class.
|
182
|
-
class TestSession < Rack::Session::Abstract::PersistedSecure::SecureSessionHash
|
185
|
+
class TestSession < Rack::Session::Abstract::PersistedSecure::SecureSessionHash # :nodoc:
|
183
186
|
DEFAULT_OPTIONS = Rack::Session::Abstract::Persisted::DEFAULT_OPTIONS
|
184
187
|
|
185
|
-
def initialize(session = {})
|
188
|
+
def initialize(session = {}, id = Rack::Session::SessionId.new(SecureRandom.hex(16)))
|
186
189
|
super(nil, nil)
|
187
|
-
@id =
|
190
|
+
@id = id
|
188
191
|
@data = stringify_keys(session)
|
189
192
|
@loaded = true
|
193
|
+
@initially_empty = @data.empty?
|
190
194
|
end
|
191
195
|
|
192
196
|
def exists?
|
@@ -214,21 +218,31 @@ module ActionController
|
|
214
218
|
@data.fetch(key.to_s, *args, &block)
|
215
219
|
end
|
216
220
|
|
221
|
+
def enabled?
|
222
|
+
true
|
223
|
+
end
|
224
|
+
|
225
|
+
def id_was
|
226
|
+
@id
|
227
|
+
end
|
228
|
+
|
217
229
|
private
|
218
230
|
def load!
|
219
231
|
@id
|
220
232
|
end
|
221
233
|
end
|
222
234
|
|
235
|
+
# = Action Controller Test Case
|
236
|
+
#
|
223
237
|
# Superclass for ActionController functional tests. Functional tests allow you to
|
224
238
|
# test a single controller action per test method.
|
225
239
|
#
|
226
240
|
# == Use integration style controller tests over functional style controller tests.
|
227
241
|
#
|
228
|
-
# Rails discourages the use of functional tests in favor of integration tests
|
242
|
+
# \Rails discourages the use of functional tests in favor of integration tests
|
229
243
|
# (use ActionDispatch::IntegrationTest).
|
230
244
|
#
|
231
|
-
# New Rails applications no longer generate functional style controller tests and they should
|
245
|
+
# New \Rails applications no longer generate functional style controller tests and they should
|
232
246
|
# only be used for backward compatibility. Integration style controller tests perform actual
|
233
247
|
# requests, whereas functional style controller tests merely simulate a request. Besides,
|
234
248
|
# integration tests are as fast as functional tests and provide lot of helpers such as +as+,
|
@@ -237,7 +251,7 @@ module ActionController
|
|
237
251
|
# == Basic example
|
238
252
|
#
|
239
253
|
# Functional tests are written as follows:
|
240
|
-
# 1. First, one uses the +get+, +post+, +patch+, +put+, +delete
|
254
|
+
# 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+, or +head+ method to simulate
|
241
255
|
# an HTTP request.
|
242
256
|
# 2. Then, one asserts whether the current state is as expected. "State" can be anything:
|
243
257
|
# the controller's HTTP response, the database contents, etc.
|
@@ -329,6 +343,8 @@ module ActionController
|
|
329
343
|
#
|
330
344
|
# assert_redirected_to page_url(title: 'foo')
|
331
345
|
class TestCase < ActiveSupport::TestCase
|
346
|
+
singleton_class.attr_accessor :executor_around_each_request
|
347
|
+
|
332
348
|
module Behavior
|
333
349
|
extend ActiveSupport::Concern
|
334
350
|
include ActionDispatch::TestProcess
|
@@ -385,7 +401,7 @@ module ActionController
|
|
385
401
|
#
|
386
402
|
# You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
|
387
403
|
# +post+, +patch+, +put+, +delete+, and +head+.
|
388
|
-
# Example sending parameters, session and setting a flash message:
|
404
|
+
# Example sending parameters, session, and setting a flash message:
|
389
405
|
#
|
390
406
|
# get :show,
|
391
407
|
# params: { id: 7 },
|
@@ -455,13 +471,19 @@ module ActionController
|
|
455
471
|
# session: { user_id: 1 },
|
456
472
|
# flash: { notice: 'This is flash message' }
|
457
473
|
#
|
458
|
-
# To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE
|
474
|
+
# To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+, and +HEAD+ requests
|
459
475
|
# prefer using #get, #post, #patch, #put, #delete and #head methods
|
460
476
|
# respectively which will make tests more expressive.
|
461
477
|
#
|
478
|
+
# It's not recommended to make more than one request in the same test. Instance
|
479
|
+
# variables that are set in one request will not persist to the next request,
|
480
|
+
# but it's not guaranteed that all \Rails internal state will be reset. Prefer
|
481
|
+
# ActionDispatch::IntegrationTest for making multiple requests in the same test.
|
482
|
+
#
|
462
483
|
# Note that the request method is not verified.
|
463
484
|
def process(action, method: "GET", params: nil, session: nil, body: nil, flash: {}, format: nil, xhr: false, as: nil)
|
464
485
|
check_required_ivars
|
486
|
+
@controller.clear_instance_variables_between_requests
|
465
487
|
|
466
488
|
action = +action.to_s
|
467
489
|
http_method = method.to_s.upcase
|
@@ -574,10 +596,19 @@ module ActionController
|
|
574
596
|
end
|
575
597
|
end
|
576
598
|
|
599
|
+
def wrap_execution(&block)
|
600
|
+
if ActionController::TestCase.executor_around_each_request && defined?(Rails.application) && Rails.application
|
601
|
+
Rails.application.executor.wrap(&block)
|
602
|
+
else
|
603
|
+
yield
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
577
607
|
def process_controller_response(action, cookies, xhr)
|
578
608
|
begin
|
579
609
|
@controller.recycle!
|
580
|
-
|
610
|
+
|
611
|
+
wrap_execution { @controller.dispatch(action, @request, @response) }
|
581
612
|
ensure
|
582
613
|
@request = @controller.request
|
583
614
|
@response = @controller.response
|
@@ -623,7 +654,7 @@ module ActionController
|
|
623
654
|
end
|
624
655
|
|
625
656
|
def check_required_ivars
|
626
|
-
#
|
657
|
+
# Check for required instance variables so we can give an
|
627
658
|
# understandable error message.
|
628
659
|
[:@routes, :@controller, :@request, :@response].each do |iv_name|
|
629
660
|
if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?
|
data/lib/action_controller.rb
CHANGED
@@ -2,8 +2,17 @@
|
|
2
2
|
|
3
3
|
require "abstract_controller"
|
4
4
|
require "action_dispatch"
|
5
|
+
require "action_controller/deprecator"
|
5
6
|
require "action_controller/metal/strong_parameters"
|
7
|
+
require "action_controller/metal/exceptions"
|
6
8
|
|
9
|
+
# = Action Controller
|
10
|
+
#
|
11
|
+
# Action Controller is a module of Action Pack.
|
12
|
+
#
|
13
|
+
# Action Controller provides a base controller class that can be subclassed to
|
14
|
+
# implement filters and actions to handle requests. The result of an action is
|
15
|
+
# typically content generated from views.
|
7
16
|
module ActionController
|
8
17
|
extend ActiveSupport::Autoload
|
9
18
|
|
@@ -18,10 +27,6 @@ module ActionController
|
|
18
27
|
end
|
19
28
|
|
20
29
|
autoload_under "metal" do
|
21
|
-
eager_autoload do
|
22
|
-
autoload :Live
|
23
|
-
end
|
24
|
-
|
25
30
|
autoload :ConditionalGet
|
26
31
|
autoload :ContentSecurityPolicy
|
27
32
|
autoload :Cookies
|
@@ -37,6 +42,7 @@ module ActionController
|
|
37
42
|
autoload :BasicImplicitRender
|
38
43
|
autoload :ImplicitRender
|
39
44
|
autoload :Instrumentation
|
45
|
+
autoload :Live
|
40
46
|
autoload :Logging
|
41
47
|
autoload :MimeResponds
|
42
48
|
autoload :ParamsWrapper
|
@@ -62,8 +68,6 @@ end
|
|
62
68
|
|
63
69
|
# Common Active Support usage in Action Controller
|
64
70
|
require "active_support/core_ext/module/attribute_accessors"
|
65
|
-
require "active_support/core_ext/load_error"
|
66
71
|
require "active_support/core_ext/module/attr_internal"
|
67
72
|
require "active_support/core_ext/name_error"
|
68
|
-
require "active_support/core_ext/uri"
|
69
73
|
require "active_support/inflector"
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack/version"
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
module Constants
|
7
|
+
# Response Header keys for Rack 2.x and 3.x
|
8
|
+
if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3")
|
9
|
+
VARY = "Vary"
|
10
|
+
CONTENT_ENCODING = "Content-Encoding"
|
11
|
+
CONTENT_SECURITY_POLICY = "Content-Security-Policy"
|
12
|
+
CONTENT_SECURITY_POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only"
|
13
|
+
LOCATION = "Location"
|
14
|
+
FEATURE_POLICY = "Feature-Policy"
|
15
|
+
X_REQUEST_ID = "X-Request-Id"
|
16
|
+
X_CASCADE = "X-Cascade"
|
17
|
+
SERVER_TIMING = "Server-Timing"
|
18
|
+
STRICT_TRANSPORT_SECURITY = "Strict-Transport-Security"
|
19
|
+
else
|
20
|
+
VARY = "vary"
|
21
|
+
CONTENT_ENCODING = "content-encoding"
|
22
|
+
CONTENT_SECURITY_POLICY = "content-security-policy"
|
23
|
+
CONTENT_SECURITY_POLICY_REPORT_ONLY = "content-security-policy-report-only"
|
24
|
+
LOCATION = "location"
|
25
|
+
FEATURE_POLICY = "feature-policy"
|
26
|
+
X_REQUEST_ID = "x-request-id"
|
27
|
+
X_CASCADE = "x-cascade"
|
28
|
+
SERVER_TIMING = "server-timing"
|
29
|
+
STRICT_TRANSPORT_SECURITY = "strict-transport-security"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -32,8 +32,8 @@ module ActionDispatch
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
# Check response freshness (Last-Modified and ETag) against request
|
36
|
-
# If-Modified-Since and If-None-Match conditions. If both headers are
|
35
|
+
# Check response freshness (+Last-Modified+ and ETag) against request
|
36
|
+
# +If-Modified-Since+ and +If-None-Match+ conditions. If both headers are
|
37
37
|
# supplied, both must match, or the request is not considered fresh.
|
38
38
|
def fresh?(response)
|
39
39
|
last_modified = if_modified_since
|
@@ -81,8 +81,8 @@ module ActionDispatch
|
|
81
81
|
|
82
82
|
# This method sets a weak ETag validator on the response so browsers
|
83
83
|
# and proxies may cache the response, keyed on the ETag. On subsequent
|
84
|
-
# requests, the If-None-Match header is set to the cached ETag. If it
|
85
|
-
# matches the current ETag, we can return a 304 Not Modified response
|
84
|
+
# requests, the +If-None-Match+ header is set to the cached ETag. If it
|
85
|
+
# matches the current ETag, we can return a <tt>304 Not Modified</tt> response
|
86
86
|
# with no body, letting the browser or proxy know that their cache is
|
87
87
|
# current. Big savings in request time and network bandwidth.
|
88
88
|
#
|
@@ -92,7 +92,7 @@ module ActionDispatch
|
|
92
92
|
# is viewing.
|
93
93
|
#
|
94
94
|
# Strong ETags are considered byte-for-byte identical. They allow a
|
95
|
-
# browser or proxy cache to support Range requests, useful for paging
|
95
|
+
# browser or proxy cache to support +Range+ requests, useful for paging
|
96
96
|
# through a PDF file or scrubbing through a video. Some CDNs only
|
97
97
|
# support strong ETags and will ignore weak ETags entirely.
|
98
98
|
#
|
@@ -112,12 +112,12 @@ module ActionDispatch
|
|
112
112
|
|
113
113
|
def etag?; etag; end
|
114
114
|
|
115
|
-
# True if an ETag is set and it's a weak validator (preceded with W
|
115
|
+
# True if an ETag is set, and it's a weak validator (preceded with <tt>W/</tt>).
|
116
116
|
def weak_etag?
|
117
117
|
etag? && etag.start_with?('W/"')
|
118
118
|
end
|
119
119
|
|
120
|
-
# True if an ETag is set and it isn't a weak validator (not preceded with W
|
120
|
+
# True if an ETag is set, and it isn't a weak validator (not preceded with <tt>W/</tt>).
|
121
121
|
def strong_etag?
|
122
122
|
etag? && !weak_etag?
|
123
123
|
end
|
@@ -138,15 +138,13 @@ module ActionDispatch
|
|
138
138
|
def cache_control_segments
|
139
139
|
if cache_control = _cache_control
|
140
140
|
cache_control.delete(" ").split(",")
|
141
|
-
else
|
142
|
-
[]
|
143
141
|
end
|
144
142
|
end
|
145
143
|
|
146
144
|
def cache_control_headers
|
147
145
|
cache_control = {}
|
148
146
|
|
149
|
-
cache_control_segments
|
147
|
+
cache_control_segments&.each do |segment|
|
150
148
|
directive, argument = segment.split("=", 2)
|
151
149
|
|
152
150
|
if SPECIAL_KEYS.include? directive
|
@@ -187,13 +185,20 @@ module ActionDispatch
|
|
187
185
|
|
188
186
|
return if control.empty? && cache_control.empty? # Let middleware handle default behavior
|
189
187
|
|
190
|
-
if
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
188
|
+
if cache_control.any?
|
189
|
+
# Any caching directive coming from a controller overrides
|
190
|
+
# no-cache/no-store in the default Cache-Control header.
|
191
|
+
control.delete(:no_cache)
|
192
|
+
control.delete(:no_store)
|
193
|
+
|
194
|
+
if extras = control.delete(:extras)
|
195
|
+
cache_control[:extras] ||= []
|
196
|
+
cache_control[:extras] += extras
|
197
|
+
cache_control[:extras].uniq!
|
198
|
+
end
|
195
199
|
|
196
|
-
|
200
|
+
control.merge! cache_control
|
201
|
+
end
|
197
202
|
|
198
203
|
options = []
|
199
204
|
|
@@ -3,23 +3,43 @@
|
|
3
3
|
require "active_support/core_ext/object/deep_dup"
|
4
4
|
require "active_support/core_ext/array/wrap"
|
5
5
|
|
6
|
-
module ActionDispatch
|
6
|
+
module ActionDispatch # :nodoc:
|
7
|
+
# = Action Dispatch Content Security Policy
|
8
|
+
#
|
9
|
+
# Configures the HTTP
|
10
|
+
# {Content-Security-Policy}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy]
|
11
|
+
# response header to help protect against XSS and injection attacks.
|
12
|
+
#
|
13
|
+
# Example global policy:
|
14
|
+
#
|
15
|
+
# Rails.application.config.content_security_policy do |policy|
|
16
|
+
# policy.default_src :self, :https
|
17
|
+
# policy.font_src :self, :https, :data
|
18
|
+
# policy.img_src :self, :https, :data
|
19
|
+
# policy.object_src :none
|
20
|
+
# policy.script_src :self, :https
|
21
|
+
# policy.style_src :self, :https
|
22
|
+
#
|
23
|
+
# # Specify URI for violation reports
|
24
|
+
# policy.report_uri "/csp-violation-report-endpoint"
|
25
|
+
# end
|
7
26
|
class ContentSecurityPolicy
|
8
27
|
class Middleware
|
9
|
-
CONTENT_TYPE = "Content-Type"
|
10
|
-
POLICY = "Content-Security-Policy"
|
11
|
-
POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only"
|
12
|
-
|
13
28
|
def initialize(app)
|
14
29
|
@app = app
|
15
30
|
end
|
16
31
|
|
17
32
|
def call(env)
|
18
|
-
|
19
|
-
|
33
|
+
status, headers, _ = response = @app.call(env)
|
34
|
+
|
35
|
+
# Returning CSP headers with a 304 Not Modified is harmful, since nonces in the new
|
36
|
+
# CSP headers might not match nonces in the cached HTML.
|
37
|
+
return response if status == 304
|
20
38
|
|
21
39
|
return response if policy_present?(headers)
|
22
40
|
|
41
|
+
request = ActionDispatch::Request.new env
|
42
|
+
|
23
43
|
if policy = request.content_security_policy
|
24
44
|
nonce = request.content_security_policy_nonce
|
25
45
|
nonce_directives = request.content_security_policy_nonce_directives
|
@@ -33,14 +53,15 @@ module ActionDispatch #:nodoc:
|
|
33
53
|
private
|
34
54
|
def header_name(request)
|
35
55
|
if request.content_security_policy_report_only
|
36
|
-
|
56
|
+
ActionDispatch::Constants::CONTENT_SECURITY_POLICY_REPORT_ONLY
|
37
57
|
else
|
38
|
-
|
58
|
+
ActionDispatch::Constants::CONTENT_SECURITY_POLICY
|
39
59
|
end
|
40
60
|
end
|
41
61
|
|
42
62
|
def policy_present?(headers)
|
43
|
-
headers[
|
63
|
+
headers[ActionDispatch::Constants::CONTENT_SECURITY_POLICY] ||
|
64
|
+
headers[ActionDispatch::Constants::CONTENT_SECURITY_POLICY_REPORT_ONLY]
|
44
65
|
end
|
45
66
|
end
|
46
67
|
|
@@ -100,43 +121,48 @@ module ActionDispatch #:nodoc:
|
|
100
121
|
end
|
101
122
|
|
102
123
|
MAPPINGS = {
|
103
|
-
self:
|
104
|
-
unsafe_eval:
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
124
|
+
self: "'self'",
|
125
|
+
unsafe_eval: "'unsafe-eval'",
|
126
|
+
unsafe_hashes: "'unsafe-hashes'",
|
127
|
+
unsafe_inline: "'unsafe-inline'",
|
128
|
+
none: "'none'",
|
129
|
+
http: "http:",
|
130
|
+
https: "https:",
|
131
|
+
data: "data:",
|
132
|
+
mediastream: "mediastream:",
|
133
|
+
allow_duplicates: "'allow-duplicates'",
|
134
|
+
blob: "blob:",
|
135
|
+
filesystem: "filesystem:",
|
136
|
+
report_sample: "'report-sample'",
|
137
|
+
script: "'script'",
|
138
|
+
strict_dynamic: "'strict-dynamic'",
|
139
|
+
ws: "ws:",
|
140
|
+
wss: "wss:"
|
117
141
|
}.freeze
|
118
142
|
|
119
143
|
DIRECTIVES = {
|
120
|
-
base_uri:
|
121
|
-
child_src:
|
122
|
-
connect_src:
|
123
|
-
default_src:
|
124
|
-
font_src:
|
125
|
-
form_action:
|
126
|
-
frame_ancestors:
|
127
|
-
frame_src:
|
128
|
-
img_src:
|
129
|
-
manifest_src:
|
130
|
-
media_src:
|
131
|
-
object_src:
|
132
|
-
prefetch_src:
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
144
|
+
base_uri: "base-uri",
|
145
|
+
child_src: "child-src",
|
146
|
+
connect_src: "connect-src",
|
147
|
+
default_src: "default-src",
|
148
|
+
font_src: "font-src",
|
149
|
+
form_action: "form-action",
|
150
|
+
frame_ancestors: "frame-ancestors",
|
151
|
+
frame_src: "frame-src",
|
152
|
+
img_src: "img-src",
|
153
|
+
manifest_src: "manifest-src",
|
154
|
+
media_src: "media-src",
|
155
|
+
object_src: "object-src",
|
156
|
+
prefetch_src: "prefetch-src",
|
157
|
+
require_trusted_types_for: "require-trusted-types-for",
|
158
|
+
script_src: "script-src",
|
159
|
+
script_src_attr: "script-src-attr",
|
160
|
+
script_src_elem: "script-src-elem",
|
161
|
+
style_src: "style-src",
|
162
|
+
style_src_attr: "style-src-attr",
|
163
|
+
style_src_elem: "style-src-elem",
|
164
|
+
trusted_types: "trusted-types",
|
165
|
+
worker_src: "worker-src"
|
140
166
|
}.freeze
|
141
167
|
|
142
168
|
DEFAULT_NONCE_DIRECTIVES = %w[script-src style-src].freeze
|
@@ -164,6 +190,15 @@ module ActionDispatch #:nodoc:
|
|
164
190
|
end
|
165
191
|
end
|
166
192
|
|
193
|
+
# Specify whether to prevent the user agent from loading any assets over
|
194
|
+
# HTTP when the page uses HTTPS:
|
195
|
+
#
|
196
|
+
# policy.block_all_mixed_content
|
197
|
+
#
|
198
|
+
# Pass +false+ to allow it again:
|
199
|
+
#
|
200
|
+
# policy.block_all_mixed_content false
|
201
|
+
#
|
167
202
|
def block_all_mixed_content(enabled = true)
|
168
203
|
if enabled
|
169
204
|
@directives["block-all-mixed-content"] = true
|
@@ -172,6 +207,14 @@ module ActionDispatch #:nodoc:
|
|
172
207
|
end
|
173
208
|
end
|
174
209
|
|
210
|
+
# Restricts the set of plugins that can be embedded:
|
211
|
+
#
|
212
|
+
# policy.plugin_types "application/x-shockwave-flash"
|
213
|
+
#
|
214
|
+
# Leave empty to allow all plugins:
|
215
|
+
#
|
216
|
+
# policy.plugin_types
|
217
|
+
#
|
175
218
|
def plugin_types(*types)
|
176
219
|
if types.first
|
177
220
|
@directives["plugin-types"] = types
|
@@ -180,10 +223,24 @@ module ActionDispatch #:nodoc:
|
|
180
223
|
end
|
181
224
|
end
|
182
225
|
|
226
|
+
# Enable the {report-uri}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri]
|
227
|
+
# directive. Violation reports will be sent to the specified URI:
|
228
|
+
#
|
229
|
+
# policy.report_uri "/csp-violation-report-endpoint"
|
230
|
+
#
|
183
231
|
def report_uri(uri)
|
184
232
|
@directives["report-uri"] = [uri]
|
185
233
|
end
|
186
234
|
|
235
|
+
# Specify asset types for which {Subresource Integrity}[https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity]
|
236
|
+
# is required:
|
237
|
+
#
|
238
|
+
# policy.require_sri_for :script, :style
|
239
|
+
#
|
240
|
+
# Leave empty to not require Subresource Integrity:
|
241
|
+
#
|
242
|
+
# policy.require_sri_for
|
243
|
+
#
|
187
244
|
def require_sri_for(*types)
|
188
245
|
if types.first
|
189
246
|
@directives["require-sri-for"] = types
|
@@ -192,6 +249,19 @@ module ActionDispatch #:nodoc:
|
|
192
249
|
end
|
193
250
|
end
|
194
251
|
|
252
|
+
# Specify whether a {sandbox}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox]
|
253
|
+
# should be enabled for the requested resource:
|
254
|
+
#
|
255
|
+
# policy.sandbox
|
256
|
+
#
|
257
|
+
# Values can be passed as arguments:
|
258
|
+
#
|
259
|
+
# policy.sandbox "allow-scripts", "allow-modals"
|
260
|
+
#
|
261
|
+
# Pass +false+ to disable the sandbox:
|
262
|
+
#
|
263
|
+
# policy.sandbox false
|
264
|
+
#
|
195
265
|
def sandbox(*values)
|
196
266
|
if values.empty?
|
197
267
|
@directives["sandbox"] = true
|
@@ -202,6 +272,14 @@ module ActionDispatch #:nodoc:
|
|
202
272
|
end
|
203
273
|
end
|
204
274
|
|
275
|
+
# Specify whether user agents should treat any assets over HTTP as HTTPS:
|
276
|
+
#
|
277
|
+
# policy.upgrade_insecure_requests
|
278
|
+
#
|
279
|
+
# Pass +false+ to disable it:
|
280
|
+
#
|
281
|
+
# policy.upgrade_insecure_requests false
|
282
|
+
#
|
205
283
|
def upgrade_insecure_requests(enabled = true)
|
206
284
|
if enabled
|
207
285
|
@directives["upgrade-insecure-requests"] = true
|
@@ -4,28 +4,15 @@ require "active_support/parameter_filter"
|
|
4
4
|
|
5
5
|
module ActionDispatch
|
6
6
|
module Http
|
7
|
-
#
|
8
|
-
# the request log by looking in the query string of the request and all
|
9
|
-
# sub-hashes of the params hash to filter. Filtering only certain sub-keys
|
10
|
-
# from a hash is possible by using the dot notation: 'credit_card.number'.
|
11
|
-
# If a block is given, each key and value of the params hash and all
|
12
|
-
# sub-hashes are passed to it, where the value or the key can be replaced using
|
13
|
-
# String#replace or similar methods.
|
7
|
+
# = Action Dispatch HTTP Filter Parameters
|
14
8
|
#
|
15
|
-
#
|
16
|
-
#
|
9
|
+
# Allows you to specify sensitive query string and POST parameters to filter
|
10
|
+
# from the request log.
|
17
11
|
#
|
12
|
+
# # Replaces values with "[FILTERED]" for keys that match /foo|bar/i.
|
18
13
|
# env["action_dispatch.parameter_filter"] = [:foo, "bar"]
|
19
|
-
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
|
20
14
|
#
|
21
|
-
#
|
22
|
-
# => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
|
23
|
-
# change { file: { code: "xxxx"} }
|
24
|
-
#
|
25
|
-
# env["action_dispatch.parameter_filter"] = -> (k, v) do
|
26
|
-
# v.reverse! if k.match?(/secret/i)
|
27
|
-
# end
|
28
|
-
# => reverses the value to all keys matching /secret/i
|
15
|
+
# For more information about filter behavior, see ActiveSupport::ParameterFilter.
|
29
16
|
module FilterParameters
|
30
17
|
ENV_MATCH = [/RAW_POST_DATA/, "rack.request.form_vars"] # :nodoc:
|
31
18
|
NULL_PARAM_FILTER = ActiveSupport::ParameterFilter.new # :nodoc:
|
@@ -36,6 +23,7 @@ module ActionDispatch
|
|
36
23
|
@filtered_parameters = nil
|
37
24
|
@filtered_env = nil
|
38
25
|
@filtered_path = nil
|
26
|
+
@parameter_filter = nil
|
39
27
|
end
|
40
28
|
|
41
29
|
# Returns a hash of parameters with all sensitive data replaced.
|
@@ -55,13 +43,16 @@ module ActionDispatch
|
|
55
43
|
@filtered_path ||= query_string.empty? ? path : "#{path}?#{filtered_query_string}"
|
56
44
|
end
|
57
45
|
|
58
|
-
|
59
|
-
def parameter_filter
|
60
|
-
|
61
|
-
|
62
|
-
|
46
|
+
# Returns the +ActiveSupport::ParameterFilter+ object used to filter in this request.
|
47
|
+
def parameter_filter
|
48
|
+
@parameter_filter ||= if has_header?("action_dispatch.parameter_filter")
|
49
|
+
parameter_filter_for get_header("action_dispatch.parameter_filter")
|
50
|
+
else
|
51
|
+
NULL_PARAM_FILTER
|
52
|
+
end
|
63
53
|
end
|
64
54
|
|
55
|
+
private
|
65
56
|
def env_filter # :doc:
|
66
57
|
user_key = fetch_header("action_dispatch.parameter_filter") {
|
67
58
|
return NULL_ENV_FILTER
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module ActionDispatch
|
4
4
|
module Http
|
5
|
+
# = Action Dispatch HTTP \Headers
|
6
|
+
#
|
5
7
|
# Provides access to the request's HTTP headers from the environment.
|
6
8
|
#
|
7
9
|
# env = { "CONTENT_TYPE" => "text/plain", "HTTP_USER_AGENT" => "curl/7.43.0" }
|
@@ -65,7 +67,7 @@ module ActionDispatch
|
|
65
67
|
@req.set_header env_name(key), value
|
66
68
|
end
|
67
69
|
|
68
|
-
# Add a value to a multivalued header like Vary or Accept-Encoding
|
70
|
+
# Add a value to a multivalued header like +Vary+ or +Accept-Encoding+.
|
69
71
|
def add(key, value)
|
70
72
|
@req.add_header env_name(key), value
|
71
73
|
end
|