Security Policy¶
Supported Versions¶
Version |
Status |
|---|---|
1.1.x |
Stable version |
< 1.1.0 |
not supported |
Reporting a Vulnerability¶
Please disclose security vulnerabilities privately at miurahr@linux.com
Threat model¶
py7zr parses an archive header in full when a SevenZipFile is opened, before the
caller has had any chance to inspect or accept the archive. Everything reached during
that parse runs on untrusted input, so the header parser in py7zr/archiveinfo.py is
treated as the primary attack surface.
Two classes of attack are in scope and are tested for:
- Archive content escaping the destination
A member name or symlink that resolves outside the directory passed to
extractall()– path traversal, zip-slip, symlink ladders, Windows device names and drive letters.- Archive metadata attacking the parser
A header field that costs almost nothing to write but a great deal to process. Counts in a 7z header are variable-length integers up to
2**64-1, so nine bytes can declare an arbitrary number of files, folders, coders or streams. Any count used to size a list or drive a loop must be validated before it is used.
Out of scope: the decompressed size of archive members, which a caller bounds with the
max_extract_size argument to SevenZipFile; and archives the caller has already
chosen to trust.
Hardening in the header parser¶
Counts read from a header are checked before they are used to allocate, by
check_count() in py7zr/archiveinfo.py. Two bounds are applied:
Relative – a count may not exceed what the remaining input could possibly back. The header is fully read into memory before parsing, so the number of bytes left is known exactly. This adapts to the archive and does not impose a ceiling on legitimately large ones.
Absolute – a set of
MAX_*constants caps counts whose entries cost far more memory to represent than the header bytes that declare them.
Both raise Bad7zFile. The relative bound is counted in bits per entry, because a
bit vector legitimately spends one bit per entry; a byte-granular bound would reject
valid archives.
Security regression tests¶
Every fixed vulnerability has a regression test marked @pytest.mark.security:
pytest -m security
They live in tests/test_badfiles.py (content escaping the destination) and
tests/test_badheaders.py (metadata attacking the parser), mirroring the two classes
above. See the “Security tests” section of the contributor guide for how they are
written and why they are organised that way.