""" Endpoints pour la détection du TCP spoofing (TTL / window size anormaux) """ from fastapi import APIRouter, HTTPException, Query from ..database import db router = APIRouter(prefix="/api/tcp-spoofing", tags=["tcp_spoofing"]) def _suspected_os(ttl: int) -> str: if 55 <= ttl <= 65: return "Linux/Mac" if 120 <= ttl <= 135: return "Windows" if ttl < 50: return "Behind proxy (depleted)" return "Unknown" def _declared_os(ua: str) -> str: ua = ua or "" if "Windows" in ua: return "Windows" if "Mac OS X" in ua: return "macOS" if "Linux" in ua or "Android" in ua: return "Linux/Android" return "Unknown" @router.get("/overview") async def get_tcp_spoofing_overview(): """Statistiques globales sur les détections de spoofing TCP.""" try: sql = """ SELECT count() AS total_detections, uniq(src_ip) AS unique_ips, countIf(tcp_ttl < 60) AS low_ttl_count, countIf(tcp_ttl = 0) AS zero_ttl_count FROM mabase_prod.view_tcp_spoofing_detected """ result = db.query(sql) row = result.result_rows[0] total_detections = int(row[0]) unique_ips = int(row[1]) low_ttl_count = int(row[2]) zero_ttl_count = int(row[3]) ttl_sql = """ SELECT tcp_ttl, count() AS cnt, uniq(src_ip) AS ips FROM mabase_prod.view_tcp_spoofing_detected GROUP BY tcp_ttl ORDER BY cnt DESC LIMIT 15 """ ttl_res = db.query(ttl_sql) ttl_distribution = [ {"ttl": int(r[0]), "count": int(r[1]), "ips": int(r[2])} for r in ttl_res.result_rows ] win_sql = """ SELECT tcp_window_size, count() AS cnt FROM mabase_prod.view_tcp_spoofing_detected GROUP BY tcp_window_size ORDER BY cnt DESC LIMIT 10 """ win_res = db.query(win_sql) window_size_distribution = [ {"window_size": int(r[0]), "count": int(r[1])} for r in win_res.result_rows ] return { "total_detections": total_detections, "unique_ips": unique_ips, "low_ttl_count": low_ttl_count, "zero_ttl_count": zero_ttl_count, "ttl_distribution": ttl_distribution, "window_size_distribution":window_size_distribution, } except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/list") async def get_tcp_spoofing_list( limit: int = Query(100, ge=1, le=1000), offset: int = Query(0, ge=0), ): """Liste paginée des détections, triée par tcp_ttl ASC.""" try: count_sql = "SELECT count() FROM mabase_prod.view_tcp_spoofing_detected" total = int(db.query(count_sql).result_rows[0][0]) sql = """ SELECT replaceRegexpAll(toString(src_ip), '^::ffff:', '') AS src_ip, ja4, tcp_ttl, tcp_window_size, first_ua FROM mabase_prod.view_tcp_spoofing_detected ORDER BY tcp_ttl ASC LIMIT %(limit)s OFFSET %(offset)s """ result = db.query(sql, {"limit": limit, "offset": offset}) items = [] for row in result.result_rows: ip = str(row[0]) ja4 = str(row[1]) ttl = int(row[2]) window_size = int(row[3]) ua = str(row[4] or "") sus_os = _suspected_os(ttl) dec_os = _declared_os(ua) spoof_flag = sus_os != dec_os and sus_os != "Unknown" and dec_os != "Unknown" items.append({ "ip": ip, "ja4": ja4, "tcp_ttl": ttl, "tcp_window_size": window_size, "first_ua": ua, "suspected_os": sus_os, "declared_os": dec_os, "spoof_flag": spoof_flag, }) return {"items": items, "total": total} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/matrix") async def get_tcp_spoofing_matrix(): """Cross-tab suspected_os × declared_os avec comptage.""" try: sql = """ SELECT src_ip, tcp_ttl, first_ua FROM mabase_prod.view_tcp_spoofing_detected """ result = db.query(sql) counts: dict = {} for row in result.result_rows: ttl = int(row[1]) ua = str(row[2] or "") sus_os = _suspected_os(ttl) dec_os = _declared_os(ua) key = (sus_os, dec_os) counts[key] = counts.get(key, 0) + 1 matrix = [ {"suspected_os": k[0], "declared_os": k[1], "count": v} for k, v in counts.items() ] matrix.sort(key=lambda x: x["count"], reverse=True) return {"matrix": matrix} except Exception as e: raise HTTPException(status_code=500, detail=str(e))