actionpack 5.2.8.1 → 6.0.6
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 +270 -347
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/abstract_controller/base.rb +4 -3
- data/lib/abstract_controller/caching/fragments.rb +6 -22
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +12 -0
- data/lib/abstract_controller/collector.rb +1 -2
- data/lib/abstract_controller/helpers.rb +7 -6
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
- data/lib/abstract_controller/translation.rb +4 -4
- data/lib/action_controller/api.rb +2 -1
- data/lib/action_controller/base.rb +2 -7
- data/lib/action_controller/caching.rb +1 -2
- data/lib/action_controller/log_subscriber.rb +8 -5
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +9 -3
- data/lib/action_controller/metal/content_security_policy.rb +0 -1
- data/lib/action_controller/metal/data_streaming.rb +5 -6
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +23 -2
- data/lib/action_controller/metal/flash.rb +5 -5
- data/lib/action_controller/metal/force_ssl.rb +15 -56
- data/lib/action_controller/metal/head.rb +1 -1
- data/lib/action_controller/metal/helpers.rb +3 -4
- data/lib/action_controller/metal/http_authentication.rb +20 -21
- data/lib/action_controller/metal/implicit_render.rb +4 -14
- data/lib/action_controller/metal/instrumentation.rb +3 -6
- data/lib/action_controller/metal/live.rb +29 -31
- data/lib/action_controller/metal/mime_responds.rb +13 -2
- data/lib/action_controller/metal/params_wrapper.rb +18 -14
- data/lib/action_controller/metal/redirecting.rb +5 -5
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +2 -3
- data/lib/action_controller/metal/request_forgery_protection.rb +25 -48
- data/lib/action_controller/metal/streaming.rb +0 -1
- data/lib/action_controller/metal/strong_parameters.rb +65 -44
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +8 -6
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +17 -3
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +7 -8
- data/lib/action_controller.rb +5 -1
- data/lib/action_dispatch/http/cache.rb +14 -11
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +28 -17
- data/lib/action_dispatch/http/filter_parameters.rb +8 -7
- data/lib/action_dispatch/http/filter_redirect.rb +1 -2
- data/lib/action_dispatch/http/headers.rb +1 -2
- data/lib/action_dispatch/http/mime_negotiation.rb +13 -6
- data/lib/action_dispatch/http/mime_type.rb +14 -8
- data/lib/action_dispatch/http/parameter_filter.rb +5 -79
- data/lib/action_dispatch/http/parameters.rb +15 -6
- data/lib/action_dispatch/http/request.rb +21 -14
- data/lib/action_dispatch/http/response.rb +40 -21
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +81 -82
- data/lib/action_dispatch/journey/formatter.rb +2 -3
- data/lib/action_dispatch/journey/gtg/builder.rb +0 -1
- data/lib/action_dispatch/journey/gtg/transition_table.rb +0 -1
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -1
- data/lib/action_dispatch/journey/nodes/node.rb +9 -8
- data/lib/action_dispatch/journey/path/pattern.rb +6 -3
- data/lib/action_dispatch/journey/route.rb +5 -4
- data/lib/action_dispatch/journey/router/utils.rb +10 -10
- data/lib/action_dispatch/journey/router.rb +0 -4
- 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 +62 -78
- data/lib/action_dispatch/middleware/debug_exceptions.rb +45 -61
- 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 +49 -16
- 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 +9 -12
- data/lib/action_dispatch/middleware/request_id.rb +2 -2
- data/lib/action_dispatch/middleware/session/abstract_store.rb +0 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -7
- data/lib/action_dispatch/middleware/show_exceptions.rb +1 -2
- data/lib/action_dispatch/middleware/ssl.rb +8 -8
- data/lib/action_dispatch/middleware/stack.rb +38 -2
- data/lib/action_dispatch/middleware/static.rb +6 -7
- 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/_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 +26 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +7 -4
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +5 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -0
- 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/routes/_table.html.erb +3 -0
- data/lib/action_dispatch/railtie.rb +7 -2
- data/lib/action_dispatch/request/session.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +97 -50
- data/lib/action_dispatch/routing/mapper.rb +63 -42
- data/lib/action_dispatch/routing/polymorphic_routes.rb +3 -6
- data/lib/action_dispatch/routing/route_set.rb +25 -31
- data/lib/action_dispatch/routing/url_for.rb +2 -2
- data/lib/action_dispatch/routing.rb +21 -20
- data/lib/action_dispatch/system_test_case.rb +44 -6
- data/lib/action_dispatch/system_testing/browser.rb +38 -7
- data/lib/action_dispatch/system_testing/driver.rb +11 -2
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +6 -5
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +7 -6
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions/response.rb +2 -3
- data/lib/action_dispatch/testing/assertions/routing.rb +15 -3
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +33 -12
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +2 -2
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_dispatch.rb +7 -2
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +29 -15
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -9,7 +9,7 @@ require "rack/utils"
|
|
9
9
|
module ActionDispatch
|
10
10
|
class Request
|
11
11
|
def cookie_jar
|
12
|
-
fetch_header("action_dispatch.cookies"
|
12
|
+
fetch_header("action_dispatch.cookies") do
|
13
13
|
self.cookie_jar = Cookies::CookieJar.build(self, cookies)
|
14
14
|
end
|
15
15
|
end
|
@@ -22,11 +22,11 @@ module ActionDispatch
|
|
22
22
|
}
|
23
23
|
|
24
24
|
def have_cookie_jar?
|
25
|
-
has_header? "action_dispatch.cookies"
|
25
|
+
has_header? "action_dispatch.cookies"
|
26
26
|
end
|
27
27
|
|
28
28
|
def cookie_jar=(jar)
|
29
|
-
set_header "action_dispatch.cookies"
|
29
|
+
set_header "action_dispatch.cookies", jar
|
30
30
|
end
|
31
31
|
|
32
32
|
def key_generator
|
@@ -61,10 +61,6 @@ module ActionDispatch
|
|
61
61
|
get_header Cookies::SIGNED_COOKIE_DIGEST
|
62
62
|
end
|
63
63
|
|
64
|
-
def secret_token
|
65
|
-
get_header Cookies::SECRET_TOKEN
|
66
|
-
end
|
67
|
-
|
68
64
|
def secret_key_base
|
69
65
|
get_header Cookies::SECRET_KEY_BASE
|
70
66
|
end
|
@@ -81,6 +77,10 @@ module ActionDispatch
|
|
81
77
|
get_header Cookies::COOKIES_ROTATIONS
|
82
78
|
end
|
83
79
|
|
80
|
+
def use_cookies_with_metadata
|
81
|
+
get_header Cookies::USE_COOKIES_WITH_METADATA
|
82
|
+
end
|
83
|
+
|
84
84
|
# :startdoc:
|
85
85
|
end
|
86
86
|
|
@@ -168,20 +168,20 @@ module ActionDispatch
|
|
168
168
|
# * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
|
169
169
|
# only HTTP. Defaults to +false+.
|
170
170
|
class Cookies
|
171
|
-
HTTP_HEADER = "Set-Cookie"
|
172
|
-
GENERATOR_KEY = "action_dispatch.key_generator"
|
173
|
-
SIGNED_COOKIE_SALT = "action_dispatch.signed_cookie_salt"
|
174
|
-
ENCRYPTED_COOKIE_SALT = "action_dispatch.encrypted_cookie_salt"
|
175
|
-
ENCRYPTED_SIGNED_COOKIE_SALT = "action_dispatch.encrypted_signed_cookie_salt"
|
176
|
-
AUTHENTICATED_ENCRYPTED_COOKIE_SALT = "action_dispatch.authenticated_encrypted_cookie_salt"
|
177
|
-
USE_AUTHENTICATED_COOKIE_ENCRYPTION = "action_dispatch.use_authenticated_cookie_encryption"
|
178
|
-
ENCRYPTED_COOKIE_CIPHER = "action_dispatch.encrypted_cookie_cipher"
|
179
|
-
SIGNED_COOKIE_DIGEST = "action_dispatch.signed_cookie_digest"
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
171
|
+
HTTP_HEADER = "Set-Cookie"
|
172
|
+
GENERATOR_KEY = "action_dispatch.key_generator"
|
173
|
+
SIGNED_COOKIE_SALT = "action_dispatch.signed_cookie_salt"
|
174
|
+
ENCRYPTED_COOKIE_SALT = "action_dispatch.encrypted_cookie_salt"
|
175
|
+
ENCRYPTED_SIGNED_COOKIE_SALT = "action_dispatch.encrypted_signed_cookie_salt"
|
176
|
+
AUTHENTICATED_ENCRYPTED_COOKIE_SALT = "action_dispatch.authenticated_encrypted_cookie_salt"
|
177
|
+
USE_AUTHENTICATED_COOKIE_ENCRYPTION = "action_dispatch.use_authenticated_cookie_encryption"
|
178
|
+
ENCRYPTED_COOKIE_CIPHER = "action_dispatch.encrypted_cookie_cipher"
|
179
|
+
SIGNED_COOKIE_DIGEST = "action_dispatch.signed_cookie_digest"
|
180
|
+
SECRET_KEY_BASE = "action_dispatch.secret_key_base"
|
181
|
+
COOKIES_SERIALIZER = "action_dispatch.cookies_serializer"
|
182
|
+
COOKIES_DIGEST = "action_dispatch.cookies_digest"
|
183
|
+
COOKIES_ROTATIONS = "action_dispatch.cookies_rotations"
|
184
|
+
USE_COOKIES_WITH_METADATA = "action_dispatch.use_cookies_with_metadata"
|
185
185
|
|
186
186
|
# Cookies can typically store 4096 bytes.
|
187
187
|
MAX_COOKIE_SIZE = 4096
|
@@ -210,9 +210,6 @@ module ActionDispatch
|
|
210
210
|
# the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
|
211
211
|
# cookie was tampered with by the user (or a 3rd party), +nil+ will be returned.
|
212
212
|
#
|
213
|
-
# If +secret_key_base+ and +secrets.secret_token+ (deprecated) are both set,
|
214
|
-
# legacy cookies signed with the old key generator will be transparently upgraded.
|
215
|
-
#
|
216
213
|
# This jar requires that you set a suitable secret for the verification on your app's +secret_key_base+.
|
217
214
|
#
|
218
215
|
# Example:
|
@@ -228,9 +225,6 @@ module ActionDispatch
|
|
228
225
|
# Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read.
|
229
226
|
# If the cookie was tampered with by the user (or a 3rd party), +nil+ will be returned.
|
230
227
|
#
|
231
|
-
# If +secret_key_base+ and +secrets.secret_token+ (deprecated) are both set,
|
232
|
-
# legacy cookies signed with the old key generator will be transparently upgraded.
|
233
|
-
#
|
234
228
|
# If +config.action_dispatch.encrypted_cookie_salt+ and +config.action_dispatch.encrypted_signed_cookie_salt+
|
235
229
|
# are both set, legacy cookies encrypted with HMAC AES-256-CBC will be transparently upgraded.
|
236
230
|
#
|
@@ -258,11 +252,6 @@ module ActionDispatch
|
|
258
252
|
end
|
259
253
|
|
260
254
|
private
|
261
|
-
|
262
|
-
def upgrade_legacy_signed_cookies?
|
263
|
-
request.secret_token.present? && request.secret_key_base.present?
|
264
|
-
end
|
265
|
-
|
266
255
|
def upgrade_legacy_hmac_aes_cbc_cookies?
|
267
256
|
request.secret_key_base.present? &&
|
268
257
|
request.encrypted_signed_cookie_salt.present? &&
|
@@ -348,7 +337,7 @@ module ActionDispatch
|
|
348
337
|
|
349
338
|
def update_cookies_from_jar
|
350
339
|
request_jar = @request.cookie_jar.instance_variable_get(:@cookies)
|
351
|
-
set_cookies = request_jar.reject { |k, _| @delete_cookies.key?(k) }
|
340
|
+
set_cookies = request_jar.reject { |k, _| @delete_cookies.key?(k) || @set_cookies.key?(k) }
|
352
341
|
|
353
342
|
@cookies.update set_cookies if set_cookies
|
354
343
|
end
|
@@ -438,7 +427,6 @@ module ActionDispatch
|
|
438
427
|
mattr_accessor :always_write_cookie, default: false
|
439
428
|
|
440
429
|
private
|
441
|
-
|
442
430
|
def escape(string)
|
443
431
|
::Rack::Utils.escape(string)
|
444
432
|
end
|
@@ -470,7 +458,13 @@ module ActionDispatch
|
|
470
458
|
|
471
459
|
def [](name)
|
472
460
|
if data = @parent_jar[name.to_s]
|
473
|
-
parse
|
461
|
+
result = parse(name, data, purpose: "cookie.#{name}")
|
462
|
+
|
463
|
+
if result.nil?
|
464
|
+
parse(name, data)
|
465
|
+
else
|
466
|
+
result
|
467
|
+
end
|
474
468
|
end
|
475
469
|
end
|
476
470
|
|
@@ -481,7 +475,7 @@ module ActionDispatch
|
|
481
475
|
options = { value: options }
|
482
476
|
end
|
483
477
|
|
484
|
-
commit(options)
|
478
|
+
commit(name, options)
|
485
479
|
@parent_jar[name] = options
|
486
480
|
end
|
487
481
|
|
@@ -490,24 +484,26 @@ module ActionDispatch
|
|
490
484
|
|
491
485
|
private
|
492
486
|
def expiry_options(options)
|
493
|
-
if
|
494
|
-
|
495
|
-
{ expires_in: options[:expires] }
|
496
|
-
else
|
497
|
-
{ expires_at: options[:expires] }
|
498
|
-
end
|
487
|
+
if options[:expires].respond_to?(:from_now)
|
488
|
+
{ expires_in: options[:expires] }
|
499
489
|
else
|
500
|
-
{}
|
490
|
+
{ expires_at: options[:expires] }
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
def cookie_metadata(name, options)
|
495
|
+
expiry_options(options).tap do |metadata|
|
496
|
+
metadata[:purpose] = "cookie.#{name}" if request.use_cookies_with_metadata
|
501
497
|
end
|
502
498
|
end
|
503
499
|
|
504
|
-
def parse(name, data); data; end
|
505
|
-
def commit(options); end
|
500
|
+
def parse(name, data, purpose: nil); data; end
|
501
|
+
def commit(name, options); end
|
506
502
|
end
|
507
503
|
|
508
504
|
class PermanentCookieJar < AbstractCookieJar # :nodoc:
|
509
505
|
private
|
510
|
-
def commit(options)
|
506
|
+
def commit(name, options)
|
511
507
|
options[:expires] = 20.years.from_now
|
512
508
|
end
|
513
509
|
end
|
@@ -523,7 +519,7 @@ module ActionDispatch
|
|
523
519
|
end
|
524
520
|
|
525
521
|
module SerializedCookieJars # :nodoc:
|
526
|
-
MARSHAL_SIGNATURE = "\x04\x08"
|
522
|
+
MARSHAL_SIGNATURE = "\x04\x08"
|
527
523
|
SERIALIZER = ActiveSupport::MessageEncryptor::NullSerializer
|
528
524
|
|
529
525
|
protected
|
@@ -542,9 +538,13 @@ module ActionDispatch
|
|
542
538
|
if value
|
543
539
|
case
|
544
540
|
when needs_migration?(value)
|
545
|
-
|
541
|
+
Marshal.load(value).tap do |v|
|
542
|
+
self[name] = { value: v }
|
543
|
+
end
|
546
544
|
when rotate
|
547
|
-
|
545
|
+
serializer.load(value).tap do |v|
|
546
|
+
self[name] = { value: v }
|
547
|
+
end
|
548
548
|
else
|
549
549
|
serializer.load(value)
|
550
550
|
end
|
@@ -577,24 +577,21 @@ module ActionDispatch
|
|
577
577
|
secret = request.key_generator.generate_key(request.signed_cookie_salt)
|
578
578
|
@verifier = ActiveSupport::MessageVerifier.new(secret, digest: signed_cookie_digest, serializer: SERIALIZER)
|
579
579
|
|
580
|
-
request.cookies_rotations.signed.each do
|
580
|
+
request.cookies_rotations.signed.each do |(*secrets)|
|
581
|
+
options = secrets.extract_options!
|
581
582
|
@verifier.rotate(*secrets, serializer: SERIALIZER, **options)
|
582
583
|
end
|
583
|
-
|
584
|
-
if upgrade_legacy_signed_cookies?
|
585
|
-
@verifier.rotate request.secret_token, serializer: SERIALIZER
|
586
|
-
end
|
587
584
|
end
|
588
585
|
|
589
586
|
private
|
590
|
-
def parse(name, signed_message)
|
587
|
+
def parse(name, signed_message, purpose: nil)
|
591
588
|
deserialize(name) do |rotate|
|
592
|
-
@verifier.verified(signed_message, on_rotation: rotate)
|
589
|
+
@verifier.verified(signed_message, on_rotation: rotate, purpose: purpose)
|
593
590
|
end
|
594
591
|
end
|
595
592
|
|
596
|
-
def commit(options)
|
597
|
-
options[:value] = @verifier.generate(serialize(options[:value]),
|
593
|
+
def commit(name, options)
|
594
|
+
options[:value] = @verifier.generate(serialize(options[:value]), **cookie_metadata(name, options))
|
598
595
|
|
599
596
|
raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
|
600
597
|
end
|
@@ -617,7 +614,8 @@ module ActionDispatch
|
|
617
614
|
@encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, cipher: "aes-256-cbc", serializer: SERIALIZER)
|
618
615
|
end
|
619
616
|
|
620
|
-
request.cookies_rotations.encrypted.each do
|
617
|
+
request.cookies_rotations.encrypted.each do |(*secrets)|
|
618
|
+
options = secrets.extract_options!
|
621
619
|
@encryptor.rotate(*secrets, serializer: SERIALIZER, **options)
|
622
620
|
end
|
623
621
|
|
@@ -628,36 +626,22 @@ module ActionDispatch
|
|
628
626
|
|
629
627
|
@encryptor.rotate(secret, sign_secret, cipher: legacy_cipher, digest: digest, serializer: SERIALIZER)
|
630
628
|
end
|
631
|
-
|
632
|
-
if upgrade_legacy_signed_cookies?
|
633
|
-
@legacy_verifier = ActiveSupport::MessageVerifier.new(request.secret_token, digest: digest, serializer: SERIALIZER)
|
634
|
-
end
|
635
629
|
end
|
636
630
|
|
637
631
|
private
|
638
|
-
def parse(name, encrypted_message)
|
632
|
+
def parse(name, encrypted_message, purpose: nil)
|
639
633
|
deserialize(name) do |rotate|
|
640
|
-
@encryptor.decrypt_and_verify(encrypted_message, on_rotation: rotate)
|
634
|
+
@encryptor.decrypt_and_verify(encrypted_message, on_rotation: rotate, purpose: purpose)
|
641
635
|
end
|
642
636
|
rescue ActiveSupport::MessageEncryptor::InvalidMessage, ActiveSupport::MessageVerifier::InvalidSignature
|
643
|
-
|
637
|
+
nil
|
644
638
|
end
|
645
639
|
|
646
|
-
def commit(options)
|
647
|
-
options[:value] = @encryptor.encrypt_and_sign(serialize(options[:value]),
|
640
|
+
def commit(name, options)
|
641
|
+
options[:value] = @encryptor.encrypt_and_sign(serialize(options[:value]), **cookie_metadata(name, options))
|
648
642
|
|
649
643
|
raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
|
650
644
|
end
|
651
|
-
|
652
|
-
def parse_legacy_signed_message(name, legacy_signed_message)
|
653
|
-
if defined?(@legacy_verifier)
|
654
|
-
deserialize(name) do |rotate|
|
655
|
-
rotate.call
|
656
|
-
|
657
|
-
@legacy_verifier.verified(legacy_signed_message)
|
658
|
-
end
|
659
|
-
end
|
660
|
-
end
|
661
645
|
end
|
662
646
|
|
663
647
|
def initialize(app)
|
@@ -3,57 +3,28 @@
|
|
3
3
|
require "action_dispatch/http/request"
|
4
4
|
require "action_dispatch/middleware/exception_wrapper"
|
5
5
|
require "action_dispatch/routing/inspector"
|
6
|
+
|
7
|
+
require "active_support/actionable_error"
|
8
|
+
|
6
9
|
require "action_view"
|
7
10
|
require "action_view/base"
|
8
11
|
|
9
|
-
require "pp"
|
10
|
-
|
11
12
|
module ActionDispatch
|
12
13
|
# This middleware is responsible for logging exceptions and
|
13
14
|
# showing a debugging page in case the request is local.
|
14
15
|
class DebugExceptions
|
15
|
-
|
16
|
-
|
17
|
-
class DebugView < ActionView::Base
|
18
|
-
def debug_params(params)
|
19
|
-
clean_params = params.clone
|
20
|
-
clean_params.delete("action")
|
21
|
-
clean_params.delete("controller")
|
22
|
-
|
23
|
-
if clean_params.empty?
|
24
|
-
"None"
|
25
|
-
else
|
26
|
-
PP.pp(clean_params, "".dup, 200)
|
27
|
-
end
|
28
|
-
end
|
16
|
+
cattr_reader :interceptors, instance_accessor: false, default: []
|
29
17
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
else
|
34
|
-
"None"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def debug_hash(object)
|
39
|
-
object.to_hash.sort_by { |k, _| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n")
|
40
|
-
end
|
41
|
-
|
42
|
-
def render(*)
|
43
|
-
logger = ActionView::Base.logger
|
44
|
-
|
45
|
-
if logger && logger.respond_to?(:silence)
|
46
|
-
logger.silence { super }
|
47
|
-
else
|
48
|
-
super
|
49
|
-
end
|
50
|
-
end
|
18
|
+
def self.register_interceptor(object = nil, &block)
|
19
|
+
interceptor = object || block
|
20
|
+
interceptors << interceptor
|
51
21
|
end
|
52
22
|
|
53
|
-
def initialize(app, routes_app = nil, response_format = :default)
|
23
|
+
def initialize(app, routes_app = nil, response_format = :default, interceptors = self.class.interceptors)
|
54
24
|
@app = app
|
55
25
|
@routes_app = routes_app
|
56
26
|
@response_format = response_format
|
27
|
+
@interceptors = interceptors
|
57
28
|
end
|
58
29
|
|
59
30
|
def call(env)
|
@@ -67,11 +38,22 @@ module ActionDispatch
|
|
67
38
|
|
68
39
|
response
|
69
40
|
rescue Exception => exception
|
41
|
+
invoke_interceptors(request, exception)
|
70
42
|
raise exception unless request.show_exceptions?
|
71
43
|
render_exception(request, exception)
|
72
44
|
end
|
73
45
|
|
74
46
|
private
|
47
|
+
def invoke_interceptors(request, exception)
|
48
|
+
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
49
|
+
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
50
|
+
|
51
|
+
@interceptors.each do |interceptor|
|
52
|
+
interceptor.call(request, exception)
|
53
|
+
rescue Exception
|
54
|
+
log_error(request, wrapper)
|
55
|
+
end
|
56
|
+
end
|
75
57
|
|
76
58
|
def render_exception(request, exception)
|
77
59
|
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
@@ -79,7 +61,11 @@ module ActionDispatch
|
|
79
61
|
log_error(request, wrapper)
|
80
62
|
|
81
63
|
if request.get_header("action_dispatch.show_detailed_exceptions")
|
82
|
-
|
64
|
+
begin
|
65
|
+
content_type = request.formats.first
|
66
|
+
rescue ActionDispatch::Http::MimeNegotiation::InvalidType
|
67
|
+
content_type = Mime[:text]
|
68
|
+
end
|
83
69
|
|
84
70
|
if api_request?(content_type)
|
85
71
|
render_for_api_request(content_type, wrapper)
|
@@ -130,23 +116,13 @@ module ActionDispatch
|
|
130
116
|
end
|
131
117
|
|
132
118
|
def create_template(request, wrapper)
|
133
|
-
|
134
|
-
|
135
|
-
trace_to_show = "Application Trace"
|
136
|
-
if traces[trace_to_show].empty? && wrapper.rescue_template != "routing_error"
|
137
|
-
trace_to_show = "Full Trace"
|
138
|
-
end
|
139
|
-
|
140
|
-
if source_to_show = traces[trace_to_show].first
|
141
|
-
source_to_show_id = source_to_show[:id]
|
142
|
-
end
|
143
|
-
|
144
|
-
DebugView.new([RESCUES_TEMPLATE_PATH],
|
119
|
+
DebugView.new(
|
145
120
|
request: request,
|
121
|
+
exception_wrapper: wrapper,
|
146
122
|
exception: wrapper.exception,
|
147
|
-
traces: traces,
|
148
|
-
show_source_idx: source_to_show_id,
|
149
|
-
trace_to_show: trace_to_show,
|
123
|
+
traces: wrapper.traces,
|
124
|
+
show_source_idx: wrapper.source_to_show_id,
|
125
|
+
trace_to_show: wrapper.trace_to_show,
|
150
126
|
routes_inspector: routes_inspector(wrapper.exception),
|
151
127
|
source_extracts: wrapper.source_extracts,
|
152
128
|
line_number: wrapper.line_number,
|
@@ -160,6 +136,7 @@ module ActionDispatch
|
|
160
136
|
|
161
137
|
def log_error(request, wrapper)
|
162
138
|
logger = logger(request)
|
139
|
+
|
163
140
|
return unless logger
|
164
141
|
|
165
142
|
exception = wrapper.exception
|
@@ -168,19 +145,26 @@ module ActionDispatch
|
|
168
145
|
trace = wrapper.framework_trace if trace.empty?
|
169
146
|
|
170
147
|
ActiveSupport::Deprecation.silence do
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
148
|
+
message = []
|
149
|
+
message << " "
|
150
|
+
message << "#{exception.class} (#{exception.message}):"
|
151
|
+
message.concat(exception.annotated_source_code) if exception.respond_to?(:annotated_source_code)
|
152
|
+
message << " "
|
153
|
+
message.concat(trace)
|
154
|
+
|
155
|
+
log_array(logger, message)
|
176
156
|
end
|
177
157
|
end
|
178
158
|
|
179
159
|
def log_array(logger, array)
|
160
|
+
lines = Array(array)
|
161
|
+
|
162
|
+
return if lines.empty?
|
163
|
+
|
180
164
|
if logger.formatter && logger.formatter.respond_to?(:tags_text)
|
181
|
-
logger.fatal
|
165
|
+
logger.fatal lines.join("\n#{logger.formatter.tags_text}")
|
182
166
|
else
|
183
|
-
logger.fatal
|
167
|
+
logger.fatal lines.join("\n")
|
184
168
|
end
|
185
169
|
end
|
186
170
|
|
@@ -32,7 +32,7 @@ module ActionDispatch
|
|
32
32
|
req = ActionDispatch::Request.new env
|
33
33
|
|
34
34
|
if req.get?
|
35
|
-
path = req.path_info.chomp("/"
|
35
|
+
path = req.path_info.chomp("/")
|
36
36
|
if path == @path
|
37
37
|
return render_details(req)
|
38
38
|
end
|
@@ -63,19 +63,19 @@ module ActionDispatch
|
|
63
63
|
|
64
64
|
str = threads.map do |thread, info|
|
65
65
|
if info[:exclusive]
|
66
|
-
lock_state = "Exclusive"
|
66
|
+
lock_state = +"Exclusive"
|
67
67
|
elsif info[:sharing] > 0
|
68
|
-
lock_state = "Sharing"
|
68
|
+
lock_state = +"Sharing"
|
69
69
|
lock_state << " x#{info[:sharing]}" if info[:sharing] > 1
|
70
70
|
else
|
71
|
-
lock_state = "No lock"
|
71
|
+
lock_state = +"No lock"
|
72
72
|
end
|
73
73
|
|
74
74
|
if info[:waiting]
|
75
75
|
lock_state << " (yielded share)"
|
76
76
|
end
|
77
77
|
|
78
|
-
msg = "Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}] #{lock_state}\n"
|
78
|
+
msg = +"Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}] #{lock_state}\n"
|
79
79
|
|
80
80
|
if info[:sleeper]
|
81
81
|
msg << " Waiting in #{info[:sleeper]}"
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pp"
|
4
|
+
|
5
|
+
require "action_view"
|
6
|
+
require "action_view/base"
|
7
|
+
|
8
|
+
module ActionDispatch
|
9
|
+
class DebugView < ActionView::Base # :nodoc:
|
10
|
+
RESCUES_TEMPLATE_PATH = File.expand_path("templates", __dir__)
|
11
|
+
|
12
|
+
def initialize(assigns)
|
13
|
+
paths = [RESCUES_TEMPLATE_PATH]
|
14
|
+
lookup_context = ActionView::LookupContext.new(paths)
|
15
|
+
super(lookup_context, assigns)
|
16
|
+
end
|
17
|
+
|
18
|
+
def compiled_method_container
|
19
|
+
self.class
|
20
|
+
end
|
21
|
+
|
22
|
+
def debug_params(params)
|
23
|
+
clean_params = params.clone
|
24
|
+
clean_params.delete("action")
|
25
|
+
clean_params.delete("controller")
|
26
|
+
|
27
|
+
if clean_params.empty?
|
28
|
+
"None"
|
29
|
+
else
|
30
|
+
PP.pp(clean_params, +"", 200)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def debug_headers(headers)
|
35
|
+
if headers.present?
|
36
|
+
headers.inspect.gsub(",", ",\n")
|
37
|
+
else
|
38
|
+
"None"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def debug_hash(object)
|
43
|
+
object.to_hash.sort_by { |k, _| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n")
|
44
|
+
end
|
45
|
+
|
46
|
+
def render(*)
|
47
|
+
logger = ActionView::Base.logger
|
48
|
+
|
49
|
+
if logger && logger.respond_to?(:silence)
|
50
|
+
logger.silence { super }
|
51
|
+
else
|
52
|
+
super
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def protect_against_forgery?
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
60
|
+
def params_valid?
|
61
|
+
@request.parameters
|
62
|
+
rescue ActionController::BadRequest
|
63
|
+
false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -12,6 +12,8 @@ module ActionDispatch
|
|
12
12
|
"ActionController::UnknownHttpMethod" => :method_not_allowed,
|
13
13
|
"ActionController::NotImplemented" => :not_implemented,
|
14
14
|
"ActionController::UnknownFormat" => :not_acceptable,
|
15
|
+
"ActionDispatch::Http::MimeNegotiation::InvalidType" => :not_acceptable,
|
16
|
+
"ActionController::MissingExactTemplate" => :not_acceptable,
|
15
17
|
"ActionController::InvalidAuthenticityToken" => :unprocessable_entity,
|
16
18
|
"ActionController::InvalidCrossOriginRequest" => :unprocessable_entity,
|
17
19
|
"ActionDispatch::Http::Parameters::ParseError" => :bad_request,
|
@@ -22,28 +24,42 @@ module ActionDispatch
|
|
22
24
|
)
|
23
25
|
|
24
26
|
cattr_accessor :rescue_templates, default: Hash.new("diagnostics").merge!(
|
25
|
-
"ActionView::MissingTemplate"
|
26
|
-
"ActionController::RoutingError"
|
27
|
-
"AbstractController::ActionNotFound"
|
28
|
-
"ActiveRecord::StatementInvalid"
|
29
|
-
"ActionView::Template::Error"
|
27
|
+
"ActionView::MissingTemplate" => "missing_template",
|
28
|
+
"ActionController::RoutingError" => "routing_error",
|
29
|
+
"AbstractController::ActionNotFound" => "unknown_action",
|
30
|
+
"ActiveRecord::StatementInvalid" => "invalid_statement",
|
31
|
+
"ActionView::Template::Error" => "template_error",
|
32
|
+
"ActionController::MissingExactTemplate" => "missing_exact_template",
|
30
33
|
)
|
31
34
|
|
32
|
-
|
35
|
+
cattr_accessor :wrapper_exceptions, default: [
|
36
|
+
"ActionView::Template::Error"
|
37
|
+
]
|
38
|
+
|
39
|
+
attr_reader :backtrace_cleaner, :exception, :wrapped_causes, :line_number, :file
|
33
40
|
|
34
41
|
def initialize(backtrace_cleaner, exception)
|
35
42
|
@backtrace_cleaner = backtrace_cleaner
|
36
|
-
@exception =
|
43
|
+
@exception = exception
|
44
|
+
@wrapped_causes = wrapped_causes_for(exception, backtrace_cleaner)
|
37
45
|
|
38
46
|
expand_backtrace if exception.is_a?(SyntaxError) || exception.cause.is_a?(SyntaxError)
|
39
47
|
end
|
40
48
|
|
49
|
+
def unwrapped_exception
|
50
|
+
if wrapper_exceptions.include?(exception.class.to_s)
|
51
|
+
exception.cause
|
52
|
+
else
|
53
|
+
exception
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
41
57
|
def rescue_template
|
42
58
|
@@rescue_templates[@exception.class.name]
|
43
59
|
end
|
44
60
|
|
45
61
|
def status_code
|
46
|
-
self.class.status_code_for_exception(
|
62
|
+
self.class.status_code_for_exception(unwrapped_exception.class.name)
|
47
63
|
end
|
48
64
|
|
49
65
|
def application_trace
|
@@ -64,7 +80,11 @@ module ActionDispatch
|
|
64
80
|
full_trace_with_ids = []
|
65
81
|
|
66
82
|
full_trace.each_with_index do |trace, idx|
|
67
|
-
trace_with_id = {
|
83
|
+
trace_with_id = {
|
84
|
+
exception_object_id: @exception.object_id,
|
85
|
+
id: idx,
|
86
|
+
trace: trace
|
87
|
+
}
|
68
88
|
|
69
89
|
if application_trace.include?(trace)
|
70
90
|
application_trace_with_ids << trace_with_id
|
@@ -97,18 +117,31 @@ module ActionDispatch
|
|
97
117
|
end
|
98
118
|
end
|
99
119
|
|
100
|
-
|
120
|
+
def trace_to_show
|
121
|
+
if traces["Application Trace"].empty? && rescue_template != "routing_error"
|
122
|
+
"Full Trace"
|
123
|
+
else
|
124
|
+
"Application Trace"
|
125
|
+
end
|
126
|
+
end
|
101
127
|
|
128
|
+
def source_to_show_id
|
129
|
+
(traces[trace_to_show].first || {})[:id]
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
102
133
|
def backtrace
|
103
134
|
Array(@exception.backtrace)
|
104
135
|
end
|
105
136
|
|
106
|
-
def
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
137
|
+
def causes_for(exception)
|
138
|
+
return enum_for(__method__, exception) unless block_given?
|
139
|
+
|
140
|
+
yield exception while exception = exception.cause
|
141
|
+
end
|
142
|
+
|
143
|
+
def wrapped_causes_for(exception, backtrace_cleaner)
|
144
|
+
causes_for(exception).map { |cause| self.class.new(backtrace_cleaner, cause) }
|
112
145
|
end
|
113
146
|
|
114
147
|
def clean_backtrace(*args)
|
@@ -38,7 +38,7 @@ module ActionDispatch
|
|
38
38
|
#
|
39
39
|
# See docs on the FlashHash class for more details about the flash.
|
40
40
|
class Flash
|
41
|
-
KEY = "action_dispatch.request.flash_hash"
|
41
|
+
KEY = "action_dispatch.request.flash_hash"
|
42
42
|
|
43
43
|
module RequestMethods
|
44
44
|
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
|