#!/usr/bin/env python3 """ PeerCortex Final Quality Audit v4 Tests 103 ASNs against v0.6.7 (running, stable) Uses cached responses where available, skips ASNs that don't respond in 15s Cross-validates: prefix counts, RPKI math, IX dedup, facilities, RIR, country """ import json, urllib.request, time, sys from datetime import datetime, timezone def fetch(url, timeout=15): try: req = urllib.request.Request(url, headers={"User-Agent": "PeerCortex-Audit/4.0"}) with urllib.request.urlopen(req, timeout=timeout) as r: return json.loads(r.read()) except Exception as e: return None ASNS = [ 1, 174, 714, 1299, 2497, 2516, 2906, 3257, 3303, 3320, 3356, 4134, 4766, 5089, 5483, 5511, 6057, 6128, 6147, 6453, 6695, 6762, 6830, 6939, 7018, 7473, 7545, 7922, 8075, 8151, 8220, 8422, 8708, 9121, 9304, 9318, 9498, 10429, 10474, 10796, 12479, 12714, 12956, 13237, 13335, 14080, 15133, 15169, 15600, 15704, 16276, 16509, 17676, 18403, 19281, 20115, 20562, 20932, 20940, 21928, 22773, 22822, 23106, 24115, 24429, 24940, 24961, 25091, 29049, 32934, 33891, 34465, 34549, 35369, 37100, 38001, 39122, 41327, 42, 42739, 43350, 43996, 44574, 45899, 47692, 48159, 50629, 51088, 54113, 56630, 57695, 58057, 59947, 60068, 60501, 62240, 64271, 132335, 136907, 137409, 139070, 197540, 199121, ] results = {"ok": 0, "warn": 0, "crit": 0, "skip": 0, "total": len(ASNS)} all_issues = [] responded = [] print(f"PeerCortex Final Audit v4 — {datetime.now(timezone.utc).isoformat()}") print(f"Target: http://localhost:3101 | ASNs: {len(ASNS)}") print("=" * 75) for i, asn in enumerate(ASNS): sys.stdout.write(f"\r [{i+1:3d}/{len(ASNS)}] AS{asn}... ") sys.stdout.flush() d = fetch(f"http://localhost:3101/api/lookup?asn={asn}", timeout=15) if not d or "network" not in d or d.get("error"): results["skip"] += 1 continue responded.append(asn) n = d.get("network", {}) p = d.get("prefixes", {}) r = d.get("rpki", {}) ix = d.get("ix_presence", {}) fac = d.get("facilities", {}) meta= d.get("meta", {}) reg = d.get("registration", {}) issues = [] # ── Math checks ───────────────────────────────────────── if p.get("ipv4", 0) + p.get("ipv6", 0) != p.get("total", -1): issues.append(("CRITICAL", "prefix_math", f"v4({p.get('ipv4')})+v6({p.get('ipv6')})={p.get('ipv4',0)+p.get('ipv6',0)} ≠ total({p.get('total')})")) rpki_sum = r.get("valid",0) + r.get("invalid",0) + r.get("not_found",0) if rpki_sum != r.get("checked", 0): issues.append(("CRITICAL", "rpki_sum", f"{rpki_sum} ≠ checked({r.get('checked')})")) if r.get("checked", 0) > 0: exp = round(r.get("valid",0) / r.get("checked") * 100) if abs(exp - r.get("coverage_percent", 0)) > 1: issues.append(("CRITICAL", "rpki_pct", f"expected {exp}% got {r.get('coverage_percent')}%")) conns = ix.get("connections", []) dedup = len(set(c.get("ix_id") for c in conns if c.get("ix_id"))) if dedup != ix.get("unique_ixps", 0): issues.append(("CRITICAL", "ix_dedup", f"dedup={dedup} ≠ unique_ixps={ix.get('unique_ixps')}")) if len(conns) != ix.get("total_connections", 0): issues.append(("WARNING", "ix_total", f"len={len(conns)} ≠ total_connections={ix.get('total_connections')}")) if len(fac.get("list",[])) != fac.get("total", 0): issues.append(("WARNING", "fac_total", f"len={len(fac.get('list',[]))} ≠ total={fac.get('total')}")) # ── Mandatory fields ───────────────────────────────────── if not n.get("name") or n.get("name") == "Unknown": issues.append(("CRITICAL", "name_empty", f"name={n.get('name')!r}")) if not n.get("asn"): issues.append(("CRITICAL", "asn_empty", "ASN field missing")) if not n.get("rir") and not reg.get("rir"): issues.append(("WARNING", "rir_empty", f"rir empty, country={n.get('country')!r}")) if not n.get("country"): issues.append(("WARNING", "country_empty", "country field empty")) if meta.get("version") != "0.6.7": issues.append(("WARNING", "version", f"got {meta.get('version')}")) # ── Categorize ─────────────────────────────────────────── crits = [x for x in issues if x[0] == "CRITICAL"] warns = [x for x in issues if x[0] == "WARNING"] if crits: results["crit"] += 1 elif warns: results["warn"] += 1 else: results["ok"] += 1 if issues: all_issues.append((asn, n.get("name","?"), issues, p.get("total"), r.get("coverage_percent"), ix.get("total_connections"), fac.get("total"), n.get("rir"), n.get("country"))) print(f"\r Done. {len(responded)} responded, {results['skip']} skipped. ") print() print("=" * 75) print(f"FINAL AUDIT SUMMARY — {len(responded)} ASNs responded ({results['skip']} timed out)") print("=" * 75) print(f" ✓ PERFECT (zero issues): {results['ok']:3d}") print(f" ⚠ WARNING only: {results['warn']:3d}") print(f" ✗ CRITICAL: {results['crit']:3d}") print(f" ⏭ Skipped (no response): {results['skip']:3d}") if results["crit"] == 0: print("\n ✅ NO CRITICAL DATA ERRORS IN ANY RESPONDED ASN") # Per-category counts warn_counts = {} crit_counts = {} for _, _, issues, *_ in all_issues: for sev, field, _ in issues: if sev == "CRITICAL": crit_counts[field] = crit_counts.get(field, 0) + 1 else: warn_counts[field] = warn_counts.get(field, 0) + 1 if crit_counts: print("\n CRITICAL by field:") for f, c in sorted(crit_counts.items(), key=lambda x: -x[1]): print(f" {f:35s}: {c} ASNs") if warn_counts: print("\n WARNING by field:") for f, c in sorted(warn_counts.items(), key=lambda x: -x[1]): print(f" {f:35s}: {c} ASNs") # Detail for any issues if all_issues: print("\n DETAILS:") for asn, name, issues, pfx, rpki_pct, ix_cnt, fac_cnt, rir, cc in all_issues: for sev, field, msg in issues: print(f" [{sev[:4]}] AS{asn:>7} | {field:30s} | {msg[:60]}") print() print(" SAMPLE DATA (responded ASNs):") print(f" {'ASN':>8} {'Name':24s} {'RIR':>6} {'CC':>3} {'Pfx':>5} {'RPKI%':>6} {'IX':>4} {'Fac':>4}") print(f" {'-'*70}") for asn in responded[:25]: d = fetch(f"http://localhost:3101/api/lookup?asn={asn}", timeout=5) if not d: continue n=d.get("network",{}); p=d.get("prefixes",{}); r=d.get("rpki",{}); ix=d.get("ix_presence",{}); fac=d.get("facilities",{}) print(f" AS{asn:>6} {n.get('name','?')[:22]:24s} {n.get('rir','?'):>6} {n.get('country','?'):>3} {p.get('total',0):>5} {r.get('coverage_percent',0):>5}% {ix.get('total_connections',0):>4} {fac.get('total',0):>4}") print() print(f"Audit complete: {datetime.now(timezone.utc).isoformat()}")