VulpesOS/tools/prepare-legacy-apps.py
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

55 lines
2.5 KiB
Python

#!/usr/bin/env python3
"""Fetch three pinned historical packages and extract byte-identical test copies."""
import hashlib
import json
from pathlib import Path
import stat
import tempfile
import urllib.request
import zipfile
ROOT = Path(__file__).resolve().parents[1]
CATALOG = json.loads((ROOT / "tests/legacy-apps.json").read_text())
BASE = ROOT / "downloads/legacy-apps"
BASE.mkdir(parents=True, exist_ok=True)
(BASE / "apps").mkdir(exist_ok=True)
for app in CATALOG:
archive = BASE / app["archive"]
if not archive.exists():
with urllib.request.urlopen(app["url"], timeout=30) as response:
data = response.read(10 * 1024 * 1024 + 1)
if len(data) > 10 * 1024 * 1024:
raise RuntimeError("Archive too large")
if hashlib.sha256(data).hexdigest() != app["sha256"]:
raise RuntimeError("Archive checksum mismatch: " + app["id"])
archive.write_bytes(data)
if hashlib.sha256(archive.read_bytes()).hexdigest() != app["sha256"]:
raise RuntimeError("Archive checksum mismatch: " + app["id"])
destination = BASE / "apps" / app["id"]
with zipfile.ZipFile(archive) as package:
if sum(info.file_size for info in package.infolist()) > 50 * 1024 * 1024:
raise RuntimeError("Extracted package too large")
manifest = json.loads(package.read("manifest.webapp"))
if manifest.get("type", "web") not in ("web", "privileged"):
raise RuntimeError("Only web and reviewed privileged packages are allowed")
for info in package.infolist():
name = Path(info.filename)
if (
name.is_absolute()
or ".." in name.parts
or "\\" in info.filename
or stat.S_ISLNK(info.external_attr >> 16)
):
raise RuntimeError("Unsafe archive entry: " + info.filename)
if not destination.exists():
with tempfile.TemporaryDirectory(dir=BASE / "apps") as temporary:
stage = Path(temporary) / "package"
stage.mkdir()
package.extractall(stage)
stage.rename(destination)
for info in package.infolist():
if not info.is_dir() and (destination / info.filename).read_bytes() != package.read(
info
):
raise RuntimeError("Extracted app was modified: " + app["id"] + "/" + info.filename)
print(app["id"] + ": archive SHA256 and extracted files verified", flush=True)