diff --git a/backend/routes/incidents.py b/backend/routes/incidents.py index d9b6dc8..a2203aa 100644 --- a/backend/routes/incidents.py +++ b/backend/routes/incidents.py @@ -10,14 +10,6 @@ from ..models import BaseModel router = APIRouter(prefix="/api/incidents", tags=["incidents"]) -# Nettoyer une adresse IP (enlever ::ffff: prefix) -def cleanIP(address: str) -> str: - if not address: - return '' - import re - return re.sub(r'^::ffff:', '', address, flags=re.IGNORECASE) - - @router.get("/clusters") async def get_incident_clusters( hours: int = Query(24, ge=1, le=168, description="Fenêtre temporelle en heures"), @@ -34,13 +26,15 @@ async def get_incident_clusters( """ try: # Cluster par subnet /24 avec une IP exemple + # Note: src_ip est en IPv6, les IPv4 sont stockés comme ::ffff:x.x.x.x + # toIPv4() convertit les IPv4-mapped, IPv4NumToString() retourne l'IPv4 en notation x.x.x.x cluster_query = """ WITH subnet_groups AS ( SELECT concat( - splitByChar('.', toString(src_ip))[1], '.', - splitByChar('.', toString(src_ip))[2], '.', - splitByChar('.', toString(src_ip))[3], '.0/24' + splitByChar('.', IPv4NumToString(toIPv4(src_ip)))[1], '.', + splitByChar('.', IPv4NumToString(toIPv4(src_ip)))[2], '.', + splitByChar('.', IPv4NumToString(toIPv4(src_ip)))[3], '.0/24' ) AS subnet, count() AS total_detections, uniq(src_ip) AS unique_ips, @@ -51,9 +45,10 @@ async def get_incident_clusters( argMax(asn_number, detected_at) AS asn_number, argMax(threat_level, detected_at) AS threat_level, avg(anomaly_score) AS avg_score, - any(src_ip) AS sample_ip + any(IPv4NumToString(toIPv4(src_ip))) AS sample_ip FROM ml_detected_anomalies WHERE detected_at >= now() - INTERVAL %(hours)s HOUR + AND isIPv4MappedIPv6(src_ip) -- Filtre uniquement les IPv4 GROUP BY subnet HAVING total_detections >= 2 ) @@ -115,7 +110,7 @@ async def get_incident_clusters( "total_detections": row[1], "unique_ips": row[2], "subnet": row[0], - "sample_ip": cleanIP(row[10]) if row[10] else None, + "sample_ip": row[10] if row[10] else row[0].split('/')[0], "ja4": row[5] or "", "primary_ua": "python-requests", "primary_target": "Unknown", diff --git a/frontend/src/components/IncidentsView.tsx b/frontend/src/components/IncidentsView.tsx index 8efe815..fea9324 100644 --- a/frontend/src/components/IncidentsView.tsx +++ b/frontend/src/components/IncidentsView.tsx @@ -107,24 +107,6 @@ export function IncidentsView() { return code.toUpperCase().replace(/./g, char => String.fromCodePoint(char.charCodeAt(0) + 127397)); }; - // Nettoyer une adresse IP (enlever ::ffff: prefix) - const cleanIP = (address: string): string => { - if (!address) return ''; - return address.replace(/^::ffff:/i, ''); - }; - - // Générer une IP exemple depuis un subnet - const getSampleIP = (subnet: string): string => { - const clean = cleanIP(subnet); - const ipParts = clean.replace('/24', '').split('.'); - if (ipParts.length === 4) { - // Remplacer le dernier octet par 1 - ipParts[3] = '1'; - return ipParts.join('.'); - } - return cleanIP(subnet.split('/')[0]); - }; - if (loading) { return (
@@ -268,7 +250,7 @@ export function IncidentsView() { {cluster.id} | - {cleanIP(cluster.subnet || '')} + {cluster.subnet || ''}
@@ -322,13 +304,13 @@ export function IncidentsView() {