actionpack 7.0.4 → 7.1.3.4
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 +397 -269
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/abstract_controller/base.rb +20 -11
- data/lib/abstract_controller/caching/fragments.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +31 -6
- data/lib/abstract_controller/deprecator.rb +7 -0
- data/lib/abstract_controller/helpers.rb +75 -28
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -16
- data/lib/abstract_controller/rendering.rb +12 -14
- data/lib/abstract_controller/translation.rb +9 -6
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +6 -0
- data/lib/action_controller/api.rb +6 -4
- data/lib/action_controller/base.rb +3 -17
- data/lib/action_controller/caching.rb +2 -0
- data/lib/action_controller/deprecator.rb +7 -0
- data/lib/action_controller/form_builder.rb +2 -0
- data/lib/action_controller/log_subscriber.rb +16 -4
- data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +121 -123
- data/lib/action_controller/metal/content_security_policy.rb +5 -5
- data/lib/action_controller/metal/data_streaming.rb +20 -18
- 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 +8 -0
- data/lib/action_controller/metal/head.rb +9 -7
- data/lib/action_controller/metal/helpers.rb +3 -14
- data/lib/action_controller/metal/http_authentication.rb +17 -8
- data/lib/action_controller/metal/implicit_render.rb +5 -3
- data/lib/action_controller/metal/instrumentation.rb +8 -1
- data/lib/action_controller/metal/live.rb +25 -1
- data/lib/action_controller/metal/mime_responds.rb +2 -2
- data/lib/action_controller/metal/params_wrapper.rb +4 -2
- data/lib/action_controller/metal/permissions_policy.rb +2 -2
- data/lib/action_controller/metal/redirecting.rb +29 -8
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +114 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +144 -53
- data/lib/action_controller/metal/rescue.rb +6 -3
- data/lib/action_controller/metal/streaming.rb +71 -31
- data/lib/action_controller/metal/strong_parameters.rb +158 -101
- data/lib/action_controller/metal/url_for.rb +9 -4
- data/lib/action_controller/metal.rb +79 -21
- data/lib/action_controller/railtie.rb +24 -10
- data/lib/action_controller/renderer.rb +99 -85
- data/lib/action_controller/test_case.rb +15 -5
- data/lib/action_controller.rb +8 -1
- data/lib/action_dispatch/constants.rb +32 -0
- data/lib/action_dispatch/deprecator.rb +7 -0
- data/lib/action_dispatch/http/cache.rb +9 -11
- data/lib/action_dispatch/http/content_security_policy.rb +14 -9
- data/lib/action_dispatch/http/filter_parameters.rb +14 -28
- data/lib/action_dispatch/http/headers.rb +3 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +22 -22
- data/lib/action_dispatch/http/mime_type.rb +35 -12
- data/lib/action_dispatch/http/mime_types.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +1 -1
- data/lib/action_dispatch/http/permissions_policy.rb +38 -23
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +63 -30
- data/lib/action_dispatch/http/response.rb +80 -63
- data/lib/action_dispatch/http/upload.rb +15 -2
- data/lib/action_dispatch/journey/formatter.rb +8 -2
- data/lib/action_dispatch/journey/path/pattern.rb +14 -14
- data/lib/action_dispatch/journey/route.rb +3 -2
- data/lib/action_dispatch/journey/router.rb +9 -8
- data/lib/action_dispatch/journey/routes.rb +2 -2
- data/lib/action_dispatch/log_subscriber.rb +23 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -6
- 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 +108 -117
- data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -25
- data/lib/action_dispatch/middleware/debug_locks.rb +4 -1
- data/lib/action_dispatch/middleware/debug_view.rb +7 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +186 -27
- data/lib/action_dispatch/middleware/executor.rb +1 -1
- data/lib/action_dispatch/middleware/flash.rb +7 -0
- data/lib/action_dispatch/middleware/host_authorization.rb +18 -8
- 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 +21 -20
- data/lib/action_dispatch/middleware/request_id.rb +4 -2
- data/lib/action_dispatch/middleware/server_timing.rb +4 -4
- data/lib/action_dispatch/middleware/session/abstract_store.rb +5 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -5
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +25 -18
- data/lib/action_dispatch/middleware/ssl.rb +18 -6
- data/lib/action_dispatch/middleware/stack.rb +7 -2
- data/lib/action_dispatch/middleware/static.rb +14 -10
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -3
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- 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 +59 -41
- data/lib/action_dispatch/railtie.rb +14 -4
- data/lib/action_dispatch/request/session.rb +16 -6
- data/lib/action_dispatch/request/utils.rb +8 -3
- data/lib/action_dispatch/routing/inspector.rb +54 -6
- data/lib/action_dispatch/routing/mapper.rb +58 -24
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
- data/lib/action_dispatch/routing/redirection.rb +15 -6
- data/lib/action_dispatch/routing/route_set.rb +52 -22
- data/lib/action_dispatch/routing/routes_proxy.rb +10 -15
- data/lib/action_dispatch/routing/url_for.rb +26 -22
- data/lib/action_dispatch/routing.rb +7 -7
- data/lib/action_dispatch/system_test_case.rb +3 -3
- data/lib/action_dispatch/system_testing/browser.rb +20 -19
- data/lib/action_dispatch/system_testing/driver.rb +14 -22
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +27 -16
- 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 +67 -28
- data/lib/action_dispatch/testing/assertions.rb +3 -1
- data/lib/action_dispatch/testing/integration.rb +27 -17
- data/lib/action_dispatch/testing/request_encoder.rb +4 -1
- data/lib/action_dispatch/testing/test_process.rb +4 -3
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +23 -9
- data/lib/action_dispatch.rb +37 -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 +65 -29
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
5
|
+
def redirect(event)
|
6
|
+
payload = event.payload
|
7
|
+
|
8
|
+
info { "Redirected to #{payload[:location]}" }
|
9
|
+
|
10
|
+
info do
|
11
|
+
status = payload[:status]
|
12
|
+
|
13
|
+
message = +"Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
|
14
|
+
message << "\n\n" if defined?(Rails.env) && Rails.env.development?
|
15
|
+
|
16
|
+
message
|
17
|
+
end
|
18
|
+
end
|
19
|
+
subscribe_log_level :redirect, :info
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
ActionDispatch::LogSubscriber.attach_to :action_dispatch
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "erb"
|
4
3
|
require "uri"
|
5
4
|
require "active_support/actionable_error"
|
6
5
|
|
@@ -30,15 +29,15 @@ module ActionDispatch
|
|
30
29
|
uri = URI.parse location
|
31
30
|
|
32
31
|
if uri.relative? || uri.scheme == "http" || uri.scheme == "https"
|
33
|
-
body = "
|
32
|
+
body = ""
|
34
33
|
else
|
35
|
-
return [400, {
|
34
|
+
return [400, { Rack::CONTENT_TYPE => "text/plain; charset=utf-8" }, ["Invalid redirection URI"]]
|
36
35
|
end
|
37
36
|
|
38
37
|
[302, {
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
Rack::CONTENT_TYPE => "text/html; charset=#{Response.default_charset}",
|
39
|
+
Rack::CONTENT_LENGTH => body.bytesize.to_s,
|
40
|
+
ActionDispatch::Constants::LOCATION => location,
|
42
41
|
}, [body]]
|
43
42
|
end
|
44
43
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
# = Action Dispatch \AssumeSSL
|
5
|
+
#
|
6
|
+
# When proxying through a load balancer that terminates SSL, the forwarded request will appear
|
7
|
+
# as though it's HTTP instead of HTTPS to the application. This makes redirects and cookie
|
8
|
+
# security target HTTP instead of HTTPS. This middleware makes the server assume that the
|
9
|
+
# proxy already terminated SSL, and that the request really is HTTPS.
|
10
|
+
class AssumeSSL
|
11
|
+
def initialize(app)
|
12
|
+
@app = app
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
env["HTTPS"] = "on"
|
17
|
+
env["HTTP_X_FORWARDED_PORT"] = "443"
|
18
|
+
env["HTTP_X_FORWARDED_PROTO"] = "https"
|
19
|
+
env["rack.url_scheme"] = "https"
|
20
|
+
|
21
|
+
@app.call(env)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -70,7 +70,7 @@ module ActionDispatch
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def cookies_same_site_protection
|
73
|
-
get_header(Cookies::COOKIES_SAME_SITE_PROTECTION)
|
73
|
+
get_header(Cookies::COOKIES_SAME_SITE_PROTECTION)&.call(self)
|
74
74
|
end
|
75
75
|
|
76
76
|
def cookies_digest
|
@@ -92,10 +92,10 @@ module ActionDispatch
|
|
92
92
|
include RequestCookieMethods
|
93
93
|
end
|
94
94
|
|
95
|
-
# Read and write data to cookies through ActionController::
|
95
|
+
# Read and write data to cookies through ActionController::Cookies#cookies.
|
96
96
|
#
|
97
97
|
# When reading cookie data, the data is read from the HTTP request header, Cookie.
|
98
|
-
# When writing cookie data, the data is sent out in the HTTP response header, Set-Cookie
|
98
|
+
# When writing cookie data, the data is sent out in the HTTP response header, +Set-Cookie+.
|
99
99
|
#
|
100
100
|
# Examples of writing:
|
101
101
|
#
|
@@ -160,13 +160,18 @@ module ActionDispatch
|
|
160
160
|
# to <tt>:all</tt>. To support multiple domains, provide an array, and
|
161
161
|
# the first domain matching <tt>request.host</tt> will be used. Make
|
162
162
|
# sure to specify the <tt>:domain</tt> option with <tt>:all</tt> or
|
163
|
-
# <tt>Array</tt> again when deleting cookies.
|
163
|
+
# <tt>Array</tt> again when deleting cookies. For more flexibility you
|
164
|
+
# can set the domain on a per-request basis by specifying <tt>:domain</tt>
|
165
|
+
# with a proc.
|
164
166
|
#
|
165
167
|
# domain: nil # Does not set cookie domain. (default)
|
166
168
|
# domain: :all # Allow the cookie for the top most level
|
167
169
|
# # domain and subdomains.
|
168
170
|
# domain: %w(.example.com .example.org) # Allow the cookie
|
169
171
|
# # for concrete domain names.
|
172
|
+
# domain: proc { Tenant.current.cookie_domain } # Set cookie domain dynamically
|
173
|
+
# domain: proc { |req| ".sub.#{req.host}" } # Set cookie domain dynamically based on request
|
174
|
+
#
|
170
175
|
#
|
171
176
|
# * <tt>:tld_length</tt> - When using <tt>:domain => :all</tt>, this option can be used to explicitly
|
172
177
|
# set the TLD length when using a short (<= 3 character) domain that is being interpreted as part of a TLD.
|
@@ -178,7 +183,8 @@ module ActionDispatch
|
|
178
183
|
# only HTTP. Defaults to +false+.
|
179
184
|
# * <tt>:same_site</tt> - The value of the +SameSite+ cookie attribute, which
|
180
185
|
# determines how this cookie should be restricted in cross-site contexts.
|
181
|
-
# Possible values are +:none+, +:lax+, and +:strict+. Defaults to
|
186
|
+
# Possible values are +nil+, +:none+, +:lax+, and +:strict+. Defaults to
|
187
|
+
# +:lax+.
|
182
188
|
class Cookies
|
183
189
|
HTTP_HEADER = "Set-Cookie"
|
184
190
|
GENERATOR_KEY = "action_dispatch.key_generator"
|
@@ -290,20 +296,6 @@ module ActionDispatch
|
|
290
296
|
class CookieJar # :nodoc:
|
291
297
|
include Enumerable, ChainedCookieJars
|
292
298
|
|
293
|
-
# This regular expression is used to split the levels of a domain.
|
294
|
-
# The top level domain can be any string without a period or
|
295
|
-
# **.**, ***.** style TLDs like co.uk or com.au
|
296
|
-
#
|
297
|
-
# www.example.co.uk gives:
|
298
|
-
# $& => example.co.uk
|
299
|
-
#
|
300
|
-
# example.com gives:
|
301
|
-
# $& => example.com
|
302
|
-
#
|
303
|
-
# lots.of.subdomains.example.local gives:
|
304
|
-
# $& => example.local
|
305
|
-
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
|
306
|
-
|
307
299
|
def self.build(req, cookies)
|
308
300
|
jar = new(req)
|
309
301
|
jar.update(cookies)
|
@@ -390,6 +382,8 @@ module ActionDispatch
|
|
390
382
|
# Removes the cookie on the client machine by setting the value to an empty string
|
391
383
|
# and the expiration date in the past. Like <tt>[]=</tt>, you can pass in
|
392
384
|
# an options hash to delete cookies with extra data such as a <tt>:path</tt>.
|
385
|
+
#
|
386
|
+
# Returns the value of the cookie, or +nil+ if the cookie does not exist.
|
393
387
|
def delete(name, options = {})
|
394
388
|
return unless @cookies.has_key? name.to_s
|
395
389
|
|
@@ -415,9 +409,15 @@ module ActionDispatch
|
|
415
409
|
@cookies.each_key { |k| delete(k, options) }
|
416
410
|
end
|
417
411
|
|
418
|
-
def write(
|
419
|
-
|
420
|
-
|
412
|
+
def write(response)
|
413
|
+
@set_cookies.each do |name, value|
|
414
|
+
if write_cookie?(value)
|
415
|
+
response.set_cookie(name, value)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
@delete_cookies.each do |name, value|
|
420
|
+
response.delete_cookie(name, value)
|
421
421
|
end
|
422
422
|
end
|
423
423
|
|
@@ -428,19 +428,6 @@ module ActionDispatch
|
|
428
428
|
::Rack::Utils.escape(string)
|
429
429
|
end
|
430
430
|
|
431
|
-
def make_set_cookie_header(header)
|
432
|
-
header = @set_cookies.inject(header) { |m, (k, v)|
|
433
|
-
if write_cookie?(v)
|
434
|
-
::Rack::Utils.add_cookie_to_header(m, k, v)
|
435
|
-
else
|
436
|
-
m
|
437
|
-
end
|
438
|
-
}
|
439
|
-
@delete_cookies.inject(header) { |m, (k, v)|
|
440
|
-
::Rack::Utils.add_remove_cookie_to_header(m, k, v)
|
441
|
-
}
|
442
|
-
end
|
443
|
-
|
444
431
|
def write_cookie?(cookie)
|
445
432
|
request.ssl? || !cookie[:secure] || always_write_cookie || request.host.end_with?(".onion")
|
446
433
|
end
|
@@ -452,17 +439,40 @@ module ActionDispatch
|
|
452
439
|
|
453
440
|
options[:path] ||= "/"
|
454
441
|
|
455
|
-
|
456
|
-
|
442
|
+
unless options.key?(:same_site)
|
443
|
+
options[:same_site] = request.cookies_same_site_protection
|
444
|
+
end
|
457
445
|
|
458
446
|
if options[:domain] == :all || options[:domain] == "all"
|
459
|
-
|
460
|
-
|
447
|
+
cookie_domain = ""
|
448
|
+
dot_splitted_host = request.host.split(".", -1)
|
449
|
+
|
450
|
+
# Case where request.host is not an IP address or it's an invalid domain
|
451
|
+
# (ip confirms to the domain structure we expect so we explicitly check for ip)
|
452
|
+
if request.host.match?(/^[\d.]+$/) || dot_splitted_host.include?("") || dot_splitted_host.length == 1
|
453
|
+
options[:domain] = nil
|
454
|
+
return
|
455
|
+
end
|
461
456
|
|
462
|
-
# If
|
463
|
-
|
464
|
-
|
465
|
-
|
457
|
+
# If there is a provided tld length then we use it otherwise default domain.
|
458
|
+
if options[:tld_length].present?
|
459
|
+
# Case where the tld_length provided is valid
|
460
|
+
if dot_splitted_host.length >= options[:tld_length]
|
461
|
+
cookie_domain = dot_splitted_host.last(options[:tld_length]).join(".")
|
462
|
+
end
|
463
|
+
# Case where tld_length is not provided
|
464
|
+
else
|
465
|
+
# Regular TLDs
|
466
|
+
if !(/\.[^.]{2,3}\.[^.]{2}\z/.match?(request.host))
|
467
|
+
cookie_domain = dot_splitted_host.last(2).join(".")
|
468
|
+
# **.**, ***.** style TLDs like co.uk and com.au
|
469
|
+
else
|
470
|
+
cookie_domain = dot_splitted_host.last(3).join(".")
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
options[:domain] = if cookie_domain.present?
|
475
|
+
cookie_domain
|
466
476
|
end
|
467
477
|
elsif options[:domain].is_a? Array
|
468
478
|
# If host matches one of the supplied domains.
|
@@ -470,6 +480,8 @@ module ActionDispatch
|
|
470
480
|
domain = domain.delete_prefix(".")
|
471
481
|
request.host == domain || request.host.end_with?(".#{domain}")
|
472
482
|
end
|
483
|
+
elsif options[:domain].respond_to?(:call)
|
484
|
+
options[:domain] = options[:domain].call(request)
|
473
485
|
end
|
474
486
|
end
|
475
487
|
end
|
@@ -533,75 +545,57 @@ module ActionDispatch
|
|
533
545
|
end
|
534
546
|
end
|
535
547
|
|
536
|
-
class MarshalWithJsonFallback # :nodoc:
|
537
|
-
def self.load(value)
|
538
|
-
Marshal.load(value)
|
539
|
-
rescue TypeError => e
|
540
|
-
ActiveSupport::JSON.decode(value) rescue raise e
|
541
|
-
end
|
542
|
-
|
543
|
-
def self.dump(value)
|
544
|
-
Marshal.dump(value)
|
545
|
-
end
|
546
|
-
end
|
547
|
-
|
548
|
-
class JsonSerializer # :nodoc:
|
549
|
-
def self.load(value)
|
550
|
-
ActiveSupport::JSON.decode(value)
|
551
|
-
end
|
552
|
-
|
553
|
-
def self.dump(value)
|
554
|
-
ActiveSupport::JSON.encode(value)
|
555
|
-
end
|
556
|
-
end
|
557
|
-
|
558
548
|
module SerializedCookieJars # :nodoc:
|
559
|
-
MARSHAL_SIGNATURE = "\x04\x08"
|
560
549
|
SERIALIZER = ActiveSupport::MessageEncryptor::NullSerializer
|
561
550
|
|
562
551
|
protected
|
563
|
-
def
|
564
|
-
request.
|
552
|
+
def digest
|
553
|
+
request.cookies_digest || "SHA1"
|
565
554
|
end
|
566
555
|
|
567
|
-
|
568
|
-
|
556
|
+
private
|
557
|
+
def serializer
|
558
|
+
@serializer ||=
|
559
|
+
case request.cookies_serializer
|
560
|
+
when nil
|
561
|
+
ActiveSupport::Messages::SerializerWithFallback[:marshal]
|
562
|
+
when :hybrid
|
563
|
+
ActiveSupport::Messages::SerializerWithFallback[:json_allow_marshal]
|
564
|
+
when Symbol
|
565
|
+
ActiveSupport::Messages::SerializerWithFallback[request.cookies_serializer]
|
566
|
+
else
|
567
|
+
request.cookies_serializer
|
568
|
+
end
|
569
569
|
end
|
570
570
|
|
571
|
-
def
|
572
|
-
|
573
|
-
|
571
|
+
def reserialize?(dumped)
|
572
|
+
serializer.is_a?(ActiveSupport::Messages::SerializerWithFallback) &&
|
573
|
+
serializer != ActiveSupport::Messages::SerializerWithFallback[:marshal] &&
|
574
|
+
!serializer.dumped?(dumped)
|
575
|
+
end
|
574
576
|
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
when rotate
|
582
|
-
serializer.load(value).tap do |v|
|
583
|
-
self[name] = { value: v }
|
584
|
-
end
|
585
|
-
else
|
586
|
-
serializer.load(value)
|
577
|
+
def parse(name, dumped, force_reserialize: false, **)
|
578
|
+
if dumped
|
579
|
+
begin
|
580
|
+
value = serializer.load(dumped)
|
581
|
+
rescue StandardError
|
582
|
+
return
|
587
583
|
end
|
584
|
+
|
585
|
+
self[name] = { value: value } if force_reserialize || reserialize?(dumped)
|
586
|
+
|
587
|
+
value
|
588
588
|
end
|
589
589
|
end
|
590
590
|
|
591
|
-
def
|
592
|
-
|
593
|
-
case serializer
|
594
|
-
when :marshal
|
595
|
-
MarshalWithJsonFallback
|
596
|
-
when :json, :hybrid
|
597
|
-
JsonSerializer
|
598
|
-
else
|
599
|
-
serializer
|
600
|
-
end
|
591
|
+
def commit(name, options)
|
592
|
+
options[:value] = serializer.dump(options[:value])
|
601
593
|
end
|
602
594
|
|
603
|
-
def
|
604
|
-
|
595
|
+
def check_for_overflow!(name, options)
|
596
|
+
if options[:value].bytesize > MAX_COOKIE_SIZE
|
597
|
+
raise CookieOverflow, "#{name} cookie overflowed with size #{options[:value].bytesize} bytes"
|
598
|
+
end
|
605
599
|
end
|
606
600
|
end
|
607
601
|
|
@@ -622,15 +616,15 @@ module ActionDispatch
|
|
622
616
|
|
623
617
|
private
|
624
618
|
def parse(name, signed_message, purpose: nil)
|
625
|
-
|
626
|
-
|
627
|
-
|
619
|
+
rotated = false
|
620
|
+
data = @verifier.verified(signed_message, purpose: purpose, on_rotation: -> { rotated = true })
|
621
|
+
super(name, data, force_reserialize: rotated)
|
628
622
|
end
|
629
623
|
|
630
624
|
def commit(name, options)
|
631
|
-
|
632
|
-
|
633
|
-
|
625
|
+
super
|
626
|
+
options[:value] = @verifier.generate(options[:value], **cookie_metadata(name, options))
|
627
|
+
check_for_overflow!(name, options)
|
634
628
|
end
|
635
629
|
end
|
636
630
|
|
@@ -672,17 +666,17 @@ module ActionDispatch
|
|
672
666
|
|
673
667
|
private
|
674
668
|
def parse(name, encrypted_message, purpose: nil)
|
675
|
-
|
676
|
-
|
677
|
-
|
669
|
+
rotated = false
|
670
|
+
data = @encryptor.decrypt_and_verify(encrypted_message, purpose: purpose, on_rotation: -> { rotated = true })
|
671
|
+
super(name, data, force_reserialize: rotated)
|
678
672
|
rescue ActiveSupport::MessageEncryptor::InvalidMessage, ActiveSupport::MessageVerifier::InvalidSignature
|
679
673
|
nil
|
680
674
|
end
|
681
675
|
|
682
676
|
def commit(name, options)
|
683
|
-
|
684
|
-
|
685
|
-
|
677
|
+
super
|
678
|
+
options[:value] = @encryptor.encrypt_and_sign(options[:value], **cookie_metadata(name, options))
|
679
|
+
check_for_overflow!(name, options)
|
686
680
|
end
|
687
681
|
end
|
688
682
|
|
@@ -691,21 +685,18 @@ module ActionDispatch
|
|
691
685
|
end
|
692
686
|
|
693
687
|
def call(env)
|
694
|
-
request = ActionDispatch::Request.new
|
695
|
-
|
696
|
-
status, headers, body = @app.call(env)
|
688
|
+
request = ActionDispatch::Request.new(env)
|
689
|
+
response = @app.call(env)
|
697
690
|
|
698
691
|
if request.have_cookie_jar?
|
699
692
|
cookie_jar = request.cookie_jar
|
700
693
|
unless cookie_jar.committed?
|
701
|
-
|
702
|
-
|
703
|
-
headers[HTTP_HEADER] = headers[HTTP_HEADER].join("\n")
|
704
|
-
end
|
694
|
+
response = Rack::Response[*response]
|
695
|
+
cookie_jar.write(response)
|
705
696
|
end
|
706
697
|
end
|
707
698
|
|
708
|
-
|
699
|
+
response.to_a
|
709
700
|
end
|
710
701
|
end
|
711
702
|
end
|
@@ -6,6 +6,8 @@ require "action_dispatch/routing/inspector"
|
|
6
6
|
require "action_view"
|
7
7
|
|
8
8
|
module ActionDispatch
|
9
|
+
# = Action Dispatch \DebugExceptions
|
10
|
+
#
|
9
11
|
# This middleware is responsible for logging exceptions and
|
10
12
|
# showing a debugging page in case the request is local.
|
11
13
|
class DebugExceptions
|
@@ -24,26 +26,26 @@ module ActionDispatch
|
|
24
26
|
end
|
25
27
|
|
26
28
|
def call(env)
|
27
|
-
request = ActionDispatch::Request.new env
|
28
29
|
_, headers, body = response = @app.call(env)
|
29
30
|
|
30
|
-
if headers[
|
31
|
+
if headers[Constants::X_CASCADE] == "pass"
|
31
32
|
body.close if body.respond_to?(:close)
|
32
33
|
raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
|
33
34
|
end
|
34
35
|
|
35
36
|
response
|
36
37
|
rescue Exception => exception
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
request = ActionDispatch::Request.new env
|
39
|
+
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
40
|
+
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
41
|
+
|
42
|
+
invoke_interceptors(request, exception, wrapper)
|
43
|
+
raise exception unless wrapper.show?(request)
|
44
|
+
render_exception(request, exception, wrapper)
|
40
45
|
end
|
41
46
|
|
42
47
|
private
|
43
|
-
def invoke_interceptors(request, exception)
|
44
|
-
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
45
|
-
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
46
|
-
|
48
|
+
def invoke_interceptors(request, exception, wrapper)
|
47
49
|
@interceptors.each do |interceptor|
|
48
50
|
interceptor.call(request, exception)
|
49
51
|
rescue Exception
|
@@ -51,9 +53,7 @@ module ActionDispatch
|
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
|
-
def render_exception(request, exception)
|
55
|
-
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
56
|
-
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
56
|
+
def render_exception(request, exception, wrapper)
|
57
57
|
log_error(request, wrapper)
|
58
58
|
|
59
59
|
if request.get_header("action_dispatch.show_detailed_exceptions")
|
@@ -94,7 +94,7 @@ module ActionDispatch
|
|
94
94
|
wrapper.status_code,
|
95
95
|
Rack::Utils::HTTP_STATUS_CODES[500]
|
96
96
|
),
|
97
|
-
exception: wrapper.
|
97
|
+
exception: wrapper.exception_inspect,
|
98
98
|
traces: wrapper.traces
|
99
99
|
}
|
100
100
|
|
@@ -115,19 +115,19 @@ module ActionDispatch
|
|
115
115
|
DebugView.new(
|
116
116
|
request: request,
|
117
117
|
exception_wrapper: wrapper,
|
118
|
+
# Everything should use the wrapper, but we need to pass
|
119
|
+
# `exception` for legacy code.
|
118
120
|
exception: wrapper.exception,
|
119
121
|
traces: wrapper.traces,
|
120
122
|
show_source_idx: wrapper.source_to_show_id,
|
121
123
|
trace_to_show: wrapper.trace_to_show,
|
122
|
-
routes_inspector: routes_inspector(wrapper
|
124
|
+
routes_inspector: routes_inspector(wrapper),
|
123
125
|
source_extracts: wrapper.source_extracts,
|
124
|
-
line_number: wrapper.line_number,
|
125
|
-
file: wrapper.file
|
126
126
|
)
|
127
127
|
end
|
128
128
|
|
129
129
|
def render(status, body, format)
|
130
|
-
[status, {
|
130
|
+
[status, { Rack::CONTENT_TYPE => "#{format}; charset=#{Response.default_charset}", Rack::CONTENT_LENGTH => body.bytesize.to_s }, [body]]
|
131
131
|
end
|
132
132
|
|
133
133
|
def log_error(request, wrapper)
|
@@ -136,26 +136,27 @@ module ActionDispatch
|
|
136
136
|
return unless logger
|
137
137
|
return if !log_rescued_responses?(request) && wrapper.rescue_response?
|
138
138
|
|
139
|
-
exception = wrapper.exception
|
140
139
|
trace = wrapper.exception_trace
|
141
140
|
|
142
141
|
message = []
|
143
142
|
message << " "
|
144
|
-
message << "#{
|
145
|
-
message.concat(
|
143
|
+
message << "#{wrapper.exception_class_name} (#{wrapper.message}):"
|
144
|
+
message.concat(wrapper.annotated_source_code)
|
146
145
|
message << " "
|
147
146
|
message.concat(trace)
|
148
147
|
|
149
|
-
log_array(logger, message)
|
148
|
+
log_array(logger, message, request)
|
150
149
|
end
|
151
150
|
|
152
|
-
def log_array(logger, lines)
|
151
|
+
def log_array(logger, lines, request)
|
153
152
|
return if lines.empty?
|
154
153
|
|
154
|
+
level = request.get_header("action_dispatch.debug_exception_log_level")
|
155
|
+
|
155
156
|
if logger.formatter && logger.formatter.respond_to?(:tags_text)
|
156
|
-
logger.
|
157
|
+
logger.add(level, lines.join("\n#{logger.formatter.tags_text}"))
|
157
158
|
else
|
158
|
-
logger.
|
159
|
+
logger.add(level, lines.join("\n"))
|
159
160
|
end
|
160
161
|
end
|
161
162
|
|
@@ -168,7 +169,7 @@ module ActionDispatch
|
|
168
169
|
end
|
169
170
|
|
170
171
|
def routes_inspector(exception)
|
171
|
-
if @routes_app.respond_to?(:routes) && (exception.
|
172
|
+
if @routes_app.respond_to?(:routes) && (exception.routing_error? || exception.template_error?)
|
172
173
|
ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
|
173
174
|
end
|
174
175
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionDispatch
|
4
|
+
# = Action Dispatch \DebugLocks
|
5
|
+
#
|
4
6
|
# This middleware can be used to diagnose deadlocks in the autoload interlock.
|
5
7
|
#
|
6
8
|
# To use it, insert it near the top of the middleware stack, using
|
@@ -97,7 +99,8 @@ module ActionDispatch
|
|
97
99
|
msg << "\n#{info[:backtrace].join("\n")}\n" if info[:backtrace]
|
98
100
|
end.join("\n\n---\n\n\n")
|
99
101
|
|
100
|
-
[200, {
|
102
|
+
[200, { Rack::CONTENT_TYPE => "text/plain; charset=#{ActionDispatch::Response.default_charset}",
|
103
|
+
Rack::CONTENT_LENGTH => str.size.to_s }, [str]]
|
101
104
|
end
|
102
105
|
|
103
106
|
def blocked_by?(victim, blocker, all_threads)
|
@@ -7,18 +7,23 @@ require "action_view/base"
|
|
7
7
|
|
8
8
|
module ActionDispatch
|
9
9
|
class DebugView < ActionView::Base # :nodoc:
|
10
|
-
|
10
|
+
RESCUES_TEMPLATE_PATHS = [File.expand_path("templates", __dir__)]
|
11
11
|
|
12
12
|
def initialize(assigns)
|
13
|
-
paths =
|
13
|
+
paths = RESCUES_TEMPLATE_PATHS.dup
|
14
14
|
lookup_context = ActionView::LookupContext.new(paths)
|
15
15
|
super(lookup_context, assigns, nil)
|
16
|
+
@exception_wrapper = assigns[:exception_wrapper]
|
16
17
|
end
|
17
18
|
|
18
19
|
def compiled_method_container
|
19
20
|
self.class
|
20
21
|
end
|
21
22
|
|
23
|
+
def error_highlight_available?
|
24
|
+
@exception_wrapper.error_highlight_available?
|
25
|
+
end
|
26
|
+
|
22
27
|
def debug_params(params)
|
23
28
|
clean_params = params.clone
|
24
29
|
clean_params.delete("action")
|