From 38efcb98e4614d10c37ecb9c3a73f8362a5f295e Mon Sep 17 00:00:00 2001 From: saiyam-jain-dev <62669344+saiyam-jain-dev@users.noreply.github.com> Date: Sun, 25 Oct 2026 12:17:56 +0530 Subject: [PATCH 1/2] [py] Align _cookie_matches domain and path matching with RFC 6265 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. In domain matching, strip any optional leading dot per RFC 6265 §5.1.3 so cookies with domain 'example.com' match subdomains (e.g. sub.example.com). 2. Canonicalize cookie domain and request hostname to lowercase for case-insensitive domain matching. 3. In path matching, handle cookie paths with trailing slashes (e.g. /docs/) so that subpaths (e.g. /docs/page) match correctly instead of attempting a prefix match against double slashes (/docs//). --- .../webdriver/common/api_request_context.py | 26 +++++++++++-------- .../common/api_request_context_tests.py | 16 ++++++++++++ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/py/selenium/webdriver/common/api_request_context.py b/py/selenium/webdriver/common/api_request_context.py index 9c3a0129d16cf..c65c7dd3c7472 100644 --- a/py/selenium/webdriver/common/api_request_context.py +++ b/py/selenium/webdriver/common/api_request_context.py @@ -119,29 +119,33 @@ def _cookie_matches(cookie: dict, url: str, default_domain: str = "") -> bool: return False parsed = urllib.parse.urlparse(url) - hostname = parsed.hostname or "" + hostname = (parsed.hostname or "").lower() path = parsed.path or "/" scheme = parsed.scheme or "http" # Domain matching (RFC 6265 section 5.1.3) - cookie_domain = cookie.get("domain", "") + cookie_domain = (cookie.get("domain") or "").lower() if not cookie_domain: - # Host-only cookie — must match the origin host exactly - if not default_domain or hostname != default_domain: - return False - elif cookie_domain.startswith("."): - # .example.com matches example.com and sub.example.com - if not (hostname == cookie_domain[1:] or hostname.endswith(cookie_domain)): + # Host-only cookie - must match the origin host exactly + if not default_domain or hostname != default_domain.lower(): return False else: - if hostname != cookie_domain: + # Strip optional leading dot per RFC 6265 section 5.1.3 + norm_domain = cookie_domain[1:] if cookie_domain.startswith(".") else cookie_domain + if not (hostname == norm_domain or hostname.endswith("." + norm_domain)): return False # Path matching (RFC 6265 section 5.1.4) - cookie_path = cookie.get("path", "/") + cookie_path = cookie.get("path") or "/" if cookie_path == "/": pass # root path matches everything - elif path != cookie_path and not path.startswith(cookie_path + "/"): + elif path == cookie_path: + pass + elif cookie_path.endswith("/") and path.startswith(cookie_path): + pass + elif not cookie_path.endswith("/") and path.startswith(cookie_path + "/"): + pass + else: return False # Secure matching diff --git a/py/test/unit/selenium/webdriver/common/api_request_context_tests.py b/py/test/unit/selenium/webdriver/common/api_request_context_tests.py index e1db2a4f19227..bf6be9f808210 100644 --- a/py/test/unit/selenium/webdriver/common/api_request_context_tests.py +++ b/py/test/unit/selenium/webdriver/common/api_request_context_tests.py @@ -294,6 +294,22 @@ def test_missing_path_defaults_root(): assert _cookie_matches({"name": "a", "value": "1", "domain": "example.com"}, "http://example.com/anything") + + +def test_domain_without_leading_dot_matches_subdomain(): + assert _cookie_matches({"name": "a", "value": "1", "domain": "example.com"}, "http://sub.example.com/") + + +def test_domain_matching_is_case_insensitive(): + assert _cookie_matches({"name": "a", "value": "1", "domain": "EXAMPLE.COM"}, "http://example.com/") + assert _cookie_matches({"name": "a", "value": "1", "domain": "example.com"}, "http://EXAMPLE.COM/") + + +def test_path_with_trailing_slash_matches_subpath(): + assert _cookie_matches( + {"name": "a", "value": "1", "domain": "example.com", "path": "/docs/"}, "http://example.com/docs/page" + ) + # --- Secure --- From bc65d1e6fea4e710398af07d790bea4727023d87 Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Sun, 25 Oct 2026 13:09:02 -0400 Subject: [PATCH 2/2] Simplify and fix formatting --- py/selenium/webdriver/common/api_request_context.py | 2 +- .../selenium/webdriver/common/api_request_context_tests.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/py/selenium/webdriver/common/api_request_context.py b/py/selenium/webdriver/common/api_request_context.py index c65c7dd3c7472..a9d7acb637e30 100644 --- a/py/selenium/webdriver/common/api_request_context.py +++ b/py/selenium/webdriver/common/api_request_context.py @@ -131,7 +131,7 @@ def _cookie_matches(cookie: dict, url: str, default_domain: str = "") -> bool: return False else: # Strip optional leading dot per RFC 6265 section 5.1.3 - norm_domain = cookie_domain[1:] if cookie_domain.startswith(".") else cookie_domain + norm_domain = cookie_domain.removeprefix(".") if not (hostname == norm_domain or hostname.endswith("." + norm_domain)): return False diff --git a/py/test/unit/selenium/webdriver/common/api_request_context_tests.py b/py/test/unit/selenium/webdriver/common/api_request_context_tests.py index bf6be9f808210..056674cd4d04a 100644 --- a/py/test/unit/selenium/webdriver/common/api_request_context_tests.py +++ b/py/test/unit/selenium/webdriver/common/api_request_context_tests.py @@ -294,8 +294,6 @@ def test_missing_path_defaults_root(): assert _cookie_matches({"name": "a", "value": "1", "domain": "example.com"}, "http://example.com/anything") - - def test_domain_without_leading_dot_matches_subdomain(): assert _cookie_matches({"name": "a", "value": "1", "domain": "example.com"}, "http://sub.example.com/") @@ -310,6 +308,7 @@ def test_path_with_trailing_slash_matches_subpath(): {"name": "a", "value": "1", "domain": "example.com", "path": "/docs/"}, "http://example.com/docs/page" ) + # --- Secure ---