|
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 |
@@ -4075,6 +4076,47 @@ def test_absolute(self): |
4075 | 4076 | tarfile.AbsolutePathError, |
4076 | 4077 | """['"].*escaped.evil['"] has an absolute path""") |
4077 | 4078 |
|
| 4079 | + def test_drive_relative(self): |
| 4080 | + # Test names and link targets that have a drive but no root. |
| 4081 | + # They are not absolute, but os.path.join() does not put them |
| 4082 | + # below a destination that has no drive or another drive. |
| 4083 | + # Windows path handling is used on all platforms. |
| 4084 | + dest = r'C:\outerdir\dest' |
| 4085 | + with support.swap_attr(os, 'path', ntpath): |
| 4086 | + for name in 'C:escaped.evil', 'x/../C:escaped.evil': |
| 4087 | + for filter in tarfile.tar_filter, tarfile.data_filter: |
| 4088 | + with self.subTest(name=name, filter=filter.__name__): |
| 4089 | + with self.assertRaises(tarfile.AbsolutePathError): |
| 4090 | + filter(tarfile.TarInfo(name), dest) |
| 4091 | + |
| 4092 | + for linkname in 'C:escaped.evil', 'x/../C:escaped.evil', './C:evil': |
| 4093 | + for type in tarfile.SYMTYPE, tarfile.LNKTYPE: |
| 4094 | + with self.subTest(linkname=linkname, type=type): |
| 4095 | + member = tarfile.TarInfo('link') |
| 4096 | + member.type = type |
| 4097 | + member.linkname = linkname |
| 4098 | + with self.assertRaises(tarfile.AbsoluteLinkError): |
| 4099 | + tarfile.data_filter(member, dest) |
| 4100 | + |
| 4101 | + member = tarfile.TarInfo('x/../C/benign.txt') |
| 4102 | + member = tarfile.data_filter(member, dest) |
| 4103 | + self.assertEqual(member.name, r'C\benign.txt') |
| 4104 | + |
| 4105 | + @unittest.skipUnless(os.name == 'nt', 'requires Windows drives') |
| 4106 | + def test_drive_relative_to_cwd(self): |
| 4107 | + # Test a name like 'C:escaped.evil' with a destination that has no |
| 4108 | + # drive. The file must not be created in the current directory. |
| 4109 | + with os_helper.temp_cwd() as cwd: |
| 4110 | + drive = os.path.splitdrive(cwd)[0] |
| 4111 | + with ArchiveMaker() as arc: |
| 4112 | + arc.add(f'{drive}escaped.evil') |
| 4113 | + |
| 4114 | + for filter in 'tar', 'data': |
| 4115 | + with self.subTest(filter=filter), arc.open() as tar: |
| 4116 | + with self.assertRaises(tarfile.AbsolutePathError): |
| 4117 | + tar.extractall('dest', filter=filter) |
| 4118 | + self.assertFalse(os.path.lexists('escaped.evil')) |
| 4119 | + |
4078 | 4120 | def test_parent_dir_out_and_back(self): |
4079 | 4121 | # Test a member that leaves the destination and comes back. |
4080 | 4122 | # The containment check looks at the resolved path, which stays |
|
0 commit comments