Skip to content

Commit 3d66a90

Browse files
committed
Honor None result of filter for link fallbacks
1 parent b38be2e commit 3d66a90

2 files changed

Lines changed: 32 additions & 4 deletions

File tree

Lib/tarfile.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2841,9 +2841,11 @@ def makelink_with_filter(self, tarinfo, targetpath,
28412841
"makelink_with_filter: if filter_function is not None, "
28422842
+ "extraction_root must also not be None")
28432843
try:
2844-
filter_function(
2844+
filtered = filter_function(
28452845
unfiltered.replace(name=tarinfo.name, deep=False),
28462846
extraction_root)
2847+
if filtered is None:
2848+
return
28472849
filtered = filter_function(unfiltered, extraction_root)
28482850
except _FILTER_ERRORS as cause:
28492851
raise LinkFallbackError(tarinfo, unfiltered.name) from cause

Lib/test/test_tarfile.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4621,9 +4621,15 @@ def test_sneaky_hardlink_fallback(self):
46214621
for filter in 'tar', 'fully_trusted':
46224622
with self.subTest(filter), self.check_context(arc.open(), filter):
46234623
if not os_helper.can_symlink():
4624-
self.expect_file("a/t/dummy")
4625-
self.expect_file("b/")
4626-
self.expect_file("c/")
4624+
if filter == 'tar':
4625+
self.expect_exception(
4626+
tarfile.LinkFallbackError,
4627+
"link 'boom' would be extracted as a copy of "
4628+
+ "'c/escape', which was rejected")
4629+
else:
4630+
self.expect_file("a/t/dummy")
4631+
self.expect_file("b/")
4632+
self.expect_file("c/")
46274633
else:
46284634
self.expect_file("a/t/dummy")
46294635
self.expect_file("b/")
@@ -4820,6 +4826,26 @@ def testing_filter(member, path):
48204826
if os_helper.can_chmod():
48214827
self.assertFalse(path.stat().st_mode & stat.S_IWUSR)
48224828

4829+
@symlink_test
4830+
def test_extract_filters_target_none(self):
4831+
# Test that when extract() falls back to extracting (rather than
4832+
# linking) a hardlink target, the member is rejected if the filter
4833+
# returns None.
4834+
with ArchiveMaker() as arc:
4835+
arc.add('a/b/s', symlink_to='../escape')
4836+
arc.add('q', hardlink_to='a/b/s')
4837+
def filter_unsafe_members(member, path):
4838+
try:
4839+
return tarfile.data_filter(member, path)
4840+
except tarfile.FilterError as error:
4841+
return None
4842+
tempdir = pathlib.Path(TEMPDIR) / 'extract'
4843+
with self.check_context(arc.open(), filter_unsafe_members):
4844+
if os_helper.can_symlink():
4845+
self.expect_file('a/b/s', symlink_to='../escape')
4846+
else:
4847+
self.expect_file('a/b/') # symlink is not extracted
4848+
48234849
def test_link_fallback_normalizes(self):
48244850
# Make sure hardlink fallbacks work for non-normalized paths for all
48254851
# filters

0 commit comments

Comments
 (0)