Skip to content

Commit 7a6bbf8

Browse files
StanFromIrelandmiss-islington
authored andcommitted
gh-160097: Reject links replacing the destination in tarfile.tar_filter (GH-160098)
(cherry picked from commit e7e0b35) Co-authored-by: Stan Ulbrych <stan@python.org>
1 parent a737e40 commit 7a6bbf8

4 files changed

Lines changed: 20 additions & 7 deletions

File tree

‎Doc/library/tarfile.rst‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,11 @@ reused in custom filters:
10561056
- :ref:`Refuse <tarfile-extraction-refuse>` to extract files whose absolute
10571057
path (after following symlinks) would end up outside the destination.
10581058
This raises :class:`~tarfile.OutsideDestinationError`.
1059+
- :ref:`Refuse <tarfile-extraction-refuse>` to extract links (hard or soft)
1060+
whose name refers to the destination directory itself.
1061+
This raises :class:`~tarfile.OutsideDestinationError`.
1062+
Note that such files are refused even on platforms that do not support
1063+
symbolic links.
10591064
- Clear high mode bits (setuid, setgid, sticky) and group/other write bits
10601065
(:const:`~stat.S_IWGRP` | :const:`~stat.S_IWOTH`).
10611066

‎Lib/tarfile.py‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,12 @@ def _get_filtered_attrs(member, dest_path, for_data=True):
819819
strict=os.path.ALLOW_MISSING)
820820
if os.path.commonpath([target_path, dest_path]) != dest_path:
821821
raise OutsideDestinationError(member, target_path)
822+
# A link member named after the destination directory itself
823+
# would replace it with a (sym)link, redirecting the destination
824+
# for all subsequent members.
825+
if ((member.islnk() or member.issym())
826+
and os.path.normpath(name) == os.curdir):
827+
raise OutsideDestinationError(member, target_path)
822828
# Limit permissions (no high bits, and go-w)
823829
mode = member.mode
824830
if mode is not None:
@@ -854,11 +860,6 @@ def _get_filtered_attrs(member, dest_path, for_data=True):
854860
if member.islnk() or member.issym():
855861
if os.path.isabs(member.linkname):
856862
raise AbsoluteLinkError(member)
857-
# A link member that resolves to the destination directory itself
858-
# would replace it with a (sym)link, redirecting the destination
859-
# for all subsequent members.
860-
if target_path == dest_path:
861-
raise OutsideDestinationError(member, target_path)
862863
normalized = os.path.normpath(member.linkname)
863864
if normalized != member.linkname:
864865
new_attrs['linkname'] = normalized

‎Lib/test/test_tarfile.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4304,8 +4304,10 @@ def test_link_at_destination(self):
43044304
t.linkname = '.'
43054305
arc.tar_w.addfile(t)
43064306

4307-
with self.check_context(arc.open(), 'data'):
4308-
self.expect_exception(tarfile.OutsideDestinationError)
4307+
for filter in 'tar', 'data':
4308+
with self.subTest(name=name, filter=filter):
4309+
with self.check_context(arc.open(), filter):
4310+
self.expect_exception(tarfile.OutsideDestinationError)
43094311

43104312
@symlink_test
43114313
@os_helper.skip_unless_symlink
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.. cve: CVE-2026-114583
2+
3+
:func:`tarfile.tar_filter` now rejects link members whose path resolves to
4+
the destination directory itself, as :func:`tarfile.data_filter` already
5+
did.

0 commit comments

Comments
 (0)