From 4cd7608eb3e4ede7679fc98182e7ffedabb3140b Mon Sep 17 00:00:00 2001 From: Jan Gosmann Date: Tue, 18 Jul 2023 22:54:36 +0200 Subject: [PATCH] Fix PermissionError when loading .netrc (#7237) (#7378) ## What do these changes do? If no NETRC environment variable is provided and the .netrc path cannot be accessed due to missing permission, a PermissionError was raised instead of returning None. See issue #7237. This PR fixes the issue. If the changes look good, I can also prepare backports. ## Are there changes in behavior for the user? If the .netrc cannot be accessed due to a permission problem (and the `NETRC` environment variable is unset), no `PermissionError` will be raised. Instead it will be silently ignored. ## Related issue number Fixes #7237 (cherry picked from commit 0d2e43bf2a920975a5da4d9295e0ba887080bf5b) # Conflicts: # aiohttp/helpers.py --- CHANGES/7237.bugfix | 1 + CONTRIBUTORS.txt | 1 + aiohttp/helpers.py | 7 +++++-- tests/test_helpers.py | 21 +++++++++++++++++++++ 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 CHANGES/7237.bugfix diff --git a/CHANGES/7237.bugfix b/CHANGES/7237.bugfix new file mode 100644 index 00000000000..26f85ea9c95 --- /dev/null +++ b/CHANGES/7237.bugfix @@ -0,0 +1 @@ +Fixed ``PermissionError`` when .netrc is unreadable due to permissions. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 80bda390a61..4671e6b19e6 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -157,6 +157,7 @@ Jake Davis Jakob Ackermann Jakub Wilk Jan Buchar +Jan Gosmann Jarno Elonen Jashandeep Sohi Jean-Baptiste Estival diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index 36f80b8160d..2978356a249 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -3,6 +3,7 @@ import asyncio import base64 import binascii +import contextlib import datetime import enum import functools @@ -209,8 +210,11 @@ def netrc_from_env() -> Optional[netrc.netrc]: except netrc.NetrcParseError as e: client_logger.warning("Could not parse .netrc file: %s", e) except OSError as e: + netrc_exists = False + with contextlib.suppress(OSError): + netrc_exists = netrc_path.is_file() # we couldn't read the file (doesn't exist, permissions, etc.) - if netrc_env or netrc_path.is_file(): + if netrc_env or netrc_exists: # only warn if the environment wanted us to load it, # or it appears like the default file does actually exist client_logger.warning("Could not read .netrc file: %s", e) @@ -757,7 +761,6 @@ def ceil_timeout( class HeadersMixin: - ATTRS = frozenset(["_content_type", "_content_dict", "_stored_content_type"]) _headers: MultiMapping[str] diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 5ef664cbd0e..085c00b8cde 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -6,6 +6,7 @@ import tempfile import weakref from math import ceil, modf +from pathlib import Path from unittest import mock from urllib.request import getproxies_environment @@ -827,6 +828,26 @@ def test_netrc_from_env(expected_username: str): assert netrc_obj.authenticators("example.com")[0] == expected_username +@pytest.fixture +def protected_dir(tmp_path: Path): + protected_dir = tmp_path / "protected" + protected_dir.mkdir() + try: + protected_dir.chmod(0o600) + yield protected_dir + finally: + protected_dir.rmdir() + + +def test_netrc_from_home_does_not_raise_if_access_denied( + protected_dir: Path, monkeypatch: pytest.MonkeyPatch +): + monkeypatch.setattr(Path, "home", lambda: protected_dir) + monkeypatch.delenv("NETRC", raising=False) + + helpers.netrc_from_env() + + @pytest.mark.parametrize( ["netrc_contents", "expected_auth"], [