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.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
55
tools/prepare-legacy-apps.py
Normal file
55
tools/prepare-legacy-apps.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#!/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)
|
||||
Loading…
Reference in a new issue