actionpack 5.2.4.4 → 6.1.1
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 +264 -322
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/abstract_controller.rb +1 -0
- data/lib/abstract_controller/base.rb +38 -4
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/caching/fragments.rb +6 -22
- data/lib/abstract_controller/callbacks.rb +14 -2
- data/lib/abstract_controller/collector.rb +1 -2
- data/lib/abstract_controller/helpers.rb +106 -90
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +11 -5
- data/lib/action_controller.rb +7 -4
- 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.rb +10 -8
- 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 +3 -5
- 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 +24 -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 +30 -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 +31 -22
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +6 -6
- 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 +62 -34
- 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 +167 -58
- data/lib/action_controller/metal/url_for.rb +1 -1
- 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 +70 -65
- data/lib/action_dispatch.rb +9 -3
- data/lib/action_dispatch/http/cache.rb +26 -21
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +33 -19
- 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 +42 -23
- 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.rb +0 -2
- data/lib/action_dispatch/journey/formatter.rb +54 -30
- 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.rb +26 -34
- data/lib/action_dispatch/journey/router/utils.rb +14 -12
- data/lib/action_dispatch/journey/routes.rb +0 -2
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +128 -109
- 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 +121 -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 +3 -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 +3 -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 +10 -9
- data/lib/action_dispatch/request/utils.rb +26 -2
- data/lib/action_dispatch/routing.rb +21 -20
- 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 +3 -3
- data/lib/action_dispatch/routing/route_set.rb +71 -69
- data/lib/action_dispatch/routing/url_for.rb +2 -2
- data/lib/action_dispatch/system_test_case.rb +54 -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.rb +1 -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/integration.rb +61 -28
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +29 -4
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/gem_version.rb +4 -4
- metadata +38 -26
- 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
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController
|
4
|
+
module Logging
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# Set a different log level per request.
|
9
|
+
#
|
10
|
+
# # Use the debug log level if a particular cookie is set.
|
11
|
+
# class ApplicationController < ActionController::Base
|
12
|
+
# log_at :debug, if: -> { cookies[:debug] }
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
def log_at(level, **options)
|
16
|
+
around_action ->(_, action) { logger.log_at(level, &action) }, **options
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -11,7 +11,7 @@ module ActionController #:nodoc:
|
|
11
11
|
# @people = Person.all
|
12
12
|
# end
|
13
13
|
#
|
14
|
-
# That action implicitly responds to all formats, but formats can also be
|
14
|
+
# That action implicitly responds to all formats, but formats can also be explicitly enumerated:
|
15
15
|
#
|
16
16
|
# def index
|
17
17
|
# @people = Person.all
|
@@ -105,7 +105,7 @@ module ActionController #:nodoc:
|
|
105
105
|
#
|
106
106
|
# Mime::Type.register "image/jpg", :jpg
|
107
107
|
#
|
108
|
-
#
|
108
|
+
# +respond_to+ also allows you to specify a common block for different formats by using +any+:
|
109
109
|
#
|
110
110
|
# def index
|
111
111
|
# @people = Person.all
|
@@ -124,6 +124,14 @@ module ActionController #:nodoc:
|
|
124
124
|
#
|
125
125
|
# render json: @people
|
126
126
|
#
|
127
|
+
# +any+ can also be used with no arguments, in which case it will be used for any format requested by
|
128
|
+
# the user:
|
129
|
+
#
|
130
|
+
# respond_to do |format|
|
131
|
+
# format.html
|
132
|
+
# format.any { redirect_to support_path }
|
133
|
+
# end
|
134
|
+
#
|
127
135
|
# Formats can have different variants.
|
128
136
|
#
|
129
137
|
# The request variant is a specialization of the request format, like <tt>:tablet</tt>,
|
@@ -134,7 +142,7 @@ module ActionController #:nodoc:
|
|
134
142
|
#
|
135
143
|
# You can set the variant in a +before_action+:
|
136
144
|
#
|
137
|
-
# request.variant = :tablet if request.user_agent
|
145
|
+
# request.variant = :tablet if /iPad/.match?(request.user_agent)
|
138
146
|
#
|
139
147
|
# Respond to variants in the action just like you respond to formats:
|
140
148
|
#
|
@@ -197,8 +205,11 @@ module ActionController #:nodoc:
|
|
197
205
|
yield collector if block_given?
|
198
206
|
|
199
207
|
if format = collector.negotiate_format(request)
|
208
|
+
if media_type && media_type != format
|
209
|
+
raise ActionController::RespondToMismatchError
|
210
|
+
end
|
200
211
|
_process_format(format)
|
201
|
-
_set_rendered_content_type
|
212
|
+
_set_rendered_content_type(format) unless collector.any_response?
|
202
213
|
response = collector.response
|
203
214
|
response.call if response
|
204
215
|
else
|
@@ -257,6 +268,10 @@ module ActionController #:nodoc:
|
|
257
268
|
end
|
258
269
|
end
|
259
270
|
|
271
|
+
def any_response?
|
272
|
+
!@responses.fetch(format, false) && @responses[Mime::ALL]
|
273
|
+
end
|
274
|
+
|
260
275
|
def response
|
261
276
|
response = @responses.fetch(format, @responses[Mime::ALL])
|
262
277
|
if response.is_a?(VariantCollector) # `format.html.phone` - variant inline syntax
|
@@ -12,11 +12,13 @@ module ActionController
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def setup_param_encode # :nodoc:
|
15
|
-
@_parameter_encodings = {}
|
15
|
+
@_parameter_encodings = Hash.new { |h, k| h[k] = {} }
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
@_parameter_encodings
|
18
|
+
def action_encoding_template(action) # :nodoc:
|
19
|
+
if @_parameter_encodings.has_key?(action.to_s)
|
20
|
+
@_parameter_encodings[action.to_s]
|
21
|
+
end
|
20
22
|
end
|
21
23
|
|
22
24
|
# Specify that a given action's parameters should all be encoded as
|
@@ -44,7 +46,36 @@ module ActionController
|
|
44
46
|
# encoded as ASCII-8BIT. This is useful in the case where an application
|
45
47
|
# must handle data but encoding of the data is unknown, like file system data.
|
46
48
|
def skip_parameter_encoding(action)
|
47
|
-
@_parameter_encodings[action.to_s] =
|
49
|
+
@_parameter_encodings[action.to_s] = Hash.new { Encoding::ASCII_8BIT }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Specify the encoding for a parameter on an action.
|
53
|
+
# If not specified the default is UTF-8.
|
54
|
+
#
|
55
|
+
# You can specify a binary (ASCII_8BIT) parameter with:
|
56
|
+
#
|
57
|
+
# class RepositoryController < ActionController::Base
|
58
|
+
# # This specifies that file_path is not UTF-8 and is instead ASCII_8BIT
|
59
|
+
# param_encoding :show, :file_path, Encoding::ASCII_8BIT
|
60
|
+
#
|
61
|
+
# def show
|
62
|
+
# @repo = Repository.find_by_filesystem_path params[:file_path]
|
63
|
+
#
|
64
|
+
# # params[:repo_name] remains UTF-8 encoded
|
65
|
+
# @repo_name = params[:repo_name]
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# def index
|
69
|
+
# @repositories = Repository.all
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# The file_path parameter on the show action would be encoded as ASCII-8BIT,
|
74
|
+
# but all other arguments will remain UTF-8 encoded.
|
75
|
+
# This is useful in the case where an application must handle data
|
76
|
+
# but encoding of the data is unknown, like file system data.
|
77
|
+
def param_encoding(action, param, encoding)
|
78
|
+
@_parameter_encodings[action.to_s][param.to_s] = encoding
|
48
79
|
end
|
49
80
|
end
|
50
81
|
end
|
@@ -107,15 +107,19 @@ module ActionController
|
|
107
107
|
|
108
108
|
unless super || exclude
|
109
109
|
if m.respond_to?(:attribute_names) && m.attribute_names.any?
|
110
|
+
self.include = m.attribute_names
|
111
|
+
|
110
112
|
if m.respond_to?(:stored_attributes) && !m.stored_attributes.empty?
|
111
|
-
self.include
|
112
|
-
|
113
|
-
|
113
|
+
self.include += m.stored_attributes.values.flatten.map(&:to_s)
|
114
|
+
end
|
115
|
+
|
116
|
+
if m.respond_to?(:attribute_aliases) && m.attribute_aliases.any?
|
117
|
+
self.include += m.attribute_aliases.keys
|
114
118
|
end
|
115
119
|
|
116
120
|
if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any?
|
117
121
|
self.include += m.nested_attributes_options.keys.map do |key|
|
118
|
-
key.to_s.
|
122
|
+
(+key.to_s).concat("_attributes")
|
119
123
|
end
|
120
124
|
end
|
121
125
|
|
@@ -151,7 +155,7 @@ module ActionController
|
|
151
155
|
# try to find Foo::Bar::User, Foo::User and finally User.
|
152
156
|
def _default_wrap_model
|
153
157
|
return nil if klass.anonymous?
|
154
|
-
model_name = klass.name.
|
158
|
+
model_name = klass.name.delete_suffix("Controller").classify
|
155
159
|
|
156
160
|
begin
|
157
161
|
if model_klass = model_name.safe_constantize
|
@@ -189,7 +193,7 @@ module ActionController
|
|
189
193
|
#
|
190
194
|
# wrap_parameters Person
|
191
195
|
# # wraps parameters by determining the wrapper key from Person class
|
192
|
-
# (+person+, in this case) and the list of attribute names
|
196
|
+
# # (+person+, in this case) and the list of attribute names
|
193
197
|
#
|
194
198
|
# wrap_parameters include: [:username, :title]
|
195
199
|
# # wraps only +:username+ and +:title+ attributes from parameters.
|
@@ -240,24 +244,12 @@ module ActionController
|
|
240
244
|
|
241
245
|
# Performs parameters wrapping upon the request. Called automatically
|
242
246
|
# by the metal call stack.
|
243
|
-
def process_action(*
|
244
|
-
if _wrapper_enabled?
|
245
|
-
wrapped_hash = _wrap_parameters request.request_parameters
|
246
|
-
wrapped_keys = request.request_parameters.keys
|
247
|
-
wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
|
248
|
-
|
249
|
-
# This will make the wrapped hash accessible from controller and view.
|
250
|
-
request.parameters.merge! wrapped_hash
|
251
|
-
request.request_parameters.merge! wrapped_hash
|
252
|
-
|
253
|
-
# This will display the wrapped hash in the log file.
|
254
|
-
request.filtered_parameters.merge! wrapped_filtered_hash
|
255
|
-
end
|
247
|
+
def process_action(*)
|
248
|
+
_perform_parameter_wrapping if _wrapper_enabled?
|
256
249
|
super
|
257
250
|
end
|
258
251
|
|
259
252
|
private
|
260
|
-
|
261
253
|
# Returns the wrapper key which will be used to store wrapped parameters.
|
262
254
|
def _wrapper_key
|
263
255
|
_wrapper_options.name
|
@@ -276,9 +268,11 @@ module ActionController
|
|
276
268
|
def _extract_parameters(parameters)
|
277
269
|
if include_only = _wrapper_options.include
|
278
270
|
parameters.slice(*include_only)
|
271
|
+
elsif _wrapper_options.exclude
|
272
|
+
exclude = _wrapper_options.exclude + EXCLUDE_PARAMETERS
|
273
|
+
parameters.except(*exclude)
|
279
274
|
else
|
280
|
-
|
281
|
-
parameters.except(*(exclude + EXCLUDE_PARAMETERS))
|
275
|
+
parameters.except(*EXCLUDE_PARAMETERS)
|
282
276
|
end
|
283
277
|
end
|
284
278
|
|
@@ -289,5 +283,20 @@ module ActionController
|
|
289
283
|
ref = request.content_mime_type.ref
|
290
284
|
_wrapper_formats.include?(ref) && _wrapper_key && !request.parameters.key?(_wrapper_key)
|
291
285
|
end
|
286
|
+
|
287
|
+
def _perform_parameter_wrapping
|
288
|
+
wrapped_hash = _wrap_parameters request.request_parameters
|
289
|
+
wrapped_keys = request.request_parameters.keys
|
290
|
+
wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
|
291
|
+
|
292
|
+
# This will make the wrapped hash accessible from controller and view.
|
293
|
+
request.parameters.merge! wrapped_hash
|
294
|
+
request.request_parameters.merge! wrapped_hash
|
295
|
+
|
296
|
+
# This will display the wrapped hash in the log file.
|
297
|
+
request.filtered_parameters.merge! wrapped_filtered_hash
|
298
|
+
rescue ActionDispatch::Http::Parameters::ParseError
|
299
|
+
# swallow parse error exception
|
300
|
+
end
|
292
301
|
end
|
293
302
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController #:nodoc:
|
4
|
+
# HTTP Permissions Policy is a web standard for defining a mechanism to
|
5
|
+
# allow and deny the use of browser permissions in its own context, and
|
6
|
+
# in content within any <iframe> elements in the document.
|
7
|
+
#
|
8
|
+
# Full details of HTTP Permissions Policy specification and guidelines can
|
9
|
+
# be found at MDN:
|
10
|
+
#
|
11
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy
|
12
|
+
#
|
13
|
+
# Examples of usage:
|
14
|
+
#
|
15
|
+
# # Global policy
|
16
|
+
# Rails.application.config.permissions_policy do |f|
|
17
|
+
# f.camera :none
|
18
|
+
# f.gyroscope :none
|
19
|
+
# f.microphone :none
|
20
|
+
# f.usb :none
|
21
|
+
# f.fullscreen :self
|
22
|
+
# f.payment :self, "https://secure.example.com"
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# # Controller level policy
|
26
|
+
# class PagesController < ApplicationController
|
27
|
+
# permissions_policy do |p|
|
28
|
+
# p.geolocation "https://example.com"
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
module PermissionsPolicy
|
32
|
+
extend ActiveSupport::Concern
|
33
|
+
|
34
|
+
module ClassMethods
|
35
|
+
def permissions_policy(**options, &block)
|
36
|
+
before_action(options) do
|
37
|
+
if block_given?
|
38
|
+
policy = request.permissions_policy.clone
|
39
|
+
yield policy
|
40
|
+
request.permissions_policy = policy
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -55,11 +55,11 @@ module ActionController
|
|
55
55
|
# Statements after +redirect_to+ in our controller get executed, so +redirect_to+ doesn't stop the execution of the function.
|
56
56
|
# To terminate the execution of the function immediately after the +redirect_to+, use return.
|
57
57
|
# redirect_to post_url(@post) and return
|
58
|
-
def redirect_to(options = {},
|
58
|
+
def redirect_to(options = {}, response_options = {})
|
59
59
|
raise ActionControllerError.new("Cannot redirect to nil!") unless options
|
60
60
|
raise AbstractController::DoubleRenderError if response_body
|
61
61
|
|
62
|
-
self.status = _extract_redirect_to_status(options,
|
62
|
+
self.status = _extract_redirect_to_status(options, response_options)
|
63
63
|
self.location = _compute_redirect_to_location(request, options)
|
64
64
|
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
|
65
65
|
end
|
@@ -85,7 +85,7 @@ module ActionController
|
|
85
85
|
# * <tt>:fallback_location</tt> - The default fallback location that will be used on missing +Referer+ header.
|
86
86
|
# * <tt>:allow_other_host</tt> - Allow or disallow redirection to the host that is different to the current host, defaults to true.
|
87
87
|
#
|
88
|
-
# All other options that can be passed to
|
88
|
+
# All other options that can be passed to #redirect_to are accepted as
|
89
89
|
# options and the behavior is identical.
|
90
90
|
def redirect_back(fallback_location:, allow_other_host: true, **args)
|
91
91
|
referer = request.headers["Referer"]
|
@@ -114,11 +114,11 @@ module ActionController
|
|
114
114
|
public :_compute_redirect_to_location
|
115
115
|
|
116
116
|
private
|
117
|
-
def _extract_redirect_to_status(options,
|
117
|
+
def _extract_redirect_to_status(options, response_options)
|
118
118
|
if options.is_a?(Hash) && options.key?(:status)
|
119
119
|
Rack::Utils.status_code(options.delete(:status))
|
120
|
-
elsif
|
121
|
-
Rack::Utils.status_code(
|
120
|
+
elsif response_options.key?(:status)
|
121
|
+
Rack::Utils.status_code(response_options[:status])
|
122
122
|
else
|
123
123
|
302
|
124
124
|
end
|
@@ -157,24 +157,24 @@ module ActionController
|
|
157
157
|
json = json.to_json(options) unless json.kind_of?(String)
|
158
158
|
|
159
159
|
if options[:callback].present?
|
160
|
-
if
|
160
|
+
if media_type.nil? || media_type == Mime[:json]
|
161
161
|
self.content_type = Mime[:js]
|
162
162
|
end
|
163
163
|
|
164
164
|
"/**/#{options[:callback]}(#{json})"
|
165
165
|
else
|
166
|
-
self.content_type
|
166
|
+
self.content_type = Mime[:json] if media_type.nil?
|
167
167
|
json
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
171
171
|
add :js do |js, options|
|
172
|
-
self.content_type
|
172
|
+
self.content_type = Mime[:js] if media_type.nil?
|
173
173
|
js.respond_to?(:to_js) ? js.to_js(options) : js
|
174
174
|
end
|
175
175
|
|
176
176
|
add :xml do |xml, options|
|
177
|
-
self.content_type
|
177
|
+
self.content_type = Mime[:xml] if media_type.nil?
|
178
178
|
xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
|
179
179
|
end
|
180
180
|
end
|
@@ -40,7 +40,7 @@ module ActionController
|
|
40
40
|
def render_to_string(*)
|
41
41
|
result = super
|
42
42
|
if result.respond_to?(:each)
|
43
|
-
string = ""
|
43
|
+
string = +""
|
44
44
|
result.each { |r| string << r }
|
45
45
|
string
|
46
46
|
else
|
@@ -53,7 +53,6 @@ module ActionController
|
|
53
53
|
end
|
54
54
|
|
55
55
|
private
|
56
|
-
|
57
56
|
def _process_variant(options)
|
58
57
|
if defined?(request) && !request.nil? && request.variant.present?
|
59
58
|
options[:variant] = request.variant
|
@@ -73,11 +72,17 @@ module ActionController
|
|
73
72
|
end
|
74
73
|
|
75
74
|
def _set_rendered_content_type(format)
|
76
|
-
if format && !response.
|
75
|
+
if format && !response.media_type
|
77
76
|
self.content_type = format.to_s
|
78
77
|
end
|
79
78
|
end
|
80
79
|
|
80
|
+
def _set_vary_header
|
81
|
+
if self.headers["Vary"].blank? && request.should_apply_vary_header?
|
82
|
+
self.headers["Vary"] = "Accept"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
81
86
|
# Normalize arguments by catching blocks and setting them on :update.
|
82
87
|
def _normalize_args(action = nil, options = {}, &blk)
|
83
88
|
options = super
|
@@ -3,7 +3,6 @@
|
|
3
3
|
require "rack/session/abstract/id"
|
4
4
|
require "action_controller/metal/exceptions"
|
5
5
|
require "active_support/security_utils"
|
6
|
-
require "active_support/core_ext/string/strip"
|
7
6
|
|
8
7
|
module ActionController #:nodoc:
|
9
8
|
class InvalidAuthenticityToken < ActionControllerError #:nodoc:
|
@@ -18,7 +17,7 @@ module ActionController #:nodoc:
|
|
18
17
|
# access. When a request reaches your application, \Rails verifies the received
|
19
18
|
# token with the token in the session. All requests are checked except GET requests
|
20
19
|
# as these should be idempotent. Keep in mind that all session-oriented requests
|
21
|
-
#
|
20
|
+
# are CSRF protected by default, including JavaScript and HTML requests.
|
22
21
|
#
|
23
22
|
# Since HTML and JavaScript requests are typically made from the browser, we
|
24
23
|
# need to ensure to verify request authenticity for the web browser. We can
|
@@ -31,31 +30,30 @@ module ActionController #:nodoc:
|
|
31
30
|
# URL on your site. When your JavaScript response loads on their site, it executes.
|
32
31
|
# With carefully crafted JavaScript on their end, sensitive data in your JavaScript
|
33
32
|
# response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or
|
34
|
-
# Ajax) requests are allowed to make
|
33
|
+
# Ajax) requests are allowed to make requests for JavaScript responses.
|
35
34
|
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# <tt>
|
35
|
+
# Subclasses of <tt>ActionController::Base</tt> are protected by default with the
|
36
|
+
# <tt>:exception</tt> strategy, which raises an
|
37
|
+
# <tt>ActionController::InvalidAuthenticityToken</tt> error on unverified requests.
|
38
|
+
#
|
39
|
+
# APIs may want to disable this behavior since they are typically designed to be
|
40
|
+
# state-less: that is, the request API client handles the session instead of Rails.
|
41
|
+
# One way to achieve this is to use the <tt>:null_session</tt> strategy instead,
|
42
|
+
# which allows unverified requests to be handled, but with an empty session:
|
39
43
|
#
|
40
44
|
# class ApplicationController < ActionController::Base
|
41
|
-
# protect_from_forgery
|
45
|
+
# protect_from_forgery with: :null_session
|
42
46
|
# end
|
43
47
|
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
# <tt>:null_session</tt> method, which provides an empty session
|
47
|
-
# during request.
|
48
|
-
#
|
49
|
-
# We may want to disable CSRF protection for APIs since they are typically
|
50
|
-
# designed to be state-less. That is, the request API client will handle
|
51
|
-
# the session for you instead of Rails.
|
48
|
+
# Note that API only applications don't include this module or a session middleware
|
49
|
+
# by default, and so don't require CSRF protection to be configured.
|
52
50
|
#
|
53
51
|
# The token parameter is named <tt>authenticity_token</tt> by default. The name and
|
54
52
|
# value of this token must be added to every layout that renders forms by including
|
55
53
|
# <tt>csrf_meta_tags</tt> in the HTML +head+.
|
56
54
|
#
|
57
55
|
# Learn more about CSRF attacks and securing your application in the
|
58
|
-
# {Ruby on Rails Security Guide}[
|
56
|
+
# {Ruby on Rails Security Guide}[https://guides.rubyonrails.org/security.html].
|
59
57
|
module RequestForgeryProtection
|
60
58
|
extend ActiveSupport::Concern
|
61
59
|
|
@@ -92,6 +90,10 @@ module ActionController #:nodoc:
|
|
92
90
|
config_accessor :default_protect_from_forgery
|
93
91
|
self.default_protect_from_forgery = false
|
94
92
|
|
93
|
+
# Controls whether URL-safe CSRF tokens are generated.
|
94
|
+
config_accessor :urlsafe_csrf_tokens, instance_writer: false
|
95
|
+
self.urlsafe_csrf_tokens = false
|
96
|
+
|
95
97
|
helper_method :form_authenticity_token
|
96
98
|
helper_method :protect_against_forgery?
|
97
99
|
end
|
@@ -145,7 +147,6 @@ module ActionController #:nodoc:
|
|
145
147
|
end
|
146
148
|
|
147
149
|
private
|
148
|
-
|
149
150
|
def protection_method_class(name)
|
150
151
|
ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
|
151
152
|
rescue NameError
|
@@ -169,7 +170,6 @@ module ActionController #:nodoc:
|
|
169
170
|
end
|
170
171
|
|
171
172
|
private
|
172
|
-
|
173
173
|
class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
|
174
174
|
def initialize(req)
|
175
175
|
super(nil, req)
|
@@ -276,7 +276,7 @@ module ActionController #:nodoc:
|
|
276
276
|
|
277
277
|
# Check for cross-origin JavaScript responses.
|
278
278
|
def non_xhr_javascript_response? # :doc:
|
279
|
-
|
279
|
+
%r(\A(?:text|application)/javascript).match?(media_type) && !request.xhr?
|
280
280
|
end
|
281
281
|
|
282
282
|
AUTHENTICITY_TOKEN_LENGTH = 32
|
@@ -321,11 +321,6 @@ module ActionController #:nodoc:
|
|
321
321
|
global_csrf_token(session)
|
322
322
|
end
|
323
323
|
|
324
|
-
one_time_pad = SecureRandom.random_bytes(AUTHENTICITY_TOKEN_LENGTH)
|
325
|
-
encrypted_csrf_token = xor_byte_strings(one_time_pad, raw_token)
|
326
|
-
masked_token = one_time_pad + encrypted_csrf_token
|
327
|
-
Base64.urlsafe_encode64(masked_token, padding: false)
|
328
|
-
|
329
324
|
mask_token(raw_token)
|
330
325
|
end
|
331
326
|
|
@@ -338,7 +333,7 @@ module ActionController #:nodoc:
|
|
338
333
|
end
|
339
334
|
|
340
335
|
begin
|
341
|
-
masked_token =
|
336
|
+
masked_token = decode_csrf_token(encoded_masked_token)
|
342
337
|
rescue ArgumentError # encoded_masked_token is invalid Base64
|
343
338
|
return false
|
344
339
|
end
|
@@ -376,7 +371,7 @@ module ActionController #:nodoc:
|
|
376
371
|
one_time_pad = SecureRandom.random_bytes(AUTHENTICITY_TOKEN_LENGTH)
|
377
372
|
encrypted_csrf_token = xor_byte_strings(one_time_pad, raw_token)
|
378
373
|
masked_token = one_time_pad + encrypted_csrf_token
|
379
|
-
|
374
|
+
encode_csrf_token(masked_token)
|
380
375
|
end
|
381
376
|
|
382
377
|
def compare_with_real_token(token, session) # :doc:
|
@@ -391,7 +386,7 @@ module ActionController #:nodoc:
|
|
391
386
|
if per_form_csrf_tokens
|
392
387
|
correct_token = per_form_csrf_token(
|
393
388
|
session,
|
394
|
-
|
389
|
+
request.path.chomp("/"),
|
395
390
|
request.request_method
|
396
391
|
)
|
397
392
|
|
@@ -402,8 +397,8 @@ module ActionController #:nodoc:
|
|
402
397
|
end
|
403
398
|
|
404
399
|
def real_csrf_token(session) # :doc:
|
405
|
-
session[:_csrf_token] ||=
|
406
|
-
|
400
|
+
session[:_csrf_token] ||= generate_csrf_token
|
401
|
+
decode_csrf_token(session[:_csrf_token])
|
407
402
|
end
|
408
403
|
|
409
404
|
def per_form_csrf_token(session, action_path, method) # :doc:
|
@@ -426,9 +421,14 @@ module ActionController #:nodoc:
|
|
426
421
|
end
|
427
422
|
|
428
423
|
def xor_byte_strings(s1, s2) # :doc:
|
429
|
-
|
430
|
-
s1.
|
431
|
-
|
424
|
+
s2 = s2.dup
|
425
|
+
size = s1.bytesize
|
426
|
+
i = 0
|
427
|
+
while i < size
|
428
|
+
s2.setbyte(i, s1.getbyte(i) ^ s2.getbyte(i))
|
429
|
+
i += 1
|
430
|
+
end
|
431
|
+
s2
|
432
432
|
end
|
433
433
|
|
434
434
|
# The form's authenticity parameter. Override to provide your own.
|
@@ -441,11 +441,11 @@ module ActionController #:nodoc:
|
|
441
441
|
allow_forgery_protection
|
442
442
|
end
|
443
443
|
|
444
|
-
NULL_ORIGIN_MESSAGE =
|
444
|
+
NULL_ORIGIN_MESSAGE = <<~MSG
|
445
445
|
The browser returned a 'null' origin for a request with origin-based forgery protection turned on. This usually
|
446
446
|
means you have the 'no-referrer' Referrer-Policy header enabled, or that the request came from a site that
|
447
447
|
refused to give its origin. This makes it impossible for Rails to verify the source of the requests. Likely the
|
448
|
-
best solution is to change your referrer policy to something less strict like same-origin or strict-
|
448
|
+
best solution is to change your referrer policy to something less strict like same-origin or strict-origin.
|
449
449
|
If you cannot change the referrer policy, you can disable origin checking with the
|
450
450
|
Rails.application.config.action_controller.forgery_protection_origin_check setting.
|
451
451
|
MSG
|
@@ -466,5 +466,33 @@ module ActionController #:nodoc:
|
|
466
466
|
uri = URI.parse(action_path)
|
467
467
|
uri.path.chomp("/")
|
468
468
|
end
|
469
|
+
|
470
|
+
def generate_csrf_token # :nodoc:
|
471
|
+
if urlsafe_csrf_tokens
|
472
|
+
SecureRandom.urlsafe_base64(AUTHENTICITY_TOKEN_LENGTH, padding: false)
|
473
|
+
else
|
474
|
+
SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
def encode_csrf_token(csrf_token) # :nodoc:
|
479
|
+
if urlsafe_csrf_tokens
|
480
|
+
Base64.urlsafe_encode64(csrf_token, padding: false)
|
481
|
+
else
|
482
|
+
Base64.strict_encode64(csrf_token)
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
def decode_csrf_token(encoded_csrf_token) # :nodoc:
|
487
|
+
if urlsafe_csrf_tokens
|
488
|
+
Base64.urlsafe_decode64(encoded_csrf_token)
|
489
|
+
else
|
490
|
+
begin
|
491
|
+
Base64.strict_decode64(encoded_csrf_token)
|
492
|
+
rescue ArgumentError
|
493
|
+
Base64.urlsafe_decode64(encoded_csrf_token)
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
469
497
|
end
|
470
498
|
end
|