Skip to content

Commit f426bff

Browse files
gh-117596: Add more tests for os.path with invalid paths
1 parent af6b3b8 commit f426bff

File tree

2 files changed

+225
-14
lines changed

2 files changed

+225
-14
lines changed

Lib/test/test_ntpath.py

Lines changed: 157 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,21 @@ def test_splitdrive(self):
124124
tester('ntpath.splitdrive("//?/UNC/server/share/dir")',
125125
("//?/UNC/server/share", "/dir"))
126126

127+
def test_splitdrive_invalid_paths(self):
128+
self.assertEqual(ntpath.splitdrive('\\\\ser\x00ver\\sha\x00re\\di\x00r'),
129+
('\\\\ser\x00ver\\sha\x00re', '\\di\x00r'))
130+
self.assertEqual(ntpath.splitdrive(b'\\\\ser\x00ver\\sha\x00re\\di\x00r'),
131+
(b'\\\\ser\x00ver\\sha\x00re', b'\\di\x00r'))
132+
self.assertEqual(ntpath.splitdrive("\\\\\udfff\\\udffe\\\udffd"),
133+
('\\\\\udfff\\\udffe', '\\\udffd'))
134+
if sys.platform == 'win32':
135+
self.assertRaises(UnicodeDecodeError, ntpath.splitdrive, b'\\\\\xff\\share\\dir')
136+
self.assertRaises(UnicodeDecodeError, ntpath.splitdrive, b'\\\\server\\\xff\\dir')
137+
self.assertRaises(UnicodeDecodeError, ntpath.splitdrive, b'\\\\server\\share\\\xff')
138+
else:
139+
self.assertEqual(ntpath.splitdrive(b'\\\\\xff\\\xfe\\\xfd'),
140+
(b'\\\\\xff\\\xfe', b'\\\xfd'))
141+
127142
def test_splitroot(self):
128143
tester("ntpath.splitroot('')", ('', '', ''))
129144
tester("ntpath.splitroot('foo')", ('', '', 'foo'))
@@ -214,6 +229,21 @@ def test_splitroot(self):
214229
tester('ntpath.splitroot(" :/foo")', (" :", "/", "foo"))
215230
tester('ntpath.splitroot("/:/foo")', ("", "/", ":/foo"))
216231

