#!/usr/bin/env python3 """Uzupelnia sha256sums dla receptur krytycznych (tylko wpisy SKIP). - lokalne pliki (paczki, configi) hashowane z katalogu receptury - URL-e: podstawienie ${pkgname}/${pkgver}/${pkgrel}, pobranie, hash - wpisy nierozwiazywalne (git://, brak pliku, blad sieci) zostaja jako SKIP Format pliku poza blokiem sha256sums nie jest zmieniany. """ import hashlib, os, re, sys, tempfile, urllib.request, yaml R = "/var/lib/pagan-sync/recipes" APPLY = "--apply" in sys.argv 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"} def sha256_file(path, chunk=1 << 20): h = hashlib.sha256() with open(path, "rb") as f: while True: b = f.read(chunk) if not b: break h.update(b) return h.hexdigest() def expand(s, pkgname, pkgver, pkgrel): for k, v in (("pkgname", pkgname), ("pkgver", pkgver), ("pkgrel", pkgrel)): s = s.replace("${%s}" % k, str(v)).replace("$%s" % k, str(v)) return s def as_list(v): if v is None: return [] return v if isinstance(v, list) else [v] def rewrite_sums(text, new_sums): lines = text.split("\n") out, i, handled = [], 0, False while i < len(lines): line = lines[i] if not handled and re.match(r"^sha256sums:", line): handled = True rest = line.split(":", 1)[1].strip() if rest.startswith("["): if not rest.endswith("]"): return None out.append("sha256sums: [" + ", ".join(new_sums) + "]") i += 1 continue out.append("sha256sums:") i += 1 while i < len(lines) and re.match(r"^\s*-\s*", lines[i]): i += 1 for v in new_sums: out.append("- " + v) continue out.append(line) i += 1 return "\n".join(out) if handled else None targets = [] for dp, _d, fs in os.walk(R): if "PAGBUILD.yaml" not in fs: continue p = os.path.join(dp, "PAGBUILD.yaml") try: d = yaml.safe_load(open(p, encoding="utf-8")) or {} except Exception: continue if d.get("pkgname") not in CRIT: continue sums = as_list(d.get("sha256sums")) if any(str(s).strip().upper().startswith("SKIP") for s in sums): targets.append((p, d)) print(f"receptur krytycznych z SKIP: {len(targets)}") changed = filled = failed = 0 report = [] for path, d in sorted(targets): text = open(path, encoding="utf-8").read() srcs = as_list(d.get("source")) sums = as_list(d.get("sha256sums")) rel = path[len(R) + 1:] if len(srcs) != len(sums): report.append(f"POMINIETO (src={len(srcs)} sums={len(sums)}): {rel}") failed += 1 continue new_sums, tmpfiles = [], [] for src, cur in zip(srcs, sums): if not str(cur).strip().upper().startswith("SKIP"): new_sums.append(str(cur)) continue raw = str(src) sub = expand(raw, d.get("pkgname"), d.get("pkgver"), d.get("pkgrel")) try: if sub.startswith(("http://", "https://", "ftp://")): tmp = tempfile.NamedTemporaryFile(delete=False) req = urllib.request.Request(sub, headers={"User-Agent": "pagan-audit/1.0"}) with urllib.request.urlopen(req, timeout=300) as r, open(tmp.name, "wb") as f: while True: b = r.read(1 << 20) if not b: break f.write(b) tmpfiles.append(tmp.name) h = sha256_file(tmp.name) new_sums.append(h) filled += 1 else: local = os.path.join(os.path.dirname(path), sub) if os.path.isfile(local): new_sums.append(sha256_file(local)) filled += 1 else: new_sums.append("SKIP") report.append(f" brak pliku lokalnego: {rel} :: {sub}") failed += 1 except Exception as e: new_sums.append("SKIP") report.append(f" BLAD {rel} :: {sub} ({type(e).__name__})") failed += 1 for t in tmpfiles: try: os.unlink(t) except OSError: pass new_text = rewrite_sums(text, new_sums) if new_text is None: report.append(f"POMINIETO (format sum): {rel}") failed += 1 continue if new_text != text: if APPLY: open(path, "w", encoding="utf-8").write(new_text) changed += 1 report.append(f"UZUPELNIONO: {rel}") print(f"uzupelnionych wpisow: {filled} | plikow zmienionych: {changed} | problemow: {failed}") print("tryb:", "APPLY" if APPLY else "DRY-RUN") for line in report: print(line)