51 lines
2.3 KiB
Python
51 lines
2.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Deterministic authored ZIP inputs for the bounded-input expansion guard."""
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import struct
|
|
import zlib
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
|
|
|
|
def build(output_size: int, padded_size: int | None = None) -> tuple[bytes, dict]:
|
|
plain = b"x" * output_size
|
|
compressor = zlib.compressobj(9, zlib.DEFLATED, -15)
|
|
compressed = compressor.compress(plain) + compressor.flush()
|
|
payload = compressed if padded_size is None else compressed.ljust(padded_size, b"\0")
|
|
name = b"index.html"
|
|
crc = zlib.crc32(plain)
|
|
local = struct.pack("<IHHHHHIIIHH", 0x04034B50, 20, 0x800, 8, 0, 33,
|
|
crc, len(payload), len(plain), len(name), 0)
|
|
central = struct.pack("<IHHHHHHIIIHHHHHII", 0x02014B50, (3 << 8) | 20,
|
|
20, 0x800, 8, 0, 33, crc, len(payload), len(plain),
|
|
len(name), 0, 0, 0, 0, 0o100600 << 16, 0) + name
|
|
offset = len(local) + len(name) + len(payload)
|
|
end = struct.pack("<IHHHHIIH", 0x06054B50, 0, 0, 1, 1, len(central), offset, 0)
|
|
archive = local + name + payload + central + end
|
|
return archive, {
|
|
"output_bytes": len(plain), "deflate_stream_bytes": len(compressed),
|
|
"zip_declared_compressed_bytes": len(payload),
|
|
"sha256": hashlib.sha256(archive).hexdigest(),
|
|
"output_sha256": hashlib.sha256(plain).hexdigest(),
|
|
}
|
|
|
|
|
|
def main() -> None:
|
|
manifest = {"generator": "generate.py; Python stdlib zlib, raw DEFLATE",
|
|
"content": "Authored repeated ASCII x; no external material or fonts.", "files": {}}
|
|
for name, size, padded, expectation in [
|
|
("padded-33mib.zip", 33 * 1024 * 1024, 256 * 1024, "open succeeds; extraction E_ARCHIVE_LIMIT before more than 32 MiB is sent"),
|
|
("threshold-32mib.zip", 32 * 1024 * 1024, None, "open and extraction succeed; ratio threshold is strictly greater than 32 MiB"),
|
|
("unpadded-33mib.zip", 33 * 1024 * 1024, None, "open E_ARCHIVE_LIMIT from metadata preflight"),
|
|
]:
|
|
archive, info = build(size, padded)
|
|
(ROOT / name).write_bytes(archive)
|
|
manifest["files"][name] = info | {"expected": expectation}
|
|
(ROOT / "manifest.json").write_text(json.dumps(manifest, indent=2) + "\n")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|