https://github.com/python/cpython/commit/7e67b36d977f480b2cc28fbab671c119d60bc11a
commit: 7e67b36d977f480b2cc28fbab671c119d60bc11a
branch: 3.14
author: Miss Islington (bot) <31488909+miss-isling...@users.noreply.github.com>
committer: serhiy-storchaka <storch...@gmail.com>
date: 2025-05-20T12:55:21Z
summary:

[3.14] gh-117596: Add more tests for os.path with invalid paths (GH-134189) 
(GH-134265)

(cherry picked from commit 871d26987533e81ab63af067e1fc96aa37a26bf7)

Co-authored-by: Serhiy Storchaka <storch...@gmail.com>

files:
M Lib/test/test_ntpath.py
M Lib/test/test_posixpath.py

diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py
index c10387b58e3f9c..f83ef225a6e48e 100644
--- a/Lib/test/test_ntpath.py
+++ b/Lib/test/test_ntpath.py
@@ -124,6 +124,22 @@ def test_splitdrive(self):
         tester('ntpath.splitdrive("//?/UNC/server/share/dir")',
                ("//?/UNC/server/share", "/dir"))
 
+    def test_splitdrive_invalid_paths(self):
+        splitdrive = ntpath.splitdrive
+        self.assertEqual(splitdrive('\\\\ser\x00ver\\sha\x00re\\di\x00r'),
+                         ('\\\\ser\x00ver\\sha\x00re', '\\di\x00r'))
+        self.assertEqual(splitdrive(b'\\\\ser\x00ver\\sha\x00re\\di\x00r'),
+                         (b'\\\\ser\x00ver\\sha\x00re', b'\\di\x00r'))
+        self.assertEqual(splitdrive("\\\\\udfff\\\udffe\\\udffd"),
+                         ('\\\\\udfff\\\udffe', '\\\udffd'))
+        if sys.platform == 'win32':
+            self.assertRaises(UnicodeDecodeError, splitdrive, 
b'\\\\\xff\\share\\dir')
+            self.assertRaises(UnicodeDecodeError, splitdrive, 
b'\\\\server\\\xff\\dir')
+            self.assertRaises(UnicodeDecodeError, splitdrive, 
b'\\\\server\\share\\\xff')
+        else:
+            self.assertEqual(splitdrive(b'\\\\\xff\\\xfe\\\xfd'),
+                             (b'\\\\\xff\\\xfe', b'\\\xfd'))
+
     def test_splitroot(self):
         tester("ntpath.splitroot('')", ('', '', ''))
         tester("ntpath.splitroot('foo')", ('', '', 'foo'))
@@ -214,6 +230,22 @@ def test_splitroot(self):
         tester('ntpath.splitroot(" :/foo")', (" :", "/", "foo"))
         tester('ntpath.splitroot("/:/foo")', ("", "/", ":/foo"))
 
+    def test_splitroot_invalid_paths(self):
+        splitroot = ntpath.splitroot
+        self.assertEqual(splitroot('\\\\ser\x00ver\\sha\x00re\\di\x00r'),
+                         ('\\\\ser\x00ver\\sha\x00re', '\\', 'di\x00r'))
+        self.assertEqual(splitroot(b'\\\\ser\x00ver\\sha\x00re\\di\x00r'),
+                         (b'\\\\ser\x00ver\\sha\x00re', b'\\', b'di\x00r'))
+        self.assertEqual(splitroot("\\\\\udfff\\\udffe\\\udffd"),
+                         ('\\\\\udfff\\\udffe', '\\', '\udffd'))
+        if sys.platform == 'win32':
+            self.assertRaises(UnicodeDecodeError, splitroot, 
b'\\\\\xff\\share\\dir')
+            self.assertRaises(UnicodeDecodeError, splitroot, 
b'\\\\server\\\xff\\dir')
+            self.assertRaises(UnicodeDecodeError, splitroot, 
b'\\\\server\\share\\\xff')
+        else:
+            self.assertEqual(splitroot(b'\\\\\xff\\\xfe\\\xfd'),
+                             (b'\\\\\xff\\\xfe', b'\\', b'\xfd'))
+
     def test_split(self):
         tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar'))
         tester('ntpath.split("\\\\conky\\mountpoint\\foo\\bar")',
@@ -226,6 +258,21 @@ def test_split(self):
         tester('ntpath.split("c:/")', ('c:/', ''))
         tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint/', 
''))
 
