Tar Archive Extracted Without a Filter
An archive is unpacked without checking where its entries point.
Why it matters
A tar file decides its own file names. One entry named ../../.ssh/authorized_keys or /etc/cron.d/job lands wherever it says, and a symlink entry followed by a file entry through that link does the same. If the archive came from an upload, a download or a user, the person who made it chooses what your server writes to disk.
What it looks like
This is the shape of code that triggers the rule. AI tools produce it because it works, and nothing tells them it is unsafe.
import tarfile
def unpack(upload_path, dest):
with tarfile.open(upload_path) as tar:
tar.extractall(dest)The smallest fix
with tarfile.open(path) as tar:
tar.extractall(dest, filter="data") # Python 3.12+ (backported to 3.8.17, 3.9.17, 3.10.12, 3.11.4)The better fix
If you have a few more minutes, this is the approach that holds up as the app grows.
# On older interpreters, check every member before extracting:
import os
def safe_extract(tar, dest):
dest = os.path.realpath(dest)
for m in tar.getmembers():
target = os.path.realpath(os.path.join(dest, m.name))
if not target.startswith(dest + os.sep) or m.issym() or m.islnk() or m.isdev():
raise ValueError(f"unsafe archive member: {m.name}")
tar.extractall(dest)Let your AI tool fix it
When the scanner finds this in your project, it fills in the file and line for you. This is the prompt it gives you to paste into Claude Code, Cursor or whatever you use.
How to check the fix worked
1. Build a tar with an entry named "../escape.txt"; confirm extraction raises and the file is not created outside the destination. 2. Extract a normal archive and confirm the files appear.