]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-135815: skip `netrc` security checks if `os.getuid` is missing (#135816)
authorBénédikt Tran <10796600+picnixz@users.noreply.github.com>
Sun, 22 Jun 2025 19:48:06 +0000 (21:48 +0200)
committerGitHub <noreply@github.com>
Sun, 22 Jun 2025 19:48:06 +0000 (19:48 +0000)
Doc/library/netrc.rst
Lib/netrc.py
Lib/test/test_netrc.py
Misc/NEWS.d/next/Library/2025-06-22-16-23-44.gh-issue-135815.0DandH.rst [new file with mode: 0644]

index f6260383b2b057a54db674054673df33db700682..74c97e8c9a975983647d83ec603435de35ee87f8 100644 (file)
@@ -24,12 +24,14 @@ the Unix :program:`ftp` program and other FTP clients.
    a :exc:`FileNotFoundError` exception will be raised.
    Parse errors will raise :exc:`NetrcParseError` with diagnostic
    information including the file name, line number, and terminating token.
+
    If no argument is specified on a POSIX system, the presence of passwords in
    the :file:`.netrc` file will raise a :exc:`NetrcParseError` if the file
    ownership or permissions are insecure (owned by a user other than the user
    running the process, or accessible for read or write by any other user).
    This implements security behavior equivalent to that of ftp and other
-   programs that use :file:`.netrc`.
+   programs that use :file:`.netrc`. Such security checks are not available
+   on platforms that do not support :func:`os.getuid`.
 
    .. versionchanged:: 3.4 Added the POSIX permission check.
 
index b285fd8e357ddbc3176ffd4c7773c3089d1e5da7..bd003e80a48081e079698d7c401e8dec56bcc263 100644 (file)
@@ -7,6 +7,19 @@ import os, stat
 __all__ = ["netrc", "NetrcParseError"]
 
 
+def _can_security_check():
+    # On WASI, getuid() is indicated as a stub but it may also be missing.
+    return os.name == 'posix' and hasattr(os, 'getuid')
+
+
+def _getpwuid(uid):
+    try:
+        import pwd
+        return pwd.getpwuid(uid)[0]
+    except (ImportError, LookupError):
+        return f'uid {uid}'
+
+
 class NetrcParseError(Exception):
     """Exception raised on syntax errors in the .netrc file."""
     def __init__(self, msg, filename=None, lineno=None):
@@ -142,18 +155,12 @@ class netrc:
             self._security_check(fp, default_netrc, self.hosts[entryname][0])
 
     def _security_check(self, fp, default_netrc, login):
-        if os.name == 'posix' and default_netrc and login != "anonymous":
+        if _can_security_check() and default_netrc and login != "anonymous":
             prop = os.fstat(fp.fileno())
-            if prop.st_uid != os.getuid():
-                import pwd
-                try:
-                    fowner = pwd.getpwuid(prop.st_uid)[0]
-                except KeyError:
-                    fowner = 'uid %s' % prop.st_uid
-                try:
-                    user = pwd.getpwuid(os.getuid())[0]
-                except KeyError:
-                    user = 'uid %s' % os.getuid()
+            current_user_id = os.getuid()
+            if prop.st_uid != current_user_id:
+                fowner = _getpwuid(prop.st_uid)
+                user = _getpwuid(current_user_id)
                 raise NetrcParseError(
                     (f"~/.netrc file owner ({fowner}, {user}) does not match"
                      " current user"))
index 81e11a293cc4c8beb04b8ef53a8734a5a6c2f0bd..9d720f627102e34599444de0206603af32771814 100644 (file)
@@ -1,11 +1,7 @@
 import netrc, os, unittest, sys, textwrap
+from test import support
 from test.support import os_helper
 
-try:
-    import pwd
-except ImportError:
-    pwd = None
-
 temp_filename = os_helper.TESTFN
 
 class NetrcTestCase(unittest.TestCase):
@@ -269,9 +265,14 @@ class NetrcTestCase(unittest.TestCase):
             machine bar.domain.com login foo password pass
             """, '#pass')
 
+    @unittest.skipUnless(support.is_wasi, 'WASI only test')
+    def test_security_on_WASI(self):
+        self.assertFalse(netrc._can_security_check())
+        self.assertEqual(netrc._getpwuid(0), 'uid 0')
+        self.assertEqual(netrc._getpwuid(123456), 'uid 123456')
 
     @unittest.skipUnless(os.name == 'posix', 'POSIX only test')
-    @unittest.skipIf(pwd is None, 'security check requires pwd module')
+    @unittest.skipUnless(hasattr(os, 'getuid'), "os.getuid is required")
     @os_helper.skip_unless_working_chmod
     def test_security(self):
         # This test is incomplete since we are normally not run as root and
diff --git a/Misc/NEWS.d/next/Library/2025-06-22-16-23-44.gh-issue-135815.0DandH.rst b/Misc/NEWS.d/next/Library/2025-06-22-16-23-44.gh-issue-135815.0DandH.rst
new file mode 100644 (file)
index 0000000..0f4a68b
--- /dev/null
@@ -0,0 +1,2 @@
+:mod:`netrc`: skip security checks if :func:`os.getuid` is missing.
+Patch by Bénédikt Tran.