🔒 Repository is read-only – file editing is disabled.

PaganLinux/tmp-audit-http-sources.py main

132 linii Raw ← Powrót
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
#!/usr/bin/env python3
"""Audyt http:// w zrodlach receptur - wersja przyrostowa.

Faza A: skan receptur -> mapa host -> lista (rel, key, url). Zapisywana od razu.
Faza B: test https:// dla kazdego hosta (max 2 URL-e), wynik logowany na biezaco
        i dopisywany do cache, zeby przerwanie nie tracilo pracy.
"""
import collections
import json
import os
import re
import ssl
import sys
import urllib.error
import urllib.request

import yaml

R = "/var/lib/pagan-sync/recipes"
KEYS = ("source", "sources", "url")
SCAN = "/tmp/http-audit-scan.json"
CACHE = "/tmp/http-audit-cache.json"
UA = {"User-Agent": "pagan-audit/1.0"}


def as_list(v):
    if v is None:
        return []
    return v if isinstance(v, list) else [v]


def log(msg):
    print(msg, flush=True)


def phase_scan():
    findings = collections.defaultdict(list)
    for dp, _d, fs in os.walk(R):
        if "PAGBUILD.yaml" not in fs:
            continue
        p = os.path.join(dp, "PAGBUILD.yaml")
        rel = p[len(R) + 1:]
        try:
            d = yaml.safe_load(open(p, encoding="utf-8")) or {}
        except Exception:
            continue
        for k in KEYS:
            for v in as_list(d.get(k)):
                s = str(v)
                m = re.match(r"^https?://([^/\s]+)", s)
                if s.startswith("http://") and m:
                    findings[m.group(1)].append([rel, k, s])
    json.dump(findings, open(SCAN, "w"), indent=1, ensure_ascii=False)
    log(f"Faza A: hostow={len(findings)} wystapien={sum(len(v) for v in findings.values())}")
    log(f"zapisano {SCAN}")
    return findings


def try_url(url, timeout=12):
    ctx = ssl.create_default_context()
    try:
        req = urllib.request.Request(url, headers=UA, method="HEAD")
        with urllib.request.urlopen(req, timeout=timeout, context=ctx) as r:
            return True, r.status
    except urllib.error.HTTPError as e:
        if e.code in (403, 405, 400, 501):
            pass  # sprobuj GET
        else:
            return False, f"HTTP {e.code}"
    except Exception:
        pass
    try:
        req = urllib.request.Request(url, headers=UA, method="GET")
        with urllib.request.urlopen(req, timeout=timeout, context=ctx) as r:
            return True, r.status
    except urllib.error.HTTPError as e:
        if e.code in (403, 405):
            return True, e.code
        return False, f"HTTP {e.code}"
    except Exception as e:
        return False, type(e).__name__


def phase_test(findings):
    cache = {}
    if os.path.exists(CACHE):
        try:
            cache = json.load(open(CACHE))
        except Exception:
            cache = {}

    def save():
        json.dump(cache, open(CACHE, "w"), indent=1, ensure_ascii=False)

    results = {}
    order = sorted(findings, key=lambda h: -len(findings[h]))
    for host in order:
        urls = findings[host]
        ok_any = False
        statuses = []
        for rel, k, url in urls[:2]:
            hu = "https://" + url[len("http://"):]
            if hu in cache:
                ok, st = cache[hu]
            else:
                ok, st = try_url(hu)
                cache[hu] = [ok, st]
                save()
            statuses.append(f"{st}")
            if ok:
                ok_any = True
                break
        results[host] = {"n": len(urls), "ok": ok_any, "statuses": statuses,
                         "sample": urls[0][2]}
        log(f"{'OK  ' if ok_any else 'FAIL'} n={len(urls):4d} {host:45s} {','.join(statuses)}")
    json.dump(results, open("/tmp/http-audit-results.json", "w"), indent=1, ensure_ascii=False)
    return results


def main():
    findings = phase_scan()
    results = phase_test(findings)
    n_ok = sum(1 for r in results.values() if r["ok"])
    ent_ok = sum(r["n"] for r in results.values() if r["ok"])
    total = sum(r["n"] for r in results.values())
    log("")
    log(f"hosty https dziala: {n_ok}/{len(results)}")
    log(f"wystapien do zmiany: {ent_ok}/{total}")


if __name__ == "__main__":
    sys.exit(main())