232+
def test_splitroot_invalid_paths(self):
233+
self.assertEqual(ntpath.splitroot('\\\\ser\x00ver\\sha\x00re\\di\x00r'),
234+
('\\\\ser\x00ver\\sha\x00re', '\\', 'di\x00r'))
235+
self.assertEqual(ntpath.splitroot(b'\\\\ser\x00ver\\sha\x00re\\di\x00r'),
236+
(b'\\\\ser\x00ver\\sha\x00re', b'\\', b'di\x00r'))
237+
self.assertEqual(ntpath.splitroot("\\\\\udfff\\\udffe\\\udffd"),
238+
('\\\\\udfff\\\udffe', '\\', '\udffd'))
239+
if sys.platform == 'win32':
240+
self.assertRaises(UnicodeDecodeError, ntpath.splitroot, b'\\\\\xff\\share\\dir')
241+
self.assertRaises(UnicodeDecodeError, ntpath.splitroot, b'\\\\server\\\xff\\dir')
242+
self.assertRaises(UnicodeDecodeError, ntpath.splitroot, b'\\\\server\\share\\\xff')
243+
else:
244+
self.assertEqual(ntpath.splitroot(b'\\\\\xff\\\xfe\\\xfd'),
245+
(b'\\\\\xff\\\xfe', b'\\', b'\xfd'))
246+
217247
def test_split(self):
218248
tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar'))
219249
tester('ntpath.split("\\\\conky\\mountpoint\\foo\\bar")',
@@ -226,6 +256,20 @@ def test_split(self):
226256
tester('ntpath.split("c:/")', ('c:/', ''))
227257
tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint/', ''))
228258

259+
def test_split_invalid_paths(self):
260+
self.assertEqual(ntpath.split('c:\\fo\x00o\\ba\x00r'),
261+
('c:\\fo\x00o', 'ba\x00r'))
262+
self.assertEqual(ntpath.split(b'c:\\fo\x00o\\ba\x00r'),
263+
(b'c:\\fo\x00o', b'ba\x00r'))
264+
self.assertEqual(ntpath.split('c:\\\udfff\\\udffe'),
265+
('c:\\\udfff', '\udffe'))
266+
if sys.platform == 'win32':
267+
self.assertRaises(UnicodeDecodeError, ntpath.split, b'c:\\\xff\\bar')
268+
self.assertRaises(UnicodeDecodeError, ntpath.split, b'c:\\foo\\\xff')
269+
else:
270+
self.assertEqual(ntpath.split(b'c:\\\xff\\\xfe'),
271+
(b'c:\\\xff', b'\xfe'))
272+
229273
def test_isabs(self):
230274
tester('ntpath.isabs("foo\\bar")', 0)
231275
tester('ntpath.isabs("foo/bar")', 0)
@@ -333,6 +377,29 @@ def test_join(self):
333377
tester("ntpath.join('D:a', './c:b')", 'D:a\\.\\c:b')
334378
tester("ntpath.join('D:/a', './c:b')", 'D:\\a\\.\\c:b')
335379

380+
def test_normcase(self):
381+
normcase = ntpath.normcase
382+
self.assertEqual(normcase(''), '')
383+
self.assertEqual(normcase(b''), b'')
384+
self.assertEqual(normcase('ABC'), 'abc')
385+
self.assertEqual(normcase(b'ABC'), b'abc')
386+
self.assertEqual(normcase('\xc4\u0141\u03a8'), '\xe4\u0142\u03c8')
387+
expected = '\u03c9\u2126' if sys.platform == 'win32' else '\u03c9\u03c9'
388+
self.assertEqual(normcase('\u03a9\u2126'), expected)
389+
if sys.platform == 'win32' or sys.getfilesystemencoding() == 'utf-8':
390+
self.assertEqual(normcase('\xc4\u0141\u03a8'.encode()),
391+
'\xe4\u0142\u03c8'.encode())
392+
self.assertEqual(normcase('\u03a9\u2126'.encode()),
393+
expected.encode())
394+
395+
def test_normcase_invalid_paths(self):
396+
self.assertEqual(ntpath.normcase('abc\x00def'), 'abc\x00def')
397+
self.assertEqual(ntpath.normcase(b'abc\x00def'), b'abc\x00def')
398+
self.assertEqual(ntpath.normcase('\udfff'), '\udfff')
399+
if sys.platform == 'win32':
400+
path = b'ABC' + bytes(range(128, 256))
401+
self.assertEqual(ntpath.normcase(path), path.lower())
402+
336403
def test_normpath(self):
337404
tester("ntpath.normpath('A//////././//.//B')", r'A\B')
338405
tester("ntpath.normpath('A/./B')", r'A\B')
@@ -381,6 +448,21 @@ def test_normpath(self):
381448
tester("ntpath.normpath('\\\\')", '\\\\')
382449
tester("ntpath.normpath('//?/UNC/server/share/..')", '\\\\?\\UNC\\server\\share\\')
383450

451+
def test_normpath_invalid_paths(self):
452+
normpath = ntpath.normpath
453+
self.assertEqual(normpath('fo\x00o'), 'fo\x00o')
454+
self.assertEqual(normpath(b'fo\x00o'), b'fo\x00o')
455+
self.assertEqual(normpath('fo\x00o\\..\\bar'), 'bar')
456+
self.assertEqual(normpath(b'fo\x00o\\..\\bar'), b'bar')
457+
self.assertEqual(normpath('\udfff'), '\udfff')
458+
self.assertEqual(normpath('\udfff\\..\\foo'), 'foo')
459+
if sys.platform == 'win32':
460+
self.assertRaises(UnicodeDecodeError, normpath, b'\xff')
461+
self.assertRaises(UnicodeDecodeError, normpath, b'\xff\\..\\foo')
462+
else:
463+
self.assertEqual(normpath(b'\xff'), b'\xff')
464+
self.assertEqual(normpath(b'\xff\\..\\foo'), b'foo')
465+
384466
def test_realpath_curdir(self):
385467
expected = ntpath.normpath(os.getcwd())
386468
tester("ntpath.realpath('.')", expected)
@@ -420,10 +502,6 @@ def test_realpath_basic(self):
420502
d = drives.pop().encode()
421503
self.assertEqual(ntpath.realpath(d), d)
422504

423-
# gh-106242: Embedded nulls and non-strict fallback to abspath
424-
self.assertEqual(ABSTFN + "\0spam",
425-
ntpath.realpath(os_helper.TESTFN + "\0spam", strict=False))
426-
427505
@os_helper.skip_unless_symlink
428506
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
429507
def test_realpath_strict(self):
@@ -434,8 +512,51 @@ def test_realpath_strict(self):
434512
self.addCleanup(os_helper.unlink, ABSTFN)
435513
self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN, strict=True)
436514
self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN + "2", strict=True)
515+
516+
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
517+
def test_realpath_invalid_paths(self):
518+
realpath = ntpath.realpath
519+
ABSTFN = ntpath.abspath(os_helper.TESTFN)
520+
ABSTFNb = os.fsencode(ABSTFN)
521+
path = ABSTFN + '\x00'
522+
# gh-106242: Embedded nulls and non-strict fallback to abspath
523+
self.assertEqual(realpath(path, strict=False), path)
437524
# gh-106242: Embedded nulls should raise OSError (not ValueError)
438-
self.assertRaises(OSError, ntpath.realpath, ABSTFN + "\0spam", strict=True)
525+
self.assertRaises(OSError, realpath, path, strict=True)
526+
path = ABSTFNb + b'\x00'
527+
self.assertEqual(realpath(path, strict=False), path)
528+
self.assertRaises(OSError, realpath, path, strict=True)
529+
path = ABSTFN + '\\nonexistent\\x\x00'
530+
self.assertEqual(realpath(path, strict=False), path)
531+
self.assertRaises(OSError, realpath, path, strict=True)
532+
path = ABSTFNb + b'\\nonexistent\\x\x00'
533+
self.assertEqual(realpath(path, strict=False), path)
534+
self.assertRaises(OSError, realpath, path, strict=True)
535+
path = ABSTFN + '\x00\\..'
536+
self.assertEqual(realpath(path, strict=False), os.getcwd())
537+
self.assertEqual(realpath(path, strict=True), os.getcwd())
538+
path = ABSTFNb + b'\x00\\..'
539+
self.assertEqual(realpath(path, strict=False), os.getcwdb())
540+
self.assertEqual(realpath(path, strict=True), os.getcwdb())
541+
path = ABSTFN + '\\nonexistent\\x\x00\\..'
542+
self.assertEqual(realpath(path, strict=False), ABSTFN + '\\nonexistent')
543+
self.assertRaises(OSError, realpath, path, strict=True)
544+
path = ABSTFNb + b'\\nonexistent\\x\x00\\..'
545+
self.assertEqual(realpath(path, strict=False), ABSTFNb + b'\\nonexistent')
546+
self.assertRaises(OSError, realpath, path, strict=True)
547+
548+
path = ABSTFNb + b'\xff'
549+
self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
550+
self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
551+
path = ABSTFNb + b'\\nonexistent\\\xff'
552+
self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
553+
self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
554+
path = ABSTFNb + b'\xff\\..'
555+
self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
556+
self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
557+
path = ABSTFNb + b'\\nonexistent\\\xff\\..'
558+
self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
559+
self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
439560

