(
path: pathlib.Path, *, required_member: str, expected_sha256: str,
expected_size: int, require_executable_mode: bool
)
| 330 | |
| 331 | |
| 332 | def verify_zip( |
| 333 | path: pathlib.Path, *, required_member: str, expected_sha256: str, |
| 334 | expected_size: int, require_executable_mode: bool |
| 335 | ) -> None: |
| 336 | matches = 0 |
| 337 | try: |
| 338 | with zipfile.ZipFile(path, mode="r") as archive: |
| 339 | members = archive.infolist() |
| 340 | if not members or len(members) > MAX_MEMBERS: |
| 341 | raise ContractError(f"zip member count is invalid: {path.name}") |
| 342 | if sum(member.file_size for member in members) > MAX_TOTAL_MEMBER_BYTES: |
| 343 | raise ContractError(f"zip exceeds total member-size ceiling: {path.name}") |
| 344 | seen: set[str] = set() |
| 345 | for member in members: |
| 346 | name = member.filename |
| 347 | if not safe_member_name(name) or name in seen: |
| 348 | raise ContractError(f"zip has unsafe or duplicate member: {path.name}:{name}") |
| 349 | seen.add(name) |
| 350 | if zip_entry_kind(member) == "special": |
| 351 | raise ContractError(f"zip contains a special member: {path.name}:{name}") |
| 352 | if member.file_size > MAX_MEMBER_BYTES: |
| 353 | raise ContractError(f"zip member exceeds size ceiling: {path.name}:{name}") |
| 354 | if name != required_member: |
| 355 | continue |
| 356 | if zip_entry_kind(member) != "regular": |
| 357 | raise ContractError(f"required zip executable is not regular: {path.name}") |
| 358 | mode = (member.external_attr >> 16) & 0xFFFF |
| 359 | if require_executable_mode and stat.S_IMODE(mode) != 0o755: |
| 360 | raise ContractError( |
| 361 | f"required zip executable has wrong mode: {path.name}:{name}" |
| 362 | ) |
| 363 | matches += 1 |
| 364 | with archive.open(member, mode="r") as handle: |
| 365 | actual_sha256, actual_size = hash_stream( |
| 366 | handle, declared_size=member.file_size, label=f"{path.name}:{name}" |
| 367 | ) |
| 368 | if actual_sha256 != expected_sha256 or actual_size != expected_size: |
| 369 | raise ContractError(f"zip executable does not match selection: {path.name}") |
| 370 | except (zipfile.BadZipFile, EOFError, RuntimeError) as error: |
| 371 | raise ContractError(f"malformed zip archive: {path.name}") from error |
| 372 | if matches != 1: |
| 373 | raise ContractError(f"zip must contain exactly one required executable: {path.name}") |
| 374 | |
| 375 | |
| 376 | def discover_archives(root: pathlib.Path) -> dict[str, pathlib.Path]: |
nothing calls this directly
no test coverage detected