+    def test_split_invalid_paths(self):
+        split = ntpath.split
+        self.assertEqual(split('c:\\fo\x00o\\ba\x00r'),
+                         ('c:\\fo\x00o', 'ba\x00r'))
+        self.assertEqual(split(b'c:\\fo\x00o\\ba\x00r'),
+                         (b'c:\\fo\x00o', b'ba\x00r'))
+        self.assertEqual(split('c:\\\udfff\\\udffe'),
+                         ('c:\\\udfff', '\udffe'))
+        if sys.platform == 'win32':
+            self.assertRaises(UnicodeDecodeError, split, b'c:\\\xff\\bar')
+            self.assertRaises(UnicodeDecodeError, split, b'c:\\foo\\\xff')
+        else:
+            self.assertEqual(split(b'c:\\\xff\\\xfe'),
+                             (b'c:\\\xff', b'\xfe'))
+
     def test_isabs(self):
         tester('ntpath.isabs("foo\\bar")', 0)
         tester('ntpath.isabs("foo/bar")', 0)
@@ -333,6 +380,30 @@ def test_join(self):
         tester("ntpath.join('D:a', './c:b')", 'D:a\\.\\c:b')
         tester("ntpath.join('D:/a', './c:b')", 'D:\\a\\.\\c:b')
 
+    def test_normcase(self):
+        normcase = ntpath.normcase
+        self.assertEqual(normcase(''), '')
+        self.assertEqual(normcase(b''), b'')
+        self.assertEqual(normcase('ABC'), 'abc')
+        self.assertEqual(normcase(b'ABC'), b'abc')
+        self.assertEqual(normcase('\xc4\u0141\u03a8'), '\xe4\u0142\u03c8')
+        expected = '\u03c9\u2126' if sys.platform == 'win32' else 
'\u03c9\u03c9'
+        self.assertEqual(normcase('\u03a9\u2126'), expected)
+        if sys.platform == 'win32' or sys.getfilesystemencoding() == 'utf-8':
+            self.assertEqual(normcase('\xc4\u0141\u03a8'.encode()),
+                             '\xe4\u0142\u03c8'.encode())
+            self.assertEqual(normcase('\u03a9\u2126'.encode()),
+                             expected.encode())
+
+    def test_normcase_invalid_paths(self):
+        normcase = ntpath.normcase
+        self.assertEqual(normcase('abc\x00def'), 'abc\x00def')
+        self.assertEqual(normcase(b'abc\x00def'), b'abc\x00def')
+        self.assertEqual(normcase('\udfff'), '\udfff')
+        if sys.platform == 'win32':
+            path = b'ABC' + bytes(range(128, 256))
+            self.assertEqual(normcase(path), path.lower())
+
     def test_normpath(self):
         tester("ntpath.normpath('A//////././//.//B')", r'A\B')
         tester("ntpath.normpath('A/./B')", r'A\B')
@@ -381,6 +452,21 @@ def test_normpath(self):
         tester("ntpath.normpath('\\\\')", '\\\\')
         tester("ntpath.normpath('//?/UNC/server/share/..')", 
'\\\\?\\UNC\\server\\share\\')
 
+    def test_normpath_invalid_paths(self):
+        normpath = ntpath.normpath
+        self.assertEqual(normpath('fo\x00o'), 'fo\x00o')
+        self.assertEqual(normpath(b'fo\x00o'), b'fo\x00o')
+        self.assertEqual(normpath('fo\x00o\\..\\bar'), 'bar')
+        self.assertEqual(normpath(b'fo\x00o\\..\\bar'), b'bar')
+        self.assertEqual(normpath('\udfff'), '\udfff')
+        self.assertEqual(normpath('\udfff\\..\\foo'), 'foo')
+        if sys.platform == 'win32':
+            self.assertRaises(UnicodeDecodeError, normpath, b'\xff')
+            self.assertRaises(UnicodeDecodeError, normpath, b'\xff\\..\\foo')
+        else:
+            self.assertEqual(normpath(b'\xff'), b'\xff')
+            self.assertEqual(normpath(b'\xff\\..\\foo'), b'foo')
+
     def test_realpath_curdir(self):
         expected = ntpath.normpath(os.getcwd())
         tester("ntpath.realpath('.')", expected)
