|
3 | 3 | import sys |
4 | 4 | import os |
5 | 5 | import io |
| 6 | +import ntpath |
6 | 7 | from hashlib import sha256 |
7 | 8 | from contextlib import contextmanager, ExitStack |
8 | 9 | from random import Random |
@@ -4298,6 +4299,47 @@ def test_absolute(self): |
4298 | 4299 | tarfile.AbsolutePathError, |
4299 | 4300 | """['"].*escaped.evil['"] has an absolute path""") |
4300 | 4301 |
|
| 4302 | + def test_drive_relative(self): |
| 4303 | + # Test names and link targets that have a drive but no root. |
| 4304 | + # They are not absolute, but os.path.join() does not put them |
| 4305 | + # below a destination that has no drive or another drive. |
| 4306 | + # Windows path handling is used on all platforms. |
| 4307 | + dest = r'C:\outerdir\dest' |
| 4308 | + with support.swap_attr(os, 'path', ntpath): |
| 4309 | + for name in 'C:escaped.evil', 'x/../C:escaped.evil': |
| 4310 | + for filter in tarfile.tar_filter, tarfile.data_filter: |
| 4311 | + with self.subTest(name=name, filter=filter.__name__): |
| 4312 | + with self.assertRaises(tarfile.AbsolutePathError): |
| 4313 | + filter(tarfile.TarInfo(name), dest) |
| 4314 | + |
| 4315 | + for linkname in 'C:escaped.evil', 'x/../C:escaped.evil', './C:evil': |
| 4316 | + for type in tarfile.SYMTYPE, tarfile.LNKTYPE: |
| 4317 | + with self.subTest(linkname=linkname, type=type): |
| 4318 | + member = tarfile.TarInfo('link') |
| 4319 | + member.type = type |
| 4320 | + member.linkname = linkname |
| 4321 | + with self.assertRaises(tarfile.AbsoluteLinkError): |
| 4322 | + tarfile.data_filter(member, dest) |
| 4323 | + |
| 4324 | + member = tarfile.TarInfo('x/../C/benign.txt') |
| 4325 | + member = tarfile.data_filter(member, dest) |
| 4326 | + self.assertEqual(member.name, r'C\benign.txt') |
| 4327 | + |
| 4328 | + @unittest.skipUnless(os.name == 'nt', 'requires Windows drives') |
| 4329 | + def test_drive_relative_to_cwd(self): |
| 4330 | + # Test a name like 'C:escaped.evil' with a destination that has no |
| 4331 | + # drive. The file must not be created in the current directory. |
| 4332 | + with os_helper.temp_cwd() as cwd: |
| 4333 | + drive = os.path.splitdrive(cwd)[0] |
| 4334 | + with ArchiveMaker() as arc: |
| 4335 | + arc.add(f'{drive}escaped.evil') |
| 4336 | + |
| 4337 | + for filter in 'tar', 'data': |
| 4338 | + with self.subTest(filter=filter), arc.open() as tar: |
| 4339 | + with self.assertRaises(tarfile.AbsolutePathError): |
| 4340 | + tar.extractall('dest', filter=filter) |
| 4341 | + self.assertFalse(os.path.lexists('escaped.evil')) |
| 4342 | + |
4301 | 4343 | def test_parent_dir_out_and_back(self): |
4302 | 4344 | # Test a member that leaves the destination and comes back. |
4303 | 4345 | # The containment check looks at the resolved path, which stays |
|
0 commit comments