|
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 |
7 | 8 | from random import Random |
@@ -3615,6 +3616,47 @@ def test_absolute(self): |
3615 | 3616 | tarfile.AbsolutePathError, |
3616 | 3617 | """['"].*escaped.evil['"] has an absolute path""") |
3617 | 3618 |
|
| 3619 | + def test_drive_relative(self): |
| 3620 | + # Test names and link targets that have a drive but no root. |
| 3621 | + # They are not absolute, but os.path.join() does not put them |
| 3622 | + # below a destination that has no drive or another drive. |
| 3623 | + # Windows path handling is used on all platforms. |
| 3624 | + dest = r'C:\outerdir\dest' |
| 3625 | + with support.swap_attr(os, 'path', ntpath): |
| 3626 | + for name in 'C:escaped.evil', 'x/../C:escaped.evil': |
| 3627 | + for filter in tarfile.tar_filter, tarfile.data_filter: |
| 3628 | + with self.subTest(name=name, filter=filter.__name__): |
| 3629 | + with self.assertRaises(tarfile.AbsolutePathError): |
| 3630 | + filter(tarfile.TarInfo(name), dest) |
| 3631 | + |
| 3632 | + for linkname in 'C:escaped.evil', 'x/../C:escaped.evil', './C:evil': |
| 3633 | + for type in tarfile.SYMTYPE, tarfile.LNKTYPE: |
| 3634 | + with self.subTest(linkname=linkname, type=type): |
| 3635 | + member = tarfile.TarInfo('link') |
| 3636 | + member.type = type |
| 3637 | + member.linkname = linkname |
| 3638 | + with self.assertRaises(tarfile.AbsoluteLinkError): |
| 3639 | + tarfile.data_filter(member, dest) |
| 3640 | + |
| 3641 | + member = tarfile.TarInfo('x/../C/benign.txt') |
| 3642 | + member = tarfile.data_filter(member, dest) |
| 3643 | + self.assertEqual(member.name, r'C\benign.txt') |
| 3644 | + |
| 3645 | + @unittest.skipUnless(os.name == 'nt', 'requires Windows drives') |
| 3646 | + def test_drive_relative_to_cwd(self): |
| 3647 | + # Test a name like 'C:escaped.evil' with a destination that has no |
| 3648 | + # drive. The file must not be created in the current directory. |
| 3649 | + with os_helper.temp_cwd() as cwd: |
| 3650 | + drive = os.path.splitdrive(cwd)[0] |
| 3651 | + with ArchiveMaker() as arc: |
| 3652 | + arc.add(f'{drive}escaped.evil') |
| 3653 | + |
| 3654 | + for filter in 'tar', 'data': |
| 3655 | + with self.subTest(filter=filter), arc.open() as tar: |
| 3656 | + with self.assertRaises(tarfile.AbsolutePathError): |
| 3657 | + tar.extractall('dest', filter=filter) |
| 3658 | + self.assertFalse(os.path.lexists('escaped.evil')) |
| 3659 | + |
3618 | 3660 | def test_parent_dir_out_and_back(self): |
3619 | 3661 | # Test a member that leaves the destination and comes back. |
3620 | 3662 | # The containment check looks at the resolved path, which stays |
|
0 commit comments