https://github.com/python/cpython/commit/e7e0b3523ad6ba8472873dda594cf4021a16f8b5
commit: e7e0b3523ad6ba8472873dda594cf4021a16f8b5
branch: main
author: Stan Ulbrych <[email protected]>
committer: StanFromIreland <[email protected]>
date: 2026-10-22T21:23:08+01:00
summary:
gh-160097: Reject links replacing the destination in `tarfile.tar_filter`
(#160098)
files:
A Misc/NEWS.d/next/Security/2026-10-21-19-58-53.gh-issue-160097.-opfAL.rst
M Doc/library/tarfile.rst
M Lib/tarfile.py
M Lib/test/test_tarfile.py
diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst
index 1b2c328f9cad9f..06eba01d21e526 100644
--- a/Doc/library/tarfile.rst
+++ b/Doc/library/tarfile.rst
@@ -1131,6 +1131,11 @@ reused in custom filters:
- :ref:`Refuse <tarfile-extraction-refuse>` to extract files whose absolute
path (after following symlinks) would end up outside the destination.
This raises :class:`~tarfile.OutsideDestinationError`.
+ - :ref:`Refuse <tarfile-extraction-refuse>` to extract links (hard or soft)
+ whose name refers to the destination directory itself.
+ This raises :class:`~tarfile.OutsideDestinationError`.
+ Note that such files are refused even on platforms that do not support
+ symbolic links.
- Clear high mode bits (setuid, setgid, sticky) and group/other write bits
(:const:`~stat.S_IWGRP` | :const:`~stat.S_IWOTH`).
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index 41394171832cbc..57bba1bba54db3 100644
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -854,6 +854,12 @@ def _get_filtered_attrs(member, dest_path, for_data=True):
strict=os.path.ALLOW_MISSING)
if os.path.commonpath([target_path, dest_path]) != dest_path:
raise OutsideDestinationError(member, target_path)
+ # A link member named after the destination directory itself
+ # would replace it with a (sym)link, redirecting the destination
+ # for all subsequent members.
+ if ((member.islnk() or member.issym())
+ and os.path.normpath(name) == os.curdir):
+ raise OutsideDestinationError(member, target_path)
# Limit permissions (no high bits, and go-w)
mode = member.mode
if mode is not None:
@@ -889,11 +895,6 @@ def _get_filtered_attrs(member, dest_path, for_data=True):
if member.islnk() or member.issym():
if os.path.isabs(member.linkname):
raise AbsoluteLinkError(member)
- # A link member that resolves to the destination directory itself
- # would replace it with a (sym)link, redirecting the destination
- # for all subsequent members.
- if target_path == dest_path:
- raise OutsideDestinationError(member, target_path)
normalized = os.path.normpath(member.linkname)
if normalized != member.linkname:
new_attrs['linkname'] = normalized
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index dba51d7131aa35..639fd0449a989a 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -4671,8 +4671,10 @@ def test_link_at_destination(self):
t.linkname = '.'
arc.tar_w.addfile(t)
- with self.check_context(arc.open(), 'data'):
- self.expect_exception(tarfile.OutsideDestinationError)
+ for filter in 'tar', 'data':
+ with self.subTest(name=name, filter=filter):
+ with self.check_context(arc.open(), filter):
+ self.expect_exception(tarfile.OutsideDestinationError)
@symlink_test
@os_helper.skip_unless_symlink
diff --git
a/Misc/NEWS.d/next/Security/2026-10-21-19-58-53.gh-issue-160097.-opfAL.rst
b/Misc/NEWS.d/next/Security/2026-10-21-19-58-53.gh-issue-160097.-opfAL.rst
new file mode 100644
index 00000000000000..b48c8f7213ee82
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2026-10-21-19-58-53.gh-issue-160097.-opfAL.rst
@@ -0,0 +1,5 @@
+.. cve: CVE-2026-114583
+
+:func:`tarfile.tar_filter` now rejects link members whose path resolves to
+the destination directory itself, as :func:`tarfile.data_filter` already
+did.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]