440561
@os_helper.skip_unless_symlink
441562
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
@@ -847,6 +968,21 @@ def test_abspath(self):
847968
drive, _ = ntpath.splitdrive(cwd_dir)
848969
tester('ntpath.abspath("/abc/")', drive + "\\abc")
849970

971+
def test_abspath_invalid_paths(self):
972+
abspath = ntpath.abspath
973+
self.assertEqual(abspath('c:\\fo\x00o'), 'c:\\fo\x00o')
974+
self.assertEqual(abspath(b'c:\\fo\x00o'), b'c:\\fo\x00o')
975+
self.assertEqual(abspath('c:\\fo\x00o\\..\\bar'), 'c:\\bar')
976+
self.assertEqual(abspath(b'c:\\fo\x00o\\..\\bar'), b'c:\\bar')
977+
self.assertEqual(abspath('c:\\\udfff'), 'c:\\\udfff')
978+
self.assertEqual(abspath('c:\\\udfff\\..\\foo'), 'c:\\foo')
979+
if sys.platform == 'win32':
980+
self.assertRaises(UnicodeDecodeError, abspath, b'c:\\\xff')
981+
self.assertRaises(UnicodeDecodeError, abspath, b'c:\\\xff\\..\\foo')
982+
else:
983+
self.assertEqual(abspath(b'c:\\\xff'), b'c:\\\xff')
984+
self.assertEqual(abspath(b'c:\\\xff\\..\\foo'), b'c:\\foo')
985+
850986
def test_relpath(self):
851987
tester('ntpath.relpath("a")', 'a')
852988
tester('ntpath.relpath(ntpath.abspath("a"))', 'a')
@@ -989,6 +1125,17 @@ def test_ismount(self):
9891125
self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$"))
9901126
self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$\\"))
9911127