@@ -420,10 +506,6 @@ def test_realpath_basic(self):
         d = drives.pop().encode()
         self.assertEqual(ntpath.realpath(d), d)
 
-        # gh-106242: Embedded nulls and non-strict fallback to abspath
-        self.assertEqual(ABSTFN + "\0spam",
-                         ntpath.realpath(os_helper.TESTFN + "\0spam", 
strict=False))
-
     @os_helper.skip_unless_symlink
     @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
     def test_realpath_strict(self):
@@ -434,8 +516,51 @@ def test_realpath_strict(self):
         self.addCleanup(os_helper.unlink, ABSTFN)
         self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN, 
strict=True)
         self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN + "2", 
strict=True)
+
+    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
+    def test_realpath_invalid_paths(self):
+        realpath = ntpath.realpath
+        ABSTFN = ntpath.abspath(os_helper.TESTFN)
+        ABSTFNb = os.fsencode(ABSTFN)
+        path = ABSTFN + '\x00'
+        # gh-106242: Embedded nulls and non-strict fallback to abspath
+        self.assertEqual(realpath(path, strict=False), path)
         # gh-106242: Embedded nulls should raise OSError (not ValueError)
-        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "\0spam", 
strict=True)
+        self.assertRaises(OSError, realpath, path, strict=True)
+        path = ABSTFNb + b'\x00'
+        self.assertEqual(realpath(path, strict=False), path)
+        self.assertRaises(OSError, realpath, path, strict=True)
+        path = ABSTFN + '\\nonexistent\\x\x00'
+        self.assertEqual(realpath(path, strict=False), path)
+        self.assertRaises(OSError, realpath, path, strict=True)
+        path = ABSTFNb + b'\\nonexistent\\x\x00'
+        self.assertEqual(realpath(path, strict=False), path)
+        self.assertRaises(OSError, realpath, path, strict=True)
+        path = ABSTFN + '\x00\\..'
+        self.assertEqual(realpath(path, strict=False), os.getcwd())
+        self.assertEqual(realpath(path, strict=True), os.getcwd())
+        path = ABSTFNb + b'\x00\\..'
+        self.assertEqual(realpath(path, strict=False), os.getcwdb())
+        self.assertEqual(realpath(path, strict=True), os.getcwdb())
+        path = ABSTFN + '\\nonexistent\\x\x00\\..'
+        self.assertEqual(realpath(path, strict=False), ABSTFN + 
'\\nonexistent')
+        self.assertRaises(OSError, realpath, path, strict=True)
+        path = ABSTFNb + b'\\nonexistent\\x\x00\\..'
+        self.assertEqual(realpath(path, strict=False), ABSTFNb + 
b'\\nonexistent')
+        self.assertRaises(OSError, realpath, path, strict=True)
+
+        path = ABSTFNb + b'\xff'
+        self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
+        self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
+        path = ABSTFNb + b'\\nonexistent\\\xff'
+        self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
+        self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
+        path = ABSTFNb + b'\xff\\..'
+        self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
+        self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
+        path = ABSTFNb + b'\\nonexistent\\\xff\\..'
+        self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
+        self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
 
     @os_helper.skip_unless_symlink
     @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
@@ -812,8 +937,6 @@ def test_abspath(self):
         tester('ntpath.abspath("C:/nul")',  "\\\\.\\nul")
         tester('ntpath.abspath("C:\\nul")', "\\\\.\\nul")
         self.assertTrue(ntpath.isabs(ntpath.abspath("C:spam")))
