#!/usr/bin/env python3 """Weryfikuje sumy SHA256 dla receptur KRYTYCZNYCH: pobiera kazde zrodlo, liczy sha256 i porownuje z suma w recepturze. To samo sprawdzenie, ktore wykrylo bledna sume tarballa util-linux 2.42.2 (pozostalosc po 2.41.x). Wyniki zapisywane przyrostowo. """ import hashlib import json import os import re import subprocess import sys import tempfile import time import yaml R = "/var/lib/pagan-sync/recipes" OUT = "/tmp/critical-sums-report.json" CRIT = ("glibc gcc binutils openssl gnutls nettle sudo shadow pam systemd dbus bash coreutils " "util-linux curl wget gnupg libgcrypt libgpg-error libassuan libksba libxcrypt make-ca " "openssh polkit seatd kernel kernel-lts kernel-mainline libcap e2fsprogs procps-ng python " "perl zlib xz tar libarchive pcre2 expat iproute2 kmod elfutils attr acl gzip bzip2 less " "sed grep findutils gawk").split() PH = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}") MAXBYTES = 600 * 1024 * 1024 def as_list(v): if v is None: return [] return v if isinstance(v, list) else [v] def expand(s, d): def rep(m): k = m.group(1) return str(d[k]) if k in d and not isinstance(d[k], (dict, list)) else m.group(0) return PH.sub(rep, s) def sha_file(path): h = hashlib.sha256() with open(path, "rb") as f: for c in iter(lambda: f.read(1 << 20), b""): h.update(c) return h.hexdigest() def check_url(url, expected): fd, path = tempfile.mkstemp(prefix="pagsum-") os.close(fd) try: p = subprocess.run(["curl", "-sS", "-L", "--max-time", "900", "-o", path, "-w", "%{http_code} %{size_download}", "-A", "curl/8.7.1", url], capture_output=True, text=True) parts = (p.stdout or "").strip().split() code = parts[0] if parts else "?" size = int(parts[1]) if len(parts) > 1 else 0 if code != "200": return "FAIL", f"HTTP {code}", size got = sha_file(path) if got == expected: return "OK", got, size return "MISMATCH", got, size finally: try: os.unlink(path) except OSError: pass def main(): report = {} results = [] targets = [] for dp, _d, fs in os.walk(R): if ".git" in dp or "PAGBUILD.yaml" not in fs: continue p = os.path.join(dp, "PAGBUILD.yaml") rel = os.path.relpath(p, R) try: d = yaml.safe_load(open(p, newline="", encoding="utf-8")) or {} except Exception: continue if d.get("pkgname") not in CRIT: continue srcs = as_list(d.get("source")) sums = as_list(d.get("sha256sums")) for i, (s, h) in enumerate(zip(srcs, sums)): if str(h).strip().upper().startswith("SKIP"): continue targets.append((rel, d, i, str(s), str(h))) print(f"zrodel do sprawdzenia: {len(targets)}", flush=True) ok = mism = fail = 0 total_bytes = 0 t0 = time.time() for n, (rel, d, i, src, expected) in enumerate(targets, 1): exp = expand(src, d) if exp.startswith(("http://", "https://", "ftp://")): status, got, size = check_url(exp, expected) total_bytes += size else: local = os.path.join(os.path.dirname(f"{R}/{rel}"), exp) if os.path.isfile(local): g = sha_file(local) status, got, size = ("OK" if g == expected else "MISMATCH"), g, os.path.getsize(local) total_bytes += size else: status, got, size = "FAIL", "brak pliku lokalnego", 0 rec = {"recipe": rel, "source": src, "expected": expected, "got": got, "status": status, "size": size} results.append(rec) if status == "OK": ok += 1 elif status == "MISMATCH": mism += 1 else: fail += 1 if status != "OK": print(f" !! {status:8s} {rel} :: {exp[:90]}", flush=True) print(f" oczekiwano: {expected}", flush=True) print(f" otrzymano : {got}", flush=True) if n % 10 == 0: print(f" ... {n}/{len(targets)} (ok={ok} mismatch={mism} fail={fail})", flush=True) json.dump(results, open(OUT, "w"), indent=1, ensure_ascii=False) json.dump(results, open(OUT, "w"), indent=1, ensure_ascii=False) print() print(f"OK: {ok} | MISMATCH (zla suma): {mism} | FAIL (nie pobrano): {fail}") print(f"pobrano lacznie: {total_bytes/1048576:.1f} MB w {time.time()-t0:.0f}s") print(f"zapisano {OUT}") if __name__ == "__main__": sys.exit(main())