Agent Wranglers

Requests and files Required

Unpack archives safely

Keep every entry of an uploaded zip or tar file inside the target folder, and stop unpacking at a fixed total size and file count.

How to do it

Check each entry's name before writing it, as in keep file paths inside their folder. Skip links and device files. Count the bytes as you unpack, and stop at a cap such as 100 MB and 1,000 files. A small archive can expand to many gigabytes. Apply the same caps to compressed request bodies.

Example

# Python 3.12 or later: the data filter refuses paths outside dest, links out and device files
MAX_BYTES, MAX_FILES = 100 * 1024 * 1024, 1000
with tarfile.open(upload_path) as tar:
    members = tar.getmembers()
    if len(members) > MAX_FILES or sum(m.size for m in members) > MAX_BYTES:
        raise ValueError("archive too large")
    tar.extractall(dest, filter="data")
// Go 1.20 or later: filepath.IsLocal refuses absolute paths and ".."
var total uint64
for count, entry := range zipReader.File {
	if !filepath.IsLocal(entry.Name) || entry.Mode()&(fs.ModeSymlink|fs.ModeDevice) != 0 {
		return errors.New("unsafe entry: " + entry.Name)
	}
	total += entry.UncompressedSize64 // the header can lie, so also copy through io.LimitReader
	if count >= maxFiles || total > maxBytes {
		return errors.New("archive too large")
	}
}

How to check

The agent uploads an archive with an entry named ../../marker.txt, one with a link to /etc, and one that expands past the cap. All three are refused, and no file appears outside the target folder.

For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers security testing playbook: https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/safe-archive-extraction.