Archive member metadata can contain parent traversal or links even when a tar stream parses successfully.
Python tar preflight: reject links and path escapes before extraction
Operation contract
A report archive is allowed to contain regular files under a known folder. This program inspects names, rejects parent components and absolute paths, and refuses non-file members. It never extracts. In an actual ingestion path, apply a supported extraction filter, unpack into a fresh directory, and enforce member-count and total-size budgets while writing.
Failure and ownership boundary
Preflight metadata alone cannot stop a changing archive source or a race in a shared destination. Duplicate member names, case-insensitive collisions, decompression cost, and special files deserve separate checks. An extraction filter is a defense layer, not a complete quota or sandbox. This fixture makes no cross-platform extraction guarantee.
Working program
import io
import tarfile
from pathlib import PurePosixPath
archive_bytes = io.BytesIO()
with tarfile.open(fileobj=archive_bytes, mode="w") as archive:
safe = tarfile.TarInfo("reports/quarter-47.txt")
safe.size = 3
archive.addfile(safe, io.BytesIO(b"47\n"))
escape = tarfile.TarInfo("../outside.txt")
escape.size = 0
archive.addfile(escape, io.BytesIO())
link = tarfile.TarInfo("reports/shortcut")
link.type = tarfile.SYMTYPE
link.linkname = "../outside.txt"
archive.addfile(link)
archive_bytes.seek(0)
accepted = []
rejected = 0
with tarfile.open(fileobj=archive_bytes, mode="r:") as archive:
for member in archive:
name = PurePosixPath(member.name)
if (not member.isfile() or not name.parts or name.is_absolute()
or ".." in name.parts or name.parts[0] != "reports"):
rejected += 1
else:
accepted.append(member.name)
print("accepted", accepted)
print("rejected", rejected)Output
accepted ['reports/quarter-47.txt']
rejected 2Costs and limits
Preflight walks all members and stores only accepted names here. A real extractor must budget the bytes written and clean up a partial directory on failure.
Common Mistakes
- A valid tar header does not make a member path safe.
- Rejecting '..' in names does not alone solve link or destination races.
- The data extraction filter does not enforce storage, CPU, or member-count budgets.
Connected lessons
- Python ZIP extraction: validate names and decompressed budgets before owned writes
- Python bounded gzip decoding: cap expanded output before publishing it
- Python pathlib files: specify encoding and close the resource owner
Continue with Python tar extraction: reject members before exceeding a byte quota.
