Skip to content

Commit 69fb4b6

Browse files
encukouStanFromIreland
authored andcommitted
gh-157265: tarfile: Honor None result of filter for link fallbacks (GH-157266)
(cherry picked from commit fb2f0bb) Co-authored-by: Petr Viktorin <encukou@gmail.com> Co-authored-by: Stan Ulbrych <stan@python.org>
1 parent c016c25 commit 69fb4b6

3 files changed

Lines changed: 34 additions & 4 deletions

File tree

Lib/tarfile.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2687,9 +2687,11 @@ def makelink_with_filter(self, tarinfo, targetpath,
26872687
"makelink_with_filter: if filter_function is not None, "
26882688
+ "extraction_root must also not be None")
26892689
try:
2690-
filter_function(
2690+
filtered = filter_function(
26912691
unfiltered.replace(name=tarinfo.name, deep=False),
26922692
extraction_root)
2693+
if filtered is None:
2694+
return
26932695
filtered = filter_function(unfiltered, extraction_root)
26942696
except _FILTER_ERRORS as cause:
26952697
raise LinkFallbackError(tarinfo, unfiltered.name) from cause

Lib/test/test_tarfile.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4186,9 +4186,15 @@ def test_sneaky_hardlink_fallback(self):
41864186
for filter in 'tar', 'fully_trusted':
41874187
with self.subTest(filter), self.check_context(arc.open(), filter):
41884188
if not os_helper.can_symlink():
4189-
self.expect_file("a/t/dummy")
4190-
self.expect_file("b/")
4191-
self.expect_file("c/")
4189+
if filter == 'tar':
4190+
self.expect_exception(
4191+
tarfile.LinkFallbackError,
4192+
"link 'boom' would be extracted as a copy of "
4193+
+ "'c/escape', which was rejected")
4194+
else:
4195+
self.expect_file("a/t/dummy")
4196+
self.expect_file("b/")
4197+
self.expect_file("c/")
41924198
else:
41934199
self.expect_file("a/t/dummy")
41944200
self.expect_file("b/")
@@ -4367,6 +4373,25 @@ def testing_filter(member, path):
43674373
if os_helper.can_chmod():
43684374
self.assertFalse(path.stat().st_mode & stat.S_IWUSR)
43694375

4376+
@symlink_test
4377+
def test_extract_filters_target_none(self):
4378+
# Test that when extract() falls back to extracting (rather than
4379+
# linking) a hardlink target, the member is skipped if the filter
4380+
# returns None.
4381+
with ArchiveMaker() as arc:
4382+
arc.add('a/b/s', symlink_to='../escape')
4383+
arc.add('q', hardlink_to='a/b/s')
4384+
def filter_unsafe_members(member, path):
4385+
try:
4386+
return tarfile.data_filter(member, path)
4387+
except tarfile.FilterError as error:
4388+
return None
4389+
with self.check_context(arc.open(), filter_unsafe_members):
4390+
if os_helper.can_symlink():
4391+
self.expect_file('a/b/s', symlink_to='../escape')
4392+
else:
4393+
self.expect_file('a/b/') # symlink is not extracted
4394+
43704395
def test_link_fallback_normalizes(self):
43714396
# Make sure hardlink fallbacks work for non-normalized paths for all
43724397
# filters
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
In :mod:`tarfile`, when extracting a link falls back to extracting a member
2+
of the archive, skip the member when the filter function returns None when
3+
called with the extracted member's name replaced with the link's.

0 commit comments

Comments
 (0)