Batu Lab NotesPractical developer guides

Reject archive entries that would escape an extraction folder

By Batu ยท English technical notes

Also published in our Blogger archive.

Reject member paths that resolve outside the root

The candidate target is formed below the extraction root and resolved before any write. is_relative_to returns true only when it remains under that root; a parent-path candidate fails the containment test. The fixture proves a normal nested name passes while ../x does not.

This is more precise than searching for dots in text, but it is only a containment filter. It does not write files, resolve archive-link policy, impose size limits, or choose what happens when accepted names collide.

Example

from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as d:
    root = Path(d) / 'extract'
    root.mkdir()
    safe = lambda x: (root / x).resolve().is_relative_to(root.resolve())
    assert safe('docs/a') and (not safe('../x'))
    result = 'rejected=parent-path'
    print(result)

Expected stdout:

rejected=parent-path

Sources

- zipfile.ZipFile.extract

- pathlib.PurePath.is_relative_to

Prepared with AI assistance. The example uses synthetic data; its stated limits apply.