79 lines
3.5 KiB
Diff
79 lines
3.5 KiB
Diff
From 7a7f37f146aa977350cf914eba20a95ce371485f Mon Sep 17 00:00:00 2001
|
|
From: sabulikia <sabakiaei@gmail.com>
|
|
Date: Thu, 7 Jul 2022 16:10:20 -0400
|
|
Subject: [PATCH] Use string#split instead of regex for domain parts
|
|
|
|
[CVE-2023-22792]
|
|
---
|
|
.../lib/action_dispatch/middleware/cookies.rb | 48 +++++++++++--------
|
|
actionpack/test/dispatch/cookies_test.rb | 26 ++++++++++
|
|
2 files changed, 54 insertions(+), 20 deletions(-)
|
|
|
|
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
|
|
index ac5844723303a..335122adb5c73 100644
|
|
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
|
|
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
|
|
@@ -283,20 +283,6 @@ def signed_cookie_digest
|
|
class CookieJar #:nodoc:
|
|
include Enumerable, ChainedCookieJars
|
|
|
|
- # This regular expression is used to split the levels of a domain.
|
|
- # The top level domain can be any string without a period or
|
|
- # **.**, ***.** style TLDs like co.uk or com.au
|
|
- #
|
|
- # www.example.co.uk gives:
|
|
- # $& => example.co.uk
|
|
- #
|
|
- # example.com gives:
|
|
- # $& => example.com
|
|
- #
|
|
- # lots.of.subdomains.example.local gives:
|
|
- # $& => example.local
|
|
- DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
|
|
-
|
|
def self.build(req, cookies)
|
|
jar = new(req)
|
|
jar.update(cookies)
|
|
@@ -449,13 +435,35 @@ def handle_options(options)
|
|
options[:same_site] ||= cookies_same_site_protection.call(request)
|
|
|
|
if options[:domain] == :all || options[:domain] == "all"
|
|
- # If there is a provided tld length then we use it otherwise default domain regexp.
|
|
- domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
|
|
+ cookie_domain = ""
|
|
+ dot_splitted_host = request.host.split('.', -1)
|
|
+
|
|
+ # Case where request.host is not an IP address or it's an invalid domain
|
|
+ # (ip confirms to the domain structure we expect so we explicitly check for ip)
|
|
+ if request.host.match?(/^[\d.]+$/) || dot_splitted_host.include?("") || dot_splitted_host.length == 1
|
|
+ options[:domain] = nil
|
|
+ return
|
|
+ end
|
|
+
|
|
+ # If there is a provided tld length then we use it otherwise default domain.
|
|
+ if options[:tld_length].present?
|
|
+ # Case where the tld_length provided is valid
|
|
+ if dot_splitted_host.length >= options[:tld_length]
|
|
+ cookie_domain = dot_splitted_host.last(options[:tld_length]).join('.')
|
|
+ end
|
|
+ # Case where tld_length is not provided
|
|
+ else
|
|
+ # Regular TLDs
|
|
+ if !(/([^.]{2,3}\.[^.]{2})$/.match?(request.host))
|
|
+ cookie_domain = dot_splitted_host.last(2).join('.')
|
|
+ # **.**, ***.** style TLDs like co.uk and com.au
|
|
+ else
|
|
+ cookie_domain = dot_splitted_host.last(3).join('.')
|
|
+ end
|
|
+ end
|
|
|
|
- # If host is not ip and matches domain regexp.
|
|
- # (ip confirms to domain regexp so we explicitly check for ip)
|
|
- options[:domain] = if !request.host.match?(/^[\d.]+$/) && (request.host =~ domain_regexp)
|
|
- ".#{$&}"
|
|
+ options[:domain] = if cookie_domain.present?
|
|
+ ".#{cookie_domain}"
|
|
end
|
|
elsif options[:domain].is_a? Array
|
|
# If host matches one of the supplied domains.
|