- investigation_summary.py: nouveau endpoint GET /api/investigation/{ip}/summary
agrège 6 sources (ML, bruteforce, TCP spoofing, JA4 rotation, persistance, timeline 24h)
en un score de risque 0-100 avec signaux détaillés
- InvestigationView.tsx: widget IPActivitySummary avec jauge Risk Score SVG,
badges multi-sources et mini-timeline 24h barres
- metrics.py: endpoint GET /api/metrics/baseline — comparaison 24h vs hier
(total détections, IPs uniques, alertes CRITICAL) avec % de variation
- IncidentsView.tsx: widget baseline avec ▲▼ sur le dashboard principal
- rotation.py: endpoints /sophistication et /proactive-hunt
Score sophistication = JOIN 3 tables (rotation×10 + récurrence×20 + log(bf+1)×5)
Chasse proactive = IPs récurrentes sous le seuil ML (abs(score) < 0.5)
- RotationView.tsx: onglets 🏆 Sophistication et 🕵️ Chasse proactive
avec tier APT-like/Advanced/Automated/Basic et boutons investigation
- detections.py: LEFT JOIN asn_reputation, badge coloré rouge/orange/vert
selon label (bot/scanner → score 0.05, human → 0.9)
- models.py: ajout champs asn_score et asn_rep_label dans Detection
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Problème: TTL=0 (proxy/CDN) ne permet pas de fingerprinter l'OS d'origine.
Les entrées sans données TCP étaient faussement flagguées comme spoofs.
Corrections backend (tcp_spoofing.py):
- Règle: spoof_flag=True UNIQUEMENT si TTL dans plage OS connue (52-65 Linux, 110-135 Windows)
ET OS déclaré incompatible avec l'OS fingerprinté
- TTL=0 → 'Unknown' (pas de corrélation possible)
- TTL hors plage connue → 'Unknown' (pas de corrélation possible)
- /list: filtre WHERE tcp_ttl > 0 (exclut les entrées sans données TCP)
- /list: paramètre spoof_only=true → filtre SQL sur plages TTL corrélables uniquement
- /overview: nouvelles métriques (with_tcp_data, no_tcp_data, linux_fingerprint, windows_fingerprint)
- /matrix: ajout is_spoof par cellule
Corrections frontend (TcpSpoofingView.tsx):
- Stat cards: total entries, avec TCP, fingerprint Linux/Windows (plus TTL<60)
- Bandeau informatif: nombre d'entrées sans données TCP exclues
- Checkbox 'Spoofs uniquement' → re-fetch avec spoof_only=true (filtre SQL)
- Matrice OS: cellules de vrai spoof surlignées en rouge avec icône 🚨
- useEffect séparé pour overview et items (items se recharge si spoofOnly change)
Résultat: 36 699 entrées Linux/Mac (TTL 52-65), dont 16 226 spoofant Windows UA
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- CampaignsView: update ClusterData interface to match real API response
(severity/unique_ips/score instead of threat_level/total_ips/confidence_range)
Fix fetch to use data.items, rewrite ClusterCard and BehavioralTab
Remove unused getClassificationColor and THREAT_ORDER constants
- analysis.py: fix IPv4Address object has no attribute 'split' on line 322
Add str() conversion before calling .split('.')
- entities.py: fix Date vs DateTime comparison — log_date is a Date column,
comparing against now()-INTERVAL HOUR caused yesterday's entries to be excluded
Use toDate(now() - INTERVAL X HOUR) for correct Date-level comparison
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Utilisation de 2 requêtes séparées + fusion en Python
- 1ère requête: ml_detected_anomalies pour les détections récentes
- 2ème requête: view_dashboard_entities avec IN clause pour les user-agents
- La clause IN permet d'utiliser l'index ClickHouse (splitByChar ne l'utilise pas)
- PREWHERE optimise les performances de requête
Problème résolu:
- unique_ua était toujours à 0 car la jointure LEFT JOIN ne fonctionnait pas
- La solution avec IN clause fonctionne car elle utilise l'index sur entity_value
Testé avec 141.98.11.0/24:
- 5 IPs, 8 détections, 65 user-agents uniques
- 141.98.11.209: 68 user-agents différents
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Nouveau service backend/services/reputation_ip.py
- IP-API.com: Géolocalisation + détection Proxy/Hosting
- IPinfo.io: ASN + Organisation
- Agrégation des sources avec score de menace 0-100
- Niveaux: clean/low/medium/high/critical
- Nouvelle route API GET /api/reputation/ip/:ip
- Validation IPv4
- Version complète et summary
- Timeout 10s par source
- Nouveau composant frontend ReputationPanel.tsx
- Badge de niveau de menace (code couleur)
- 4 badges détection: Proxy 🌐, Hosting ☁️, VPN 🔒, Tor 🧅
- Infos géographiques: pays, ville, ASN, organisation
- Liste des avertissements
- Sources et timestamp
- Intégration dans InvestigationView
- Panel affiché en premier (avant Graph de corrélations)
- Chargement asynchrone au montage du composant
- Dépendance: httpx==0.26.0 (requêtes HTTP async)
Testé avec 141.98.11.209 (Lithuania, AS209605) → 🟢 CLEAN (0/100)
Aucun proxy/hosting/VPN/Tor détecté
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Nouveau endpoint API GET /api/entities/subnet/:subnet
- Utilise view_dashboard_entities (données agrégées)
- Retourne stats globales + liste détaillée des IPs
- Filtre par les 3 premiers octets du subnet
- Nouveau composant frontend SubnetInvestigation.tsx
- Affiche toutes les IPs d'un subnet /24
- Tableau avec: IP, détections, JA4, UA, pays, ASN, menace, score
- Boutons 'Investiguer' et 'Détails' par IP
- URL simplifiée: /entities/subnet/x.x.x.x_24 (_ au lieu de /)
- Évite les problèmes d'encodage URL
- Conversion automatique _ → / côté frontend
- Correction route ordering dans App.tsx
- /entities/subnet/:subnet avant /entities/:type/:value
- Routes backend réordonnées
- /api/entities/subnet/:subnet avant les routes génériques
Testé avec 141.98.11.0/24 → 6 IPs trouvées, 1677 détections
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
🐛 CORRECTION:
• Problème: Les IPs n'étaient pas trouvées
• Cause: Utilisation du subnet (176.65.132.0) au lieu d'une IP réelle
• Solution: Ajout sample_ip + fallback getSampleIP()
BACKEND:
• API /api/incidents/clusters retourne sample_ip
• Utilisation de any(src_ip) dans la requête SQL
• Fallback sur None si pas d'IP trouvée
FRONTEND:
• Interface IncidentCluster: sample_ip optionnel
• Fonction getSampleIP() génère une IP depuis le subnet
• Fallback: sample_ip || getSampleIP(subnet)
• Tous les boutons utilisent la même logique
RÉSULTAT:
• Avant: /entities/ip/176.65.132.0 (n'existe pas)
• Après: /entities/ip/176.65.132.1 (IP valide)
✅ Build: SUCCESS
✅ Container: restarted
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>