-        self.assertEqual(ntpath.abspath("C:\x00"), 
ntpath.join(ntpath.abspath("C:"), "\x00"))
-        self.assertEqual(ntpath.abspath("\x00:spam"), "\x00:\\spam")
         tester('ntpath.abspath("//..")',           "\\\\")
         tester('ntpath.abspath("//../")',          "\\\\..\\")
         tester('ntpath.abspath("//../..")',        "\\\\..\\")
@@ -847,6 +970,26 @@ def test_abspath(self):
             drive, _ = ntpath.splitdrive(cwd_dir)
             tester('ntpath.abspath("/abc/")', drive + "\\abc")
 
+    def test_abspath_invalid_paths(self):
+        abspath = ntpath.abspath
+        if sys.platform == 'win32':
+            self.assertEqual(abspath("C:\x00"), ntpath.join(abspath("C:"), 
"\x00"))
+            self.assertEqual(abspath(b"C:\x00"), ntpath.join(abspath(b"C:"), 
b"\x00"))
+            self.assertEqual(abspath("\x00:spam"), "\x00:\\spam")
+            self.assertEqual(abspath(b"\x00:spam"), b"\x00:\\spam")
+        self.assertEqual(abspath('c:\\fo\x00o'), 'c:\\fo\x00o')
+        self.assertEqual(abspath(b'c:\\fo\x00o'), b'c:\\fo\x00o')
+        self.assertEqual(abspath('c:\\fo\x00o\\..\\bar'), 'c:\\bar')
+        self.assertEqual(abspath(b'c:\\fo\x00o\\..\\bar'), b'c:\\bar')
+        self.assertEqual(abspath('c:\\\udfff'), 'c:\\\udfff')
+        self.assertEqual(abspath('c:\\\udfff\\..\\foo'), 'c:\\foo')
+        if sys.platform == 'win32':
+            self.assertRaises(UnicodeDecodeError, abspath, b'c:\\\xff')
+            self.assertRaises(UnicodeDecodeError, abspath, 
b'c:\\\xff\\..\\foo')
+        else:
+            self.assertEqual(abspath(b'c:\\\xff'), b'c:\\\xff')
+            self.assertEqual(abspath(b'c:\\\xff\\..\\foo'), b'c:\\foo')
+
     def test_relpath(self):
         tester('ntpath.relpath("a")', 'a')
         tester('ntpath.relpath(ntpath.abspath("a"))', 'a')
@@ -989,6 +1132,18 @@ def test_ismount(self):
             self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$"))
             self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$\\"))
 
+    def test_ismount_invalid_paths(self):
+        ismount = ntpath.ismount
+        self.assertFalse(ismount("c:\\\udfff"))
+        if sys.platform == 'win32':
+            self.assertRaises(ValueError, ismount, "c:\\\x00")
+            self.assertRaises(ValueError, ismount, b"c:\\\x00")
+            self.assertRaises(UnicodeDecodeError, ismount, b"c:\\\xff")
+        else:
+            self.assertFalse(ismount("c:\\\x00"))
+            self.assertFalse(ismount(b"c:\\\x00"))
+            self.assertFalse(ismount(b"c:\\\xff"))
+
     def test_isreserved(self):
         self.assertFalse(ntpath.isreserved(''))
         self.assertFalse(ntpath.isreserved('.'))
@@ -1095,6 +1250,13 @@ def test_isjunction(self):
                 self.assertFalse(ntpath.isjunction('tmpdir'))
                 self.assertPathEqual(ntpath.realpath('testjunc'), 
ntpath.realpath('tmpdir'))
 
