From 2b136ceaa8742e0516ee0aedfb3a9e7b27041172 Mon Sep 17 00:00:00 2001 From: Bruno Cabral Date: Mon, 7 Mar 2022 18:28:16 -0300 Subject: [PATCH] Fix cookie handling (#6638) * Fix cookie handling * Fix cookie handling * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update aiohttp/cookiejar.py Co-authored-by: Sam Bull Co-authored-by: Bruno Cabral Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sam Bull --- CHANGES/6638.bugfix | 1 + CONTRIBUTORS.txt | 1 + aiohttp/cookiejar.py | 36 +++++++++++++++++++----------------- tests/test_cookiejar.py | 22 ++++++++++++++++++++++ 4 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 CHANGES/6638.bugfix diff --git a/CHANGES/6638.bugfix b/CHANGES/6638.bugfix new file mode 100644 index 00000000000..8154dcfe3f3 --- /dev/null +++ b/CHANGES/6638.bugfix @@ -0,0 +1 @@ +Do not overwrite cookies with same name and domain when the path is different. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 52c6b5c3d80..5205a2ab768 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -60,6 +60,7 @@ Brian Bouterse Brian C. Lane Brian Muller Bruce Merry +Bruno Souza Cabral Bryan Kok Bryce Drennan Carl George diff --git a/aiohttp/cookiejar.py b/aiohttp/cookiejar.py index 0a2656634d1..ba39b0ae286 100644 --- a/aiohttp/cookiejar.py +++ b/aiohttp/cookiejar.py @@ -64,11 +64,11 @@ def __init__( treat_as_secure_origin: Union[StrOrURL, List[StrOrURL], None] = None, loop: Optional[asyncio.AbstractEventLoop] = None, ) -> None: - super().__init__(loop=loop) - self._cookies = defaultdict( + self._loop = asyncio.get_running_loop() + self._cookies: DefaultDict[Tuple[str, str], SimpleCookie[str]] = defaultdict( SimpleCookie - ) # type: DefaultDict[str, SimpleCookie[str]] - self._host_only_cookies = set() # type: Set[Tuple[str, str]] + ) + self._host_only_cookies: Set[Tuple[str, str]] = set() self._unsafe = unsafe self._quote_cookie = quote_cookie if treat_as_secure_origin is None: @@ -84,7 +84,7 @@ def __init__( ] self._treat_as_secure_origin = treat_as_secure_origin self._next_expiration = next_whole_second() - self._expirations = {} # type: Dict[Tuple[str, str], datetime.datetime] + self._expirations: Dict[Tuple[str, str, str], datetime.datetime] = {} # #4515: datetime.max may not be representable on 32-bit platforms self._max_time = self.MAX_TIME try: @@ -112,20 +112,20 @@ def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None: to_del = [] now = datetime.datetime.now(datetime.timezone.utc) - for domain, cookie in self._cookies.items(): + for (domain, path), cookie in self._cookies.items(): for name, morsel in cookie.items(): - key = (domain, name) + key = (domain, path, name) if ( key in self._expirations and self._expirations[key] <= now ) or predicate(morsel): to_del.append(key) - for domain, name in to_del: - key = (domain, name) - self._host_only_cookies.discard(key) + for domain, path, name in to_del: + self._host_only_cookies.discard((domain, name)) + key = (domain, path, name) if key in self._expirations: - del self._expirations[(domain, name)] - self._cookies[domain].pop(name, None) + del self._expirations[(domain, path, name)] + self._cookies[(domain, path)].pop(name, None) next_expiration = min(self._expirations.values(), default=self._max_time) try: @@ -149,9 +149,11 @@ def __len__(self) -> int: def _do_expiration(self) -> None: self.clear(lambda x: False) - def _expire_cookie(self, when: datetime.datetime, domain: str, name: str) -> None: + def _expire_cookie( + self, when: datetime.datetime, domain: str, path: str, name: str + ) -> None: self._next_expiration = min(self._next_expiration, when) - self._expirations[(domain, name)] = when + self._expirations[(domain, path, name)] = when def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: """Update cookies.""" @@ -213,7 +215,7 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No ) + datetime.timedelta(seconds=delta_seconds) except OverflowError: max_age_expiration = self._max_time - self._expire_cookie(max_age_expiration, domain, name) + self._expire_cookie(max_age_expiration, domain, path, name) except ValueError: cookie["max-age"] = "" @@ -222,11 +224,11 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No if expires: expire_time = self._parse_date(expires) if expire_time: - self._expire_cookie(expire_time, domain, name) + self._expire_cookie(expire_time, domain, path, name) else: cookie["expires"] = "" - self._cookies[domain][name] = cookie + self._cookies[(domain, path)][name] = cookie self._do_expiration() diff --git a/tests/test_cookiejar.py b/tests/test_cookiejar.py index 54f1f72802a..66f18c31d72 100644 --- a/tests/test_cookiejar.py +++ b/tests/test_cookiejar.py @@ -664,6 +664,28 @@ async def make_jar(): # Assert that there is a cookie. assert len(jar) == 1 + def test_path_filter_diff_folder_same_name(self) -> None: + async def make_jar(): + return CookieJar(unsafe=True) + + jar = self.loop.run_until_complete(make_jar()) + + jar.update_cookies( + SimpleCookie("path-cookie=zero; Domain=pathtest.com; Path=/; ") + ) + jar.update_cookies( + SimpleCookie("path-cookie=one; Domain=pathtest.com; Path=/one; ") + ) + self.assertEqual(len(jar), 2) + + jar_filtered = jar.filter_cookies(URL("http://pathtest.com/")) + self.assertEqual(len(jar_filtered), 1) + self.assertEqual(jar_filtered["path-cookie"].value, "zero") + + jar_filtered = jar.filter_cookies(URL("http://pathtest.com/one")) + self.assertEqual(len(jar_filtered), 1) + self.assertEqual(jar_filtered["path-cookie"].value, "one") + async def test_dummy_cookie_jar() -> None: cookie = SimpleCookie("foo=bar; Domain=example.com;")