Skip to content

Commit e7a447a

Browse files
committed
gh-160297: Fix tarfile setting attributes through a hard link fallback
When a hard link member is extracted as a copy of a symlink member, do not apply the hard link's owner, mode and mtime to the symlink's target. ANT-2026-Y0H1Y0ER
1 parent 3b3c692 commit e7a447a

3 files changed

Lines changed: 38 additions & 0 deletions

File tree

‎Lib/tarfile.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2733,6 +2733,13 @@ def _extract_member(self, tarinfo, targetpath, set_attrs=True,
27332733
else:
27342734
self.makefile(tarinfo, targetpath)
27352735

2736+
if tarinfo.islnk() and os.path.islink(targetpath):
2737+
# makelink_with_filter() fell back to extracting a copy of a
2738+
# symlink member, which already has its own attributes set.
2739+
# Setting the hard link's attributes here would follow the
2740+
# symlink, possibly to a file outside the destination directory.
2741+
set_attrs = False
2742+
27362743
if set_attrs:
27372744
self.chown(tarinfo, targetpath, numeric_owner)
27382745
if not tarinfo.issym():

‎Lib/test/test_tarfile.py‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4825,6 +4825,33 @@ def test_sneaky_hardlink_fallback_deep(self):
48254825
self.expect_file("a/b/s", symlink_to=os.path.join('..', 'escape'))
48264826
self.expect_file("s", symlink_to=os.path.join('..', 'escape'))
48274827

4828+
@symlink_test
4829+
def test_sneaky_hardlink_fallback_attrs(self):
4830+
# Test that when a hardlink is extracted as a copy of a symlink
4831+
# member, the hardlink's attributes are not applied to the file
4832+
# the symlink points to.
4833+
with ArchiveMaker() as arc:
4834+
arc.add("a/s", symlink_to=os.path.join("..", "flag"))
4835+
arc.add("s", hardlink_to=os.path.join("a", "s"),
4836+
mode='-rwxr-xr-x', mtime=86400, uid=1337, gid=1337)
4837+
4838+
for filter in 'tar', 'fully_trusted':
4839+
with (
4840+
self.subTest(filter),
4841+
unittest.mock.patch("os.chown", create=True) as mock_chown,
4842+
self.check_context(arc.open(), filter) as cc,
4843+
):
4844+
if not os_helper.can_symlink():
4845+
self.expect_file("a/")
4846+
else:
4847+
self.expect_file("a/s", symlink_to=os.path.join('..', 'flag'))
4848+
self.expect_file("s", symlink_to=os.path.join('..', 'flag'))
4849+
st = (cc.outerdir / 'flag').stat()
4850+
self.assertNotEqual(st.st_mtime, 86400)
4851+
if sys.platform != "win32":
4852+
self.assertNotEqual(st.st_mode & 0o777, 0o755)
4853+
mock_chown.assert_not_called()
4854+
48284855
@symlink_test
48294856
@os_helper.skip_unless_hardlink
48304857
def test_sneaky_hardlink_relocation(self):
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix :meth:`tarfile.TarFile.extractall` and :meth:`tarfile.TarFile.extract`
2+
changing the mode, modification time and owner of a file outside the
3+
destination directory when a hard link member is extracted as a copy of a
4+
symbolic link member, for example with the ``'tar'`` filter.

0 commit comments

Comments
 (0)