- analysis.py: suppression du LIMIT 10 dans la requête user-agents
→ limitée à 500 (cohérent avec la page /detections/ip/<ip> qui montre 278)
→ total_count calculé via requête séparée pour des pourcentages corrects
- CountryAnalysis.tsx: ajout d'un InfoTip ⓘ sur 'PAYS DE L'IP' expliquant
que la source est le GeoIP du pipeline d'ingestion (ClickHouse) et peut
différer des APIs de réputation externes pour les IPs anycast/CDN/cloud
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- models.py: ajout anubis_bot_name, anubis_bot_action, anubis_bot_category
dans le modèle Pydantic Detection
- detections.py: fix ILLEGAL_AGGREGATION ClickHouse (argMin alias renommés
en *_best), ajout des 3 champs Anubis dans les requêtes individuelle et
groupée
- ml_features.py: fix alias sum(hits) AS total_hits (évite nested aggregate),
ajout missing_accept_enc_ratio et http_scheme_ratio dans b-features
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Suppression de 'Score Anomalie' (avg_score) des 31→30 features de clustering
- Suppression de 'Score de détection robot' (mean_score) de la sidebar et de l'API
- Suppression de bot_ips / high_risk_ips des stats (métriques dérivées des scores supprimés)
- Redistribution des poids dans risk_score_from_centroid: UA-CH mismatch +17%,
fuzzing +14%, headless +10%, vélocité +9%, ip_id_zero +7%
- Mise à jour des indices feature dans name_cluster et risk_score_from_centroid
- Palette 24 couleurs spectrales (cluster_color) → bleu/violet/rose/teal/amber/cyan/lime...
Les couleurs identifient les clusters, non leur niveau de risque
- Remplacement de la légende CRITICAL/HIGH/MEDIUM/LOW par la liste des clusters actifs
- Ajout de spread_clusters(): répulsion itérative des centroïdes trop proches (50 iter)
min_dist=0.16 → les clusters se repoussent mutuellement → visualisation plus lisible
- Interface TypeScript mise à jour (suppression mean_score, bot_ips, high_risk_ips)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Avant ce fix, force=true remettait status='idle' mais gardait le result
précédent. L'endpoint retournait alors l'ancien résultat (status=ready)
au lieu du status computing attendu.
Solution: vider aussi result et cluster_ips lors d'un force reset.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- 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>
🐛 PROBLÈME:
• ClickHouse stocke IPv4 en IPv6 (::ffff:x.x.x.x)
• Docker utilisait un cache et n'appliquait pas les modifs
• subnet et sample_ip avaient toujours ::ffff:
✅ SOLUTION:
• CTE cleaned_ips avec replaceRegexpAll pour enlever ::ffff:
• argMax(clean_ip, detected_at) pour sample_ip
• Rebuild complet avec --no-cache
RÉSULTAT:
• Avant: subnet='::ffff:176.65.132.0/24', sample_ip=null ❌
• Après: subnet='176.65.132.0/24', sample_ip='176.65.132.19' ✅📝 COMMANDE DE REBUILD:
docker compose down && docker build --no-cache -t dashboard-dashboard_web . && docker compose up -d
✅ TESTÉ ET VALIDÉ
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>