|
2 | 2 | import sys |
3 | 3 | import os |
4 | 4 | import io |
| 5 | +import ntpath |
5 | 6 | from hashlib import sha256 |
6 | 7 | from contextlib import contextmanager, ExitStack |
7 | 8 | from random import Random |
@@ -3716,6 +3717,47 @@ def test_absolute(self): |
3716 | 3717 | tarfile.AbsolutePathError, |
3717 | 3718 | """['"].*escaped.evil['"] has an absolute path""") |
3718 | 3719 |
|
| 3720 | + def test_drive_relative(self): |
| 3721 | + # Test names and link targets that have a drive but no root. |
| 3722 | + # They are not absolute, but os.path.join() does not put them |
| 3723 | + # below a destination that has no drive or another drive. |
| 3724 | + # Windows path handling is used on all platforms. |
| 3725 | + dest = r'C:\outerdir\dest' |
| 3726 | + with support.swap_attr(os, 'path', ntpath): |
| 3727 | + for name in 'C:escaped.evil', 'x/../C:escaped.evil': |
| 3728 | + for filter in tarfile.tar_filter, tarfile.data_filter: |
| 3729 | + with self.subTest(name=name, filter=filter.__name__): |
| 3730 | + with self.assertRaises(tarfile.AbsolutePathError): |
| 3731 | + filter(tarfile.TarInfo(name), dest) |
| 3732 | + |
| 3733 | + for linkname in 'C:escaped.evil', 'x/../C:escaped.evil', './C:evil': |
| 3734 | + for type in tarfile.SYMTYPE, tarfile.LNKTYPE: |
| 3735 | + with self.subTest(linkname=linkname, type=type): |
| 3736 | + member = tarfile.TarInfo('link') |
| 3737 | + member.type = type |
| 3738 | + member.linkname = linkname |
| 3739 | + with self.assertRaises(tarfile.AbsoluteLinkError): |
| 3740 | + tarfile.data_filter(member, dest) |
| 3741 | + |
| 3742 | + member = tarfile.TarInfo('x/../C/benign.txt') |
| 3743 | + member = tarfile.data_filter(member, dest) |
| 3744 | + self.assertEqual(member.name, r'C\benign.txt') |
| 3745 | + |
| 3746 | + @unittest.skipUnless(os.name == 'nt', 'requires Windows drives') |
| 3747 | + def test_drive_relative_to_cwd(self): |
| 3748 | + # Test a name like 'C:escaped.evil' with a destination that has no |
| 3749 | + # drive. The file must not be created in the current directory. |
| 3750 | + with os_helper.temp_cwd() as cwd: |
| 3751 | + drive = os.path.splitdrive(cwd)[0] |
| 3752 | + with ArchiveMaker() as arc: |
| 3753 | + arc.add(f'{drive}escaped.evil') |
| 3754 | + |
| 3755 | + for filter in 'tar', 'data': |
| 3756 | + with self.subTest(filter=filter), arc.open() as tar: |
| 3757 | + with self.assertRaises(tarfile.AbsolutePathError): |
| 3758 | + tar.extractall('dest', filter=filter) |
| 3759 | + self.assertFalse(os.path.lexists('escaped.evil')) |
| 3760 | + |
3719 | 3761 | def test_parent_dir_out_and_back(self): |
3720 | 3762 | # Test a member that leaves the destination and comes back. |
3721 | 3763 | # The containment check looks at the resolved path, which stays |
|
0 commit comments