""" Endpoints pour l'analyse des botnets via la propagation des fingerprints JA4 """ from fastapi import APIRouter, HTTPException, Query from ..database import db router = APIRouter(prefix="/api/botnets", tags=["botnets"]) def _botnet_class(unique_countries: int) -> str: if unique_countries > 100: return "global_botnet" if unique_countries > 20: return "regional_botnet" return "concentrated" @router.get("/ja4-spread") async def get_ja4_spread(): """Propagation des JA4 fingerprints à travers les pays et les IPs.""" try: sql = """ SELECT ja4, unique_ips, unique_countries, targeted_hosts FROM mabase_prod.view_host_ja4_anomalies ORDER BY unique_countries DESC """ result = db.query(sql) items = [] for row in result.result_rows: ja4 = str(row[0]) unique_ips = int(row[1]) unique_countries = int(row[2]) targeted_hosts = int(row[3]) dist_score = round( unique_countries / max(unique_ips ** 0.5, 0.001), 2 ) items.append({ "ja4": ja4, "unique_ips": unique_ips, "unique_countries": unique_countries, "targeted_hosts": targeted_hosts, "distribution_score":dist_score, "botnet_class": _botnet_class(unique_countries), }) return {"items": items, "total": len(items)} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/ja4/{ja4}/countries") async def get_ja4_countries(ja4: str, limit: int = Query(30, ge=1, le=200)): """Top pays pour un JA4 donné depuis agg_host_ip_ja4_1h.""" try: sql = """ SELECT src_country_code AS country_code, uniq(replaceRegexpAll(toString(src_ip), '^::ffff:', '')) AS unique_ips, sum(hits) AS hits FROM mabase_prod.agg_host_ip_ja4_1h WHERE ja4 = %(ja4)s GROUP BY src_country_code ORDER BY unique_ips DESC LIMIT %(limit)s """ result = db.query(sql, {"ja4": ja4, "limit": limit}) items = [ { "country_code": str(row[0]), "unique_ips": int(row[1]), "hits": int(row[2]), } for row in result.result_rows ] return {"items": items, "total": len(items)} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/summary") async def get_botnets_summary(): """Statistiques globales sur les botnets détectés.""" try: sql = """ SELECT countIf(unique_countries > 100) AS total_global_botnets, sumIf(unique_ips, unique_countries > 50) AS total_ips_in_botnets, argMax(ja4, unique_countries) AS most_spread_ja4, argMax(ja4, unique_ips) AS most_ips_ja4 FROM mabase_prod.view_host_ja4_anomalies """ result = db.query(sql) row = result.result_rows[0] return { "total_global_botnets": int(row[0]), "total_ips_in_botnets": int(row[1]), "most_spread_ja4": str(row[2]), "most_ips_ja4": str(row[3]), } except Exception as e: raise HTTPException(status_code=500, detail=str(e))