🔒 Repository is read-only – file editing is disabled.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
#!/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())