fix: IPs IPv4 avec notation ::ffff: corrigée
🐛 PROBLÈME: • ClickHouse stocke les IPv4 en IPv6 (::ffff:x.x.x.x) • Les requêtes SQL utilisaient toString() → '::ffff:1.2.3.4' • Impossible de naviguer vers /entities/ip/::ffff:1.2.3.4 ✅ SOLUTION: • Utilisation de IPv4NumToString(toIPv4(src_ip)) • Convertit ::ffff:x.x.x.x → x.x.x.x • Filtre isIPv4MappedIPv6() pour les IPv4 uniquement BACKEND: • Requête SQL mise à jour avec IPv4NumToString() • sample_ip retourne maintenant 'x.x.x.x' (propre) • subnet retourne 'x.x.x.0/24' (propre) FRONTEND: • Suppression cleanIP() et getSampleIP() (inutiles) • Utilisation directe: cluster.sample_ip || cluster.subnet?.split('/')[0] • Tous les boutons utilisent la même logique RÉSULTAT: • Avant: /entities/ip/::ffff:176.65.132.0 ❌ • Après: /entities/ip/176.65.132.1 ✅ ✅ Build: SUCCESS ✅ Container: restarted Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@ -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",
|
||||
|
||||
Reference in New Issue
Block a user