Validate tar link targets after filter fallback - #14156
Conversation
|
Sigh, I'm really disappointed at how horrible tar unpacking has become. We just fixed a similar issue. @M0nd0R can you provide a MRE that works on the affected Python versions? |
|
@notatallshaw this seems unfortunately necessary taking a look at other affected projects: pypa/pipenv#6654 |
I'll review this as a regular bug, I don't think there should be any urgency. |
|
Thanks. Here is a standalone MRE that I ran on an actual affected interpreter, import io
import os
import shutil
import sys
import tarfile
import tempfile
print(sys.version)
base = tempfile.mkdtemp()
try:
archive = os.path.join(base, "archive.tar")
dest = os.path.join(base, "extract")
victim = os.path.join(base, "victim.txt")
with open(victim, "wb") as f:
f.write(b"safe\n")
with tarfile.open(archive, "w") as t:
d = tarfile.TarInfo("pkg")
d.type = tarfile.DIRTYPE
t.addfile(d)
link = tarfile.TarInfo("pkg/link")
link.type = tarfile.LNKTYPE
link.linkname = "../victim.txt"
t.addfile(link)
data = io.BytesIO(b"overwritten\n")
f = tarfile.TarInfo("pkg/link")
f.size = len(data.getbuffer())
t.addfile(f, data)
os.makedirs(dest)
with tarfile.open(archive) as t:
for member in t.getmembers():
try:
filtered = tarfile.data_filter(member, dest)
except tarfile.LinkOutsideDestinationError:
filtered = tarfile.tar_filter(member, dest)
t.extract(filtered, dest)
with open(victim, "rb") as f:
print("victim:", f.read().decode().strip())
finally:
shutil.rmtree(base, ignore_errors=True)Observed output: I also ran the same archive through this branch's The affected version gate is the one already present in pip's unpacking code: |
What does this PR do?
This re-checks tar hardlink and symlink targets after the CPython #107845
tarfile.data_filterfallback uses the more permissivetarfile.tar_filter. It rejects fallback link targets that resolve outside the extraction destination and adds regression coverage for an outside hardlink plus positive coverage for safe in-destination links on the affected fallback path.The pre-fix reproducer on a simulated affected
sys.version_info == (3, 11, 4)allowed a hardlink archive member to redirect a later file write outside the extraction directory. After this change, the same reproducer raisesInstallationErrorand the outside file remains unchanged.Validation
PYTHONPATH=src /tmp/pip-test-venv/bin/python -m pytest tests/unit/test_utils_unpacking.py::TestUnpackArchives::test_unpack_tar_filter_fallback_rejects_outside_hardlink tests/unit/test_utils_unpacking.py::TestUnpackArchives::test_unpack_tar_filter_fallback_allows_safe_links -qPYTHONPATH=src /tmp/pip-test-venv/bin/python -m pytest tests/unit/test_utils_unpacking.py -qPYTHONPATH=src python -m py_compile src/pip/_internal/utils/unpacking.py tests/unit/test_utils_unpacking.pygit diff --check/tmp/pip-test-venv/bin/python -m ruff check src/pip/_internal/utils/unpacking.py tests/unit/test_utils_unpacking.pyPR Checklist:
Assisted-by: OpenAI Codex