1128+
def test_ismount_invalid_paths(self):
1129+
self.assertFalse(ntpath.ismount("c:\\\udfff"))
1130+
if sys.platform == 'win32':
1131+
self.assertRaises(ValueError, ntpath.ismount, "c:\\\x00")
1132+
self.assertRaises(ValueError, ntpath.ismount, b"c:\\\x00")
1133+
self.assertRaises(UnicodeDecodeError, ntpath.ismount, b"c:\\\xff")
1134+
else:
1135+
self.assertFalse(ntpath.ismount("c:\\\x00"))
1136+
self.assertFalse(ntpath.ismount(b"c:\\\x00"))
1137+
self.assertFalse(ntpath.ismount(b"c:\\\xff"))
1138+
9921139
def test_isreserved(self):
9931140
self.assertFalse(ntpath.isreserved(''))
9941141
self.assertFalse(ntpath.isreserved('.'))
@@ -1095,12 +1242,11 @@ def test_isjunction(self):
10951242
self.assertFalse(ntpath.isjunction('tmpdir'))
10961243
self.assertPathEqual(ntpath.realpath('testjunc'), ntpath.realpath('tmpdir'))
10971244

1098-
@unittest.skipIf(sys.platform != 'win32', "drive letters are a windows concept")
1099-
def test_isfile_driveletter(self):
1100-
drive = os.environ.get('SystemDrive')
1101-
if drive is None or len(drive) != 2 or drive[1] != ':':
1102-
raise unittest.SkipTest('SystemDrive is not defined or malformed')
1103-
self.assertFalse(os.path.isfile('\\\\.\\' + drive))
1245+
def test_isfile_invalid_paths(self):
1246+
self.assertIs(ntpath.isfile('/tmp\udfffabcds'), False)
1247+
self.assertIs(ntpath.isfile(b'/tmp\xffabcds'), False)
1248+
self.assertIs(ntpath.isfile('/tmp\x00abcds'), False)
1249+
self.assertIs(ntpath.isfile(b'/tmp\x00abcds'), False)
11041250

11051251
@unittest.skipUnless(hasattr(os, 'pipe'), "need os.pipe()")
11061252
def test_isfile_anonymous_pipe(self):
@@ -1195,9 +1341,6 @@ def _check_function(self, func):
11951341

