actionpack 6.1.7.5 → 7.0.8
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +319 -401
- data/MIT-LICENSE +1 -0
- data/README.rdoc +4 -5
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +13 -26
- data/lib/abstract_controller/caching/fragments.rb +2 -2
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +21 -7
- data/lib/abstract_controller/collector.rb +2 -2
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +17 -12
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/rendering.rb +9 -11
- data/lib/abstract_controller/translation.rb +5 -4
- data/lib/abstract_controller/url_for.rb +4 -6
- data/lib/action_controller/api.rb +7 -7
- data/lib/action_controller/base.rb +5 -4
- data/lib/action_controller/form_builder.rb +2 -2
- data/lib/action_controller/log_subscriber.rb +4 -3
- 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 +36 -2
- data/lib/action_controller/metal/cookies.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +23 -31
- data/lib/action_controller/metal/etag_with_flash.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +19 -30
- data/lib/action_controller/metal/flash.rb +6 -2
- data/lib/action_controller/metal/head.rb +1 -1
- data/lib/action_controller/metal/helpers.rb +2 -2
- data/lib/action_controller/metal/http_authentication.rb +66 -39
- data/lib/action_controller/metal/instrumentation.rb +57 -52
- data/lib/action_controller/metal/live.rb +43 -2
- data/lib/action_controller/metal/mime_responds.rb +3 -3
- data/lib/action_controller/metal/params_wrapper.rb +20 -11
- data/lib/action_controller/metal/permissions_policy.rb +19 -28
- data/lib/action_controller/metal/redirecting.rb +95 -22
- data/lib/action_controller/metal/renderers.rb +12 -13
- data/lib/action_controller/metal/rendering.rb +121 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +83 -32
- data/lib/action_controller/metal/rescue.rb +5 -4
- data/lib/action_controller/metal/streaming.rb +7 -9
- data/lib/action_controller/metal/strong_parameters.rb +138 -115
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +3 -5
- data/lib/action_controller/metal.rb +10 -13
- data/lib/action_controller/railtie.rb +50 -6
- data/lib/action_controller/renderer.rb +1 -20
- data/lib/action_controller/test_case.rb +28 -7
- data/lib/action_controller.rb +2 -5
- data/lib/action_dispatch/http/cache.rb +20 -13
- data/lib/action_dispatch/http/content_security_policy.rb +113 -36
- data/lib/action_dispatch/http/filter_parameters.rb +4 -19
- data/lib/action_dispatch/http/headers.rb +1 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +15 -5
- data/lib/action_dispatch/http/mime_type.rb +9 -11
- data/lib/action_dispatch/http/parameters.rb +5 -5
- data/lib/action_dispatch/http/permissions_policy.rb +17 -1
- data/lib/action_dispatch/http/request.rb +27 -37
- data/lib/action_dispatch/http/response.rb +3 -20
- data/lib/action_dispatch/http/upload.rb +13 -2
- data/lib/action_dispatch/http/url.rb +11 -19
- 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 +22 -13
- data/lib/action_dispatch/journey/route.rb +6 -13
- data/lib/action_dispatch/journey/router/utils.rb +2 -2
- data/lib/action_dispatch/journey/router.rb +1 -1
- data/lib/action_dispatch/journey/routes.rb +3 -3
- 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/middleware/actionable_exceptions.rb +0 -1
- data/lib/action_dispatch/middleware/cookies.rb +20 -13
- data/lib/action_dispatch/middleware/debug_exceptions.rb +6 -4
- data/lib/action_dispatch/middleware/debug_locks.rb +3 -3
- data/lib/action_dispatch/middleware/exception_wrapper.rb +4 -0
- data/lib/action_dispatch/middleware/executor.rb +3 -0
- data/lib/action_dispatch/middleware/flash.rb +17 -18
- data/lib/action_dispatch/middleware/host_authorization.rb +13 -17
- data/lib/action_dispatch/middleware/remote_ip.rb +20 -8
- data/lib/action_dispatch/middleware/request_id.rb +3 -3
- data/lib/action_dispatch/middleware/server_timing.rb +76 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +9 -9
- data/lib/action_dispatch/middleware/show_exceptions.rb +17 -16
- data/lib/action_dispatch/middleware/stack.rb +27 -9
- data/lib/action_dispatch/middleware/static.rb +5 -9
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
- 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 +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +28 -18
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +22 -22
- data/lib/action_dispatch/railtie.rb +8 -2
- data/lib/action_dispatch/request/session.rb +43 -13
- data/lib/action_dispatch/routing/inspector.rb +1 -1
- data/lib/action_dispatch/routing/mapper.rb +82 -83
- data/lib/action_dispatch/routing/redirection.rb +5 -2
- data/lib/action_dispatch/routing/route_set.rb +17 -7
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +24 -25
- data/lib/action_dispatch/routing.rb +5 -6
- data/lib/action_dispatch/system_test_case.rb +5 -5
- data/lib/action_dispatch/system_testing/browser.rb +3 -13
- data/lib/action_dispatch/system_testing/driver.rb +34 -10
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +11 -7
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
- data/lib/action_dispatch/testing/assertions/response.rb +1 -1
- data/lib/action_dispatch/testing/assertions/routing.rb +3 -2
- data/lib/action_dispatch/testing/assertions.rb +2 -5
- data/lib/action_dispatch/testing/integration.rb +6 -8
- data/lib/action_dispatch/testing/test_process.rb +3 -29
- data/lib/action_dispatch/testing/test_response.rb +20 -2
- data/lib/action_dispatch.rb +1 -0
- data/lib/action_pack/gem_version.rb +5 -5
- data/lib/action_pack/version.rb +1 -1
- metadata +16 -15
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
require "active_support/core_ext/array/extract_options"
|
4
4
|
require "action_dispatch/middleware/stack"
|
5
|
-
require "action_dispatch/http/request"
|
6
|
-
require "action_dispatch/http/response"
|
7
5
|
|
8
6
|
module ActionController
|
9
7
|
# Extend ActionDispatch middleware stack to make it aware of options
|
@@ -13,8 +11,8 @@ module ActionController
|
|
13
11
|
# use AuthenticationMiddleware, except: [:index, :show]
|
14
12
|
# end
|
15
13
|
#
|
16
|
-
class MiddlewareStack < ActionDispatch::MiddlewareStack
|
17
|
-
class Middleware < ActionDispatch::MiddlewareStack::Middleware
|
14
|
+
class MiddlewareStack < ActionDispatch::MiddlewareStack # :nodoc:
|
15
|
+
class Middleware < ActionDispatch::MiddlewareStack::Middleware # :nodoc:
|
18
16
|
def initialize(klass, args, actions, strategy, block)
|
19
17
|
@actions = actions
|
20
18
|
@strategy = strategy
|
@@ -62,7 +60,7 @@ module ActionController
|
|
62
60
|
|
63
61
|
# <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
|
64
62
|
# valid Rack interface without the additional niceties provided by
|
65
|
-
#
|
63
|
+
# ActionController::Base.
|
66
64
|
#
|
67
65
|
# A sample metal controller might look like this:
|
68
66
|
#
|
@@ -113,7 +111,7 @@ module ActionController
|
|
113
111
|
#
|
114
112
|
# == Other Helpers
|
115
113
|
#
|
116
|
-
# You can refer to the modules included in
|
114
|
+
# You can refer to the modules included in ActionController::Base to see
|
117
115
|
# other features you can bring into your metal controller.
|
118
116
|
#
|
119
117
|
class Metal < AbstractController::Base
|
@@ -139,7 +137,7 @@ module ActionController
|
|
139
137
|
false
|
140
138
|
end
|
141
139
|
|
142
|
-
# Delegates to the class'
|
140
|
+
# Delegates to the class's ::controller_name.
|
143
141
|
def controller_name
|
144
142
|
self.class.controller_name
|
145
143
|
end
|
@@ -184,7 +182,7 @@ module ActionController
|
|
184
182
|
response_body || response.committed?
|
185
183
|
end
|
186
184
|
|
187
|
-
def dispatch(name, request, response)
|
185
|
+
def dispatch(name, request, response) # :nodoc:
|
188
186
|
set_request!(request)
|
189
187
|
set_response!(response)
|
190
188
|
process(name)
|
@@ -196,12 +194,12 @@ module ActionController
|
|
196
194
|
@_response = response
|
197
195
|
end
|
198
196
|
|
199
|
-
def set_request!(request)
|
197
|
+
def set_request!(request) # :nodoc:
|
200
198
|
@_request = request
|
201
199
|
@_request.controller_instance = self
|
202
200
|
end
|
203
201
|
|
204
|
-
def to_a
|
202
|
+
def to_a # :nodoc:
|
205
203
|
response.to_a
|
206
204
|
end
|
207
205
|
|
@@ -219,10 +217,9 @@ module ActionController
|
|
219
217
|
class << self
|
220
218
|
# Pushes the given Rack middleware and its arguments to the bottom of the
|
221
219
|
# middleware stack.
|
222
|
-
def use(
|
223
|
-
middleware_stack.use(
|
220
|
+
def use(...)
|
221
|
+
middleware_stack.use(...)
|
224
222
|
end
|
225
|
-
ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
|
226
223
|
end
|
227
224
|
|
228
225
|
# Alias for +middleware_stack+.
|
@@ -8,8 +8,11 @@ require "action_controller/railties/helpers"
|
|
8
8
|
require "action_view/railtie"
|
9
9
|
|
10
10
|
module ActionController
|
11
|
-
class Railtie < Rails::Railtie
|
11
|
+
class Railtie < Rails::Railtie # :nodoc:
|
12
12
|
config.action_controller = ActiveSupport::OrderedOptions.new
|
13
|
+
config.action_controller.raise_on_open_redirects = false
|
14
|
+
config.action_controller.log_query_tags_around_actions = true
|
15
|
+
config.action_controller.wrap_parameters_by_default = false
|
13
16
|
|
14
17
|
config.eager_load_namespaces << ActionController
|
15
18
|
|
@@ -25,14 +28,19 @@ module ActionController
|
|
25
28
|
options = app.config.action_controller
|
26
29
|
|
27
30
|
ActiveSupport.on_load(:action_controller, run_once: true) do
|
28
|
-
ActionController::Parameters.permit_all_parameters = options.
|
31
|
+
ActionController::Parameters.permit_all_parameters = options.permit_all_parameters || false
|
29
32
|
if app.config.action_controller[:always_permitted_parameters]
|
30
33
|
ActionController::Parameters.always_permitted_parameters =
|
31
|
-
app.config.action_controller.
|
34
|
+
app.config.action_controller.always_permitted_parameters
|
32
35
|
end
|
33
|
-
|
34
|
-
|
36
|
+
|
37
|
+
action_on_unpermitted_parameters = options.action_on_unpermitted_parameters
|
38
|
+
|
39
|
+
if action_on_unpermitted_parameters.nil?
|
40
|
+
action_on_unpermitted_parameters = (Rails.env.test? || Rails.env.development?) ? :log : false
|
35
41
|
end
|
42
|
+
|
43
|
+
ActionController::Parameters.action_on_unpermitted_parameters = action_on_unpermitted_parameters
|
36
44
|
end
|
37
45
|
end
|
38
46
|
|
@@ -55,7 +63,18 @@ module ActionController
|
|
55
63
|
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
|
56
64
|
extend ::ActionController::Railties::Helpers
|
57
65
|
|
58
|
-
options.
|
66
|
+
wrap_parameters format: [:json] if options.wrap_parameters_by_default && respond_to?(:wrap_parameters)
|
67
|
+
|
68
|
+
# Configs used in other initializers
|
69
|
+
filtered_options = options.except(
|
70
|
+
:log_query_tags_around_actions,
|
71
|
+
:permit_all_parameters,
|
72
|
+
:action_on_unpermitted_parameters,
|
73
|
+
:always_permitted_parameters,
|
74
|
+
:wrap_parameters_by_default
|
75
|
+
)
|
76
|
+
|
77
|
+
filtered_options.each do |k, v|
|
59
78
|
k = "#{k}="
|
60
79
|
if respond_to?(k)
|
61
80
|
send(k, v)
|
@@ -85,5 +104,30 @@ module ActionController
|
|
85
104
|
ActionController::Metal.descendants.each(&:action_methods) if config.eager_load
|
86
105
|
end
|
87
106
|
end
|
107
|
+
|
108
|
+
initializer "action_controller.query_log_tags" do |app|
|
109
|
+
query_logs_tags_enabled = app.config.respond_to?(:active_record) &&
|
110
|
+
app.config.active_record.query_log_tags_enabled &&
|
111
|
+
app.config.action_controller.log_query_tags_around_actions
|
112
|
+
|
113
|
+
if query_logs_tags_enabled
|
114
|
+
app.config.active_record.query_log_tags |= [:controller] unless app.config.active_record.query_log_tags.include?(:namespaced_controller)
|
115
|
+
app.config.active_record.query_log_tags |= [:action]
|
116
|
+
|
117
|
+
ActiveSupport.on_load(:active_record) do
|
118
|
+
ActiveRecord::QueryLogs.taggings.merge!(
|
119
|
+
controller: ->(context) { context[:controller]&.controller_name },
|
120
|
+
action: ->(context) { context[:controller]&.action_name },
|
121
|
+
namespaced_controller: ->(context) { context[:controller].class.name if context[:controller] }
|
122
|
+
)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
initializer "action_controller.test_case" do |app|
|
128
|
+
ActiveSupport.on_load(:action_controller_test_case) do
|
129
|
+
ActionController::TestCase.executor_around_each_request = app.config.active_support.executor_around_test_case
|
130
|
+
end
|
131
|
+
end
|
88
132
|
end
|
89
133
|
end
|
@@ -68,26 +68,7 @@ module ActionController
|
|
68
68
|
@env = normalize_keys defaults, env
|
69
69
|
end
|
70
70
|
|
71
|
-
#
|
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.
|
71
|
+
# Renders a template to a string, just like ActionController::Rendering#render_to_string.
|
91
72
|
def render(*args)
|
92
73
|
raise "missing controller" unless controller
|
93
74
|
|
@@ -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
|
|
@@ -179,7 +179,7 @@ module ActionController
|
|
179
179
|
|
180
180
|
# Methods #destroy and #load! are overridden to avoid calling methods on the
|
181
181
|
# @store object, which does not exist for the TestSession class.
|
182
|
-
class TestSession < Rack::Session::Abstract::PersistedSecure::SecureSessionHash
|
182
|
+
class TestSession < Rack::Session::Abstract::PersistedSecure::SecureSessionHash # :nodoc:
|
183
183
|
DEFAULT_OPTIONS = Rack::Session::Abstract::Persisted::DEFAULT_OPTIONS
|
184
184
|
|
185
185
|
def initialize(session = {})
|
@@ -214,6 +214,10 @@ module ActionController
|
|
214
214
|
@data.fetch(key.to_s, *args, &block)
|
215
215
|
end
|
216
216
|
|
217
|
+
def enabled?
|
218
|
+
true
|
219
|
+
end
|
220
|
+
|
217
221
|
private
|
218
222
|
def load!
|
219
223
|
@id
|
@@ -237,7 +241,7 @@ module ActionController
|
|
237
241
|
# == Basic example
|
238
242
|
#
|
239
243
|
# Functional tests are written as follows:
|
240
|
-
# 1. First, one uses the +get+, +post+, +patch+, +put+, +delete
|
244
|
+
# 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+, or +head+ method to simulate
|
241
245
|
# an HTTP request.
|
242
246
|
# 2. Then, one asserts whether the current state is as expected. "State" can be anything:
|
243
247
|
# the controller's HTTP response, the database contents, etc.
|
@@ -329,6 +333,8 @@ module ActionController
|
|
329
333
|
#
|
330
334
|
# assert_redirected_to page_url(title: 'foo')
|
331
335
|
class TestCase < ActiveSupport::TestCase
|
336
|
+
singleton_class.attr_accessor :executor_around_each_request
|
337
|
+
|
332
338
|
module Behavior
|
333
339
|
extend ActiveSupport::Concern
|
334
340
|
include ActionDispatch::TestProcess
|
@@ -385,7 +391,7 @@ module ActionController
|
|
385
391
|
#
|
386
392
|
# You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
|
387
393
|
# +post+, +patch+, +put+, +delete+, and +head+.
|
388
|
-
# Example sending parameters, session and setting a flash message:
|
394
|
+
# Example sending parameters, session, and setting a flash message:
|
389
395
|
#
|
390
396
|
# get :show,
|
391
397
|
# params: { id: 7 },
|
@@ -455,13 +461,19 @@ module ActionController
|
|
455
461
|
# session: { user_id: 1 },
|
456
462
|
# flash: { notice: 'This is flash message' }
|
457
463
|
#
|
458
|
-
# To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE
|
464
|
+
# To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+, and +HEAD+ requests
|
459
465
|
# prefer using #get, #post, #patch, #put, #delete and #head methods
|
460
466
|
# respectively which will make tests more expressive.
|
461
467
|
#
|
468
|
+
# It's not recommended to make more than one request in the same test. Instance
|
469
|
+
# variables that are set in one request will not persist to the next request,
|
470
|
+
# but it's not guaranteed that all Rails internal state will be reset. Prefer
|
471
|
+
# ActionDispatch::IntegrationTest for making multiple requests in the same test.
|
472
|
+
#
|
462
473
|
# Note that the request method is not verified.
|
463
474
|
def process(action, method: "GET", params: nil, session: nil, body: nil, flash: {}, format: nil, xhr: false, as: nil)
|
464
475
|
check_required_ivars
|
476
|
+
@controller.clear_instance_variables_between_requests
|
465
477
|
|
466
478
|
action = +action.to_s
|
467
479
|
http_method = method.to_s.upcase
|
@@ -574,10 +586,19 @@ module ActionController
|
|
574
586
|
end
|
575
587
|
end
|
576
588
|
|
589
|
+
def wrap_execution(&block)
|
590
|
+
if ActionController::TestCase.executor_around_each_request && defined?(Rails.application) && Rails.application
|
591
|
+
Rails.application.executor.wrap(&block)
|
592
|
+
else
|
593
|
+
yield
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
577
597
|
def process_controller_response(action, cookies, xhr)
|
578
598
|
begin
|
579
599
|
@controller.recycle!
|
580
|
-
|
600
|
+
|
601
|
+
wrap_execution { @controller.dispatch(action, @request, @response) }
|
581
602
|
ensure
|
582
603
|
@request = @controller.request
|
583
604
|
@response = @controller.response
|
@@ -623,7 +644,7 @@ module ActionController
|
|
623
644
|
end
|
624
645
|
|
625
646
|
def check_required_ivars
|
626
|
-
#
|
647
|
+
# Check for required instance variables so we can give an
|
627
648
|
# understandable error message.
|
628
649
|
[:@routes, :@controller, :@request, :@response].each do |iv_name|
|
629
650
|
if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?
|
data/lib/action_controller.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "abstract_controller"
|
4
4
|
require "action_dispatch"
|
5
5
|
require "action_controller/metal/strong_parameters"
|
6
|
+
require "action_controller/metal/exceptions"
|
6
7
|
|
7
8
|
module ActionController
|
8
9
|
extend ActiveSupport::Autoload
|
@@ -18,10 +19,6 @@ module ActionController
|
|
18
19
|
end
|
19
20
|
|
20
21
|
autoload_under "metal" do
|
21
|
-
eager_autoload do
|
22
|
-
autoload :Live
|
23
|
-
end
|
24
|
-
|
25
22
|
autoload :ConditionalGet
|
26
23
|
autoload :ContentSecurityPolicy
|
27
24
|
autoload :Cookies
|
@@ -37,6 +34,7 @@ module ActionController
|
|
37
34
|
autoload :BasicImplicitRender
|
38
35
|
autoload :ImplicitRender
|
39
36
|
autoload :Instrumentation
|
37
|
+
autoload :Live
|
40
38
|
autoload :Logging
|
41
39
|
autoload :MimeResponds
|
42
40
|
autoload :ParamsWrapper
|
@@ -65,5 +63,4 @@ require "active_support/core_ext/module/attribute_accessors"
|
|
65
63
|
require "active_support/core_ext/load_error"
|
66
64
|
require "active_support/core_ext/module/attr_internal"
|
67
65
|
require "active_support/core_ext/name_error"
|
68
|
-
require "active_support/core_ext/uri"
|
69
66
|
require "active_support/inflector"
|
@@ -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
|
@@ -187,13 +187,20 @@ module ActionDispatch
|
|
187
187
|
|
188
188
|
return if control.empty? && cache_control.empty? # Let middleware handle default behavior
|
189
189
|
|
190
|
-
if
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
190
|
+
if cache_control.any?
|
191
|
+
# Any caching directive coming from a controller overrides
|
192
|
+
# no-cache/no-store in the default Cache-Control header.
|
193
|
+
control.delete(:no_cache)
|
194
|
+
control.delete(:no_store)
|
195
195
|
|
196
|
-
|
196
|
+
if extras = control.delete(:extras)
|
197
|
+
cache_control[:extras] ||= []
|
198
|
+
cache_control[:extras] += extras
|
199
|
+
cache_control[:extras].uniq!
|
200
|
+
end
|
201
|
+
|
202
|
+
control.merge! cache_control
|
203
|
+
end
|
197
204
|
|
198
205
|
options = []
|
199
206
|
|
@@ -3,7 +3,24 @@
|
|
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
|
+
# Configures the HTTP
|
8
|
+
# {Content-Security-Policy}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy]
|
9
|
+
# response header to help protect against XSS and injection attacks.
|
10
|
+
#
|
11
|
+
# Example global policy:
|
12
|
+
#
|
13
|
+
# Rails.application.config.content_security_policy do |policy|
|
14
|
+
# policy.default_src :self, :https
|
15
|
+
# policy.font_src :self, :https, :data
|
16
|
+
# policy.img_src :self, :https, :data
|
17
|
+
# policy.object_src :none
|
18
|
+
# policy.script_src :self, :https
|
19
|
+
# policy.style_src :self, :https
|
20
|
+
#
|
21
|
+
# # Specify URI for violation reports
|
22
|
+
# policy.report_uri "/csp-violation-report-endpoint"
|
23
|
+
# end
|
7
24
|
class ContentSecurityPolicy
|
8
25
|
class Middleware
|
9
26
|
CONTENT_TYPE = "Content-Type"
|
@@ -16,7 +33,11 @@ module ActionDispatch #:nodoc:
|
|
16
33
|
|
17
34
|
def call(env)
|
18
35
|
request = ActionDispatch::Request.new env
|
19
|
-
|
36
|
+
status, headers, _ = response = @app.call(env)
|
37
|
+
|
38
|
+
# Returning CSP headers with a 304 Not Modified is harmful, since nonces in the new
|
39
|
+
# CSP headers might not match nonces in the cached HTML.
|
40
|
+
return response if status == 304
|
20
41
|
|
21
42
|
return response if policy_present?(headers)
|
22
43
|
|
@@ -100,43 +121,47 @@ module ActionDispatch #:nodoc:
|
|
100
121
|
end
|
101
122
|
|
102
123
|
MAPPINGS = {
|
103
|
-
self:
|
104
|
-
unsafe_eval:
|
105
|
-
unsafe_inline:
|
106
|
-
none:
|
107
|
-
http:
|
108
|
-
https:
|
109
|
-
data:
|
110
|
-
mediastream:
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
124
|
+
self: "'self'",
|
125
|
+
unsafe_eval: "'unsafe-eval'",
|
126
|
+
unsafe_inline: "'unsafe-inline'",
|
127
|
+
none: "'none'",
|
128
|
+
http: "http:",
|
129
|
+
https: "https:",
|
130
|
+
data: "data:",
|
131
|
+
mediastream: "mediastream:",
|
132
|
+
allow_duplicates: "'allow-duplicates'",
|
133
|
+
blob: "blob:",
|
134
|
+
filesystem: "filesystem:",
|
135
|
+
report_sample: "'report-sample'",
|
136
|
+
script: "'script'",
|
137
|
+
strict_dynamic: "'strict-dynamic'",
|
138
|
+
ws: "ws:",
|
139
|
+
wss: "wss:"
|
117
140
|
}.freeze
|
118
141
|
|
119
142
|
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
|
-
|
143
|
+
base_uri: "base-uri",
|
144
|
+
child_src: "child-src",
|
145
|
+
connect_src: "connect-src",
|
146
|
+
default_src: "default-src",
|
147
|
+
font_src: "font-src",
|
148
|
+
form_action: "form-action",
|
149
|
+
frame_ancestors: "frame-ancestors",
|
150
|
+
frame_src: "frame-src",
|
151
|
+
img_src: "img-src",
|
152
|
+
manifest_src: "manifest-src",
|
153
|
+
media_src: "media-src",
|
154
|
+
object_src: "object-src",
|
155
|
+
prefetch_src: "prefetch-src",
|
156
|
+
require_trusted_types_for: "require-trusted-types-for",
|
157
|
+
script_src: "script-src",
|
158
|
+
script_src_attr: "script-src-attr",
|
159
|
+
script_src_elem: "script-src-elem",
|
160
|
+
style_src: "style-src",
|
161
|
+
style_src_attr: "style-src-attr",
|
162
|
+
style_src_elem: "style-src-elem",
|
163
|
+
trusted_types: "trusted-types",
|
164
|
+
worker_src: "worker-src"
|
140
165
|
}.freeze
|
141
166
|
|
142
167
|
DEFAULT_NONCE_DIRECTIVES = %w[script-src style-src].freeze
|
@@ -164,6 +189,15 @@ module ActionDispatch #:nodoc:
|
|
164
189
|
end
|
165
190
|
end
|
166
191
|
|
192
|
+
# Specify whether to prevent the user agent from loading any assets over
|
193
|
+
# HTTP when the page uses HTTPS:
|
194
|
+
#
|
195
|
+
# policy.block_all_mixed_content
|
196
|
+
#
|
197
|
+
# Pass +false+ to allow it again:
|
198
|
+
#
|
199
|
+
# policy.block_all_mixed_content false
|
200
|
+
#
|
167
201
|
def block_all_mixed_content(enabled = true)
|
168
202
|
if enabled
|
169
203
|
@directives["block-all-mixed-content"] = true
|
@@ -172,6 +206,14 @@ module ActionDispatch #:nodoc:
|
|
172
206
|
end
|
173
207
|
end
|
174
208
|
|
209
|
+
# Restricts the set of plugins that can be embedded:
|
210
|
+
#
|
211
|
+
# policy.plugin_types "application/x-shockwave-flash"
|
212
|
+
#
|
213
|
+
# Leave empty to allow all plugins:
|
214
|
+
#
|
215
|
+
# policy.plugin_types
|
216
|
+
#
|
175
217
|
def plugin_types(*types)
|
176
218
|
if types.first
|
177
219
|
@directives["plugin-types"] = types
|
@@ -180,10 +222,24 @@ module ActionDispatch #:nodoc:
|
|
180
222
|
end
|
181
223
|
end
|
182
224
|
|
225
|
+
# Enable the {report-uri}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri]
|
226
|
+
# directive. Violation reports will be sent to the specified URI:
|
227
|
+
#
|
228
|
+
# policy.report_uri "/csp-violation-report-endpoint"
|
229
|
+
#
|
183
230
|
def report_uri(uri)
|
184
231
|
@directives["report-uri"] = [uri]
|
185
232
|
end
|
186
233
|
|
234
|
+
# Specify asset types for which {Subresource Integrity}[https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity]
|
235
|
+
# is required:
|
236
|
+
#
|
237
|
+
# policy.require_sri_for :script, :style
|
238
|
+
#
|
239
|
+
# Leave empty to not require Subresource Integrity:
|
240
|
+
#
|
241
|
+
# policy.require_sri_for
|
242
|
+
#
|
187
243
|
def require_sri_for(*types)
|
188
244
|
if types.first
|
189
245
|
@directives["require-sri-for"] = types
|
@@ -192,6 +248,19 @@ module ActionDispatch #:nodoc:
|
|
192
248
|
end
|
193
249
|
end
|
194
250
|
|
251
|
+
# Specify whether a {sandbox}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox]
|
252
|
+
# should be enabled for the requested resource:
|
253
|
+
#
|
254
|
+
# policy.sandbox
|
255
|
+
#
|
256
|
+
# Values can be passed as arguments:
|
257
|
+
#
|
258
|
+
# policy.sandbox "allow-scripts", "allow-modals"
|
259
|
+
#
|
260
|
+
# Pass +false+ to disable the sandbox:
|
261
|
+
#
|
262
|
+
# policy.sandbox false
|
263
|
+
#
|
195
264
|
def sandbox(*values)
|
196
265
|
if values.empty?
|
197
266
|
@directives["sandbox"] = true
|
@@ -202,6 +271,14 @@ module ActionDispatch #:nodoc:
|
|
202
271
|
end
|
203
272
|
end
|
204
273
|
|
274
|
+
# Specify whether user agents should treat any assets over HTTP as HTTPS:
|
275
|
+
#
|
276
|
+
# policy.upgrade_insecure_requests
|
277
|
+
#
|
278
|
+
# Pass +false+ to disable it:
|
279
|
+
#
|
280
|
+
# policy.upgrade_insecure_requests false
|
281
|
+
#
|
205
282
|
def upgrade_insecure_requests(enabled = true)
|
206
283
|
if enabled
|
207
284
|
@directives["upgrade-insecure-requests"] = true
|
@@ -4,28 +4,13 @@ require "active_support/parameter_filter"
|
|
4
4
|
|
5
5
|
module ActionDispatch
|
6
6
|
module Http
|
7
|
-
# Allows you to specify sensitive
|
8
|
-
# the request log
|
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.
|
14
|
-
#
|
15
|
-
# env["action_dispatch.parameter_filter"] = [:password]
|
16
|
-
# => replaces the value to all keys matching /password/i with "[FILTERED]"
|
7
|
+
# Allows you to specify sensitive query string and POST parameters to filter
|
8
|
+
# from the request log.
|
17
9
|
#
|
10
|
+
# # Replaces values with "[FILTERED]" for keys that match /foo|bar/i.
|
18
11
|
# env["action_dispatch.parameter_filter"] = [:foo, "bar"]
|
19
|
-
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
|
20
|
-
#
|
21
|
-
# env["action_dispatch.parameter_filter"] = [ "credit_card.code" ]
|
22
|
-
# => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
|
23
|
-
# change { file: { code: "xxxx"} }
|
24
12
|
#
|
25
|
-
#
|
26
|
-
# v.reverse! if k.match?(/secret/i)
|
27
|
-
# end
|
28
|
-
# => reverses the value to all keys matching /secret/i
|
13
|
+
# For more information about filter behavior, see ActiveSupport::ParameterFilter.
|
29
14
|
module FilterParameters
|
30
15
|
ENV_MATCH = [/RAW_POST_DATA/, "rack.request.form_vars"] # :nodoc:
|
31
16
|
NULL_PARAM_FILTER = ActiveSupport::ParameterFilter.new # :nodoc:
|
@@ -65,7 +65,7 @@ module ActionDispatch
|
|
65
65
|
@req.set_header env_name(key), value
|
66
66
|
end
|
67
67
|
|
68
|
-
# Add a value to a multivalued header like Vary or Accept-Encoding
|
68
|
+
# Add a value to a multivalued header like +Vary+ or +Accept-Encoding+.
|
69
69
|
def add(key, value)
|
70
70
|
@req.add_header env_name(key), value
|
71
71
|
end
|