+    def test_isfile_invalid_paths(self):
+        isfile = ntpath.isfile
+        self.assertIs(isfile('/tmp\udfffabcds'), False)
+        self.assertIs(isfile(b'/tmp\xffabcds'), False)
+        self.assertIs(isfile('/tmp\x00abcds'), False)
+        self.assertIs(isfile(b'/tmp\x00abcds'), False)
+
     @unittest.skipIf(sys.platform != 'win32', "drive letters are a windows 
concept")
     def test_isfile_driveletter(self):
         drive = os.environ.get('SystemDrive')
@@ -1195,9 +1357,6 @@ def _check_function(self, func):
 
     def test_path_normcase(self):
         self._check_function(self.path.normcase)
-        if sys.platform == 'win32':
-            self.assertEqual(ntpath.normcase('\u03a9\u2126'), 'ωΩ')
-            self.assertEqual(ntpath.normcase('abc\x00def'), 'abc\x00def')
 
     def test_path_isabs(self):
         self._check_function(self.path.isabs)
diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py
index 572ffcf1aae36f..f3f9895f529470 100644
--- a/Lib/test/test_posixpath.py
+++ b/Lib/test/test_posixpath.py
@@ -222,6 +222,7 @@ def test_ismount_non_existent(self):
         finally:
             os_helper.rmdir(ABSTFN)
 
+    def test_ismount_invalid_paths(self):
         self.assertIs(posixpath.ismount('/\udfff'), False)
         self.assertIs(posixpath.ismount(b'/\xff'), False)
         self.assertIs(posixpath.ismount('/\x00'), False)
@@ -482,6 +483,79 @@ def test_realpath_strict(self):
         finally:
             os_helper.unlink(ABSTFN)
 
+    def test_realpath_invalid_paths(self):
+        path = '/\x00'
+        self.assertRaises(ValueError, realpath, path, strict=False)
+        self.assertRaises(ValueError, realpath, path, strict=True)
+        path = b'/\x00'
+        self.assertRaises(ValueError, realpath, path, strict=False)
+        self.assertRaises(ValueError, realpath, path, strict=True)
+        path = '/nonexistent/x\x00'
+        self.assertRaises(ValueError, realpath, path, strict=False)
+        self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+        path = b'/nonexistent/x\x00'
+        self.assertRaises(ValueError, realpath, path, strict=False)
+        self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+        path = '/\x00/..'
+        self.assertRaises(ValueError, realpath, path, strict=False)
+        self.assertRaises(ValueError, realpath, path, strict=True)
+        path = b'/\x00/..'
+        self.assertRaises(ValueError, realpath, path, strict=False)
+        self.assertRaises(ValueError, realpath, path, strict=True)
+        path = '/nonexistent/x\x00/..'
+        self.assertRaises(ValueError, realpath, path, strict=False)
+        self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+        path = b'/nonexistent/x\x00/..'
+        self.assertRaises(ValueError, realpath, path, strict=False)
+        self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+
+        path = '/\udfff'
+        if sys.platform == 'win32':
+            self.assertEqual(realpath(path, strict=False), path)
+            self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+        else:
+            self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
+            self.assertRaises(UnicodeEncodeError, realpath, path, strict=True)
+        path = '/nonexistent/\udfff'
+        if sys.platform == 'win32':
+            self.assertEqual(realpath(path, strict=False), path)
+        else:
+            self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
+        self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+        path = '/\udfff/..'
+        if sys.platform == 'win32':
+            self.assertEqual(realpath(path, strict=False), '/')
+            self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+        else:
+            self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
+            self.assertRaises(UnicodeEncodeError, realpath, path, strict=True)
+        path = '/nonexistent/\udfff/..'
+        if sys.platform == 'win32':
+            self.assertEqual(realpath(path, strict=False), '/nonexistent')
+        else:
+            self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
+        self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+
+        path = b'/\xff'
+        if sys.platform == 'win32':
+            self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
+            self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
+        else:
+            self.assertEqual(realpath(path, strict=False), path)
+            if support.is_wasi:
+                self.assertRaises(OSError, realpath, path, strict=True)
+            else:
+                self.assertRaises(FileNotFoundError, realpath, path, 
strict=True)
+        path = b'/nonexistent/\xff'
+        if sys.platform == 'win32':
+            self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
+        else:
+            self.assertEqual(realpath(path, strict=False), path)
+        if support.is_wasi:
+            self.assertRaises(OSError, realpath, path, strict=True)
+        else:
+            self.assertRaises(FileNotFoundError, realpath, path, strict=True)
+
     @os_helper.skip_unless_symlink
     @skip_if_ABSTFN_contains_backslash
     def test_realpath_relative(self):

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to