11961342
def test_path_normcase(self):
11971343
self._check_function(self.path.normcase)
1198-
if sys.platform == 'win32':
1199-
self.assertEqual(ntpath.normcase('\u03a9\u2126'), 'ωΩ')
1200-
self.assertEqual(ntpath.normcase('abc\x00def'), 'abc\x00def')
12011344

12021345
def test_path_isabs(self):
12031346
self._check_function(self.path.isabs)

Lib/test/test_posixpath.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ def test_ismount_non_existent(self):
229229
finally:
230230
safe_rmdir(ABSTFN)
231231

232+
def test_ismount_invalid_paths(self):
232233
self.assertIs(posixpath.ismount('/\udfff'), False)
233234
self.assertIs(posixpath.ismount(b'/\xff'), False)
234235
self.assertIs(posixpath.ismount('/\x00'), False)
@@ -489,6 +490,73 @@ def test_realpath_strict(self):
489490
finally:
490491
os_helper.unlink(ABSTFN)
491492

493+
def test_realpath_invalid_paths(self):
494+
path = '/\x00'
495+
self.assertRaises(ValueError, realpath, path, strict=False)
496+
self.assertRaises(ValueError, realpath, path, strict=True)
497+
path = b'/\x00'
498+
self.assertRaises(ValueError, realpath, path, strict=False)
499+
self.assertRaises(ValueError, realpath, path, strict=True)
500+
path = '/nonexistent/x\x00'
501+
self.assertRaises(ValueError, realpath, path, strict=False)
502+
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
503+
path = b'/nonexistent/x\x00'
504+
self.assertRaises(ValueError, realpath, path, strict=False)
505+
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
506+
path = '/\x00/..'
507+
self.assertRaises(ValueError, realpath, path, strict=False)
508+
self.assertRaises(ValueError, realpath, path, strict=True)
509+
path = b'/\x00/..'
510+
self.assertRaises(ValueError, realpath, path, strict=False)
511+
self.assertRaises(ValueError, realpath, path, strict=True)
512+
path = '/nonexistent/x\x00/..'
513+
self.assertRaises(ValueError, realpath, path, strict=False)
514+
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
515+
path = b'/nonexistent/x\x00/..'
516+
self.assertRaises(ValueError, realpath, path, strict=False)
517+
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
518+
519+
path = '/\udfff'
520+
if sys.platform == 'win32':
521+
self.assertEqual(realpath(path, strict=False), path)
522+
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
523+
else:
524+
self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
525+
self.assertRaises(UnicodeEncodeError, realpath, path, strict=True)
526+
path = '/nonexistent/\udfff'
527+
if sys.platform == 'win32':
528+
self.assertEqual(realpath(path, strict=False), path)
529+
else:
530+
self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
531+
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
532+
path = '/\udfff/..'
533+
if sys.platform == 'win32':
534+
self.assertEqual(realpath(path, strict=False), '/')
535+
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
536+
else:
537+
self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
538+
self.assertRaises(UnicodeEncodeError, realpath, path, strict=True)
539+
path = '/nonexistent/\udfff/..'
540+
if sys.platform == 'win32':
541+
self.assertEqual(realpath(path, strict=False), '/nonexistent')
542+
else:
543+
self.assertRaises(UnicodeEncodeError, realpath, path, strict=False)
544+
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
545+
546+
path = b'/\xff'
547+
if sys.platform == 'win32':
548+
self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
549+
self.assertRaises(UnicodeDecodeError, realpath, path, strict=True)
550+
else:
551+
self.assertEqual(realpath(path, strict=False), path)
552+
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
553+
path = b'/nonexistent/\xff'
554+
if sys.platform == 'win32':
555+
self.assertRaises(UnicodeDecodeError, realpath, path, strict=False)
556+
else:
557+
self.assertEqual(realpath(path, strict=False), path)
558+
self.assertRaises(FileNotFoundError, realpath, path, strict=True)
559+
492560
@os_helper.skip_unless_symlink
493561
@skip_if_ABSTFN_contains_backslash
494562
def test_realpath_relative(self):

0 commit comments

Comments
 (0)