From 8cf01adc8c8dbf706e4cd33bf89fd5195f638715 Mon Sep 17 00:00:00 2001 From: Gabriel Esteban Date: Mon, 26 Sep 2022 20:13:52 +0200 Subject: [PATCH] [3.8] Fix cookie handling (#6638) (#6974) * 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 (cherry picked from commit 916b3eecda825cd42415b6f8821c035647baf890) ## What do these changes do? ## Are there changes in behavior for the user? ## Related issue number ## Checklist - [ ] I think the code is well written - [ ] Unit tests for the changes exist - [ ] Documentation reflects the changes - [ ] If you provide code modification, please add yourself to `CONTRIBUTORS.txt` * The format is <Name> <Surname>. * Please keep alphabetical order, the file is sorted by names. - [ ] Add a new news fragment into the `CHANGES` folder * name it `.` for example (588.bugfix) * if you don't have an `issue_id` change it to the pr id after creating the pr * ensure type is one of the following: * `.feature`: Signifying a new feature. * `.bugfix`: Signifying a bug fix. * `.doc`: Signifying a documentation improvement. * `.removal`: Signifying a deprecation or removal of public API. * `.misc`: A ticket has been closed, but it is not of interest to users. * Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files." Co-authored-by: Bruno Cabral --- CHANGES/6638.bugfix | 1 + CONTRIBUTORS.txt | 1 + aiohttp/cookiejar.py | 32 ++++++++++++++++++-------------- tests/test_cookiejar.py | 22 ++++++++++++++++++++++ 4 files changed, 42 insertions(+), 14 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 e51f68f6f03..6c2fabbdece 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -61,6 +61,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 1ac8854a062..6c88b47e358 100644 --- a/aiohttp/cookiejar.py +++ b/aiohttp/cookiejar.py @@ -65,7 +65,9 @@ def __init__( loop: Optional[asyncio.AbstractEventLoop] = None, ) -> None: super().__init__(loop=loop) - self._cookies: DefaultDict[str, SimpleCookie[str]] = defaultdict(SimpleCookie) + self._cookies: DefaultDict[Tuple[str, str], SimpleCookie[str]] = defaultdict( + SimpleCookie + ) self._host_only_cookies: Set[Tuple[str, str]] = set() self._unsafe = unsafe self._quote_cookie = quote_cookie @@ -82,7 +84,7 @@ def __init__( ] self._treat_as_secure_origin = treat_as_secure_origin self._next_expiration = next_whole_second() - self._expirations: 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: @@ -110,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: @@ -147,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.""" @@ -211,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"] = "" @@ -220,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;")