Skip to content

Commit 92cfb2f

Browse files
Handle a member that leaves the destination and comes back
1 parent aca38ab commit 92cfb2f

3 files changed

Lines changed: 30 additions & 0 deletions

File tree

‎Lib/tarfile.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,13 @@ def _get_filtered_attrs(member, dest_path, for_data=True):
833833
# For example, 'C:/foo' on Windows.
834834
raise AbsolutePathError(member)
835835
# Ensure we stay in the destination
836+
if '..' in name.replace(os.sep, '/').split('/'):
837+
# Directories are created from the name as given, so a name that
838+
# leaves the destination part-way through would create them
839+
# outside it even if the resolved path stays inside.
840+
normalized = os.path.normpath(name)
841+
if normalized != name:
842+
name = new_attrs['name'] = normalized
836843
target_path = os.path.realpath(os.path.join(dest_path, name),
837844
strict=os.path.ALLOW_MISSING)
838845
if os.path.commonpath([target_path, dest_path]) != dest_path:

‎Lib/test/test_tarfile.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4025,6 +4025,24 @@ def test_absolute(self):
40254025
tarfile.AbsolutePathError,
40264026
"""['"].*escaped.evil['"] has an absolute path""")
40274027

4028+
def test_parent_dir_out_and_back(self):
4029+
# Test a member that leaves the destination and comes back.
4030+
# The containment check looks at the resolved path, which stays
4031+
# inside, but the intermediate directories are created from the
4032+
# name as given, which does not.
4033+
with ArchiveMaker() as arc:
4034+
arc.add(f'../escaped.evil/../{self.destdir.name}/sub/file',
4035+
content='content')
4036+
4037+
with self.check_context(arc.open(), 'fully_trusted'):
4038+
self.expect_file('../escaped.evil', type=tarfile.DIRTYPE)
4039+
self.expect_file('sub/file', content='content')
4040+
4041+
for filter in 'tar', 'data':
4042+
with self.subTest(filter):
4043+
with self.check_context(arc.open(), filter):
4044+
self.expect_file('sub/file', content='content')
4045+
40284046
@symlink_test
40294047
def test_parent_symlink(self):
40304048
# Test interplaying symlinks
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fix the :mod:`tarfile` ``tar`` and ``data`` extraction filters creating
2+
directories outside the destination for members whose name leaves the
3+
destination and returns to it, such as ``../evil/../dest/sub/file``. The
4+
containment check used the resolved path, but intermediate directories were
5+
created from the name as given.

0 commit comments

Comments
 (0)