Initial commit: Bot Detector Dashboard for SOC Incident Response

🛡️ Dashboard complet pour l'analyse et la classification des menaces

Fonctionnalités principales:
- Visualisation des détections en temps réel (24h)
- Investigation multi-entités (IP, JA4, ASN, Host, User-Agent)
- Analyse de corrélation pour classification SOC
- Clustering automatique par subnet/JA4/UA
- Export des classifications pour ML

Composants:
- Backend: FastAPI (Python) + ClickHouse
- Frontend: React + TypeScript + TailwindCSS
- 6 routes API: metrics, detections, variability, attributes, analysis, entities
- 7 types d'entités investigables

Documentation ajoutée:
- NAVIGATION_GRAPH.md: Graph complet de navigation
- SOC_OPTIMIZATION_PROPOSAL.md: Proposition d'optimisation pour SOC
  • Réduction de 7 à 2 clics pour classification
  • Nouvelle vue /incidents clusterisée
  • Panel latéral d'investigation
  • Quick Search (Cmd+K)
  • Timeline interactive
  • Graph de corrélations

Sécurité:
- .gitignore configuré (exclut .env, secrets, node_modules)
- Credentials dans .env (à ne pas committer)

⚠️ Audit sécurité réalisé - Voir recommandations dans SOC_OPTIMIZATION_PROPOSAL.md

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
SOC Analyst
2026-03-14 21:33:55 +01:00
commit a61828d1e7
55 changed files with 11189 additions and 0 deletions

View File

@ -0,0 +1,142 @@
import { useEffect, useState } from 'react';
interface JA4SubnetData {
subnet: string;
count: number;
}
interface JA4Analysis {
ja4: string;
shared_ips_count: number;
top_subnets: JA4SubnetData[];
other_ja4_for_ip: string[];
}
interface JA4AnalysisProps {
ip: string;
}
export function JA4Analysis({ ip }: JA4AnalysisProps) {
const [data, setData] = useState<JA4Analysis | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchJA4Analysis = async () => {
setLoading(true);
try {
const response = await fetch(`/api/analysis/${encodeURIComponent(ip)}/ja4`);
if (!response.ok) throw new Error('Erreur chargement JA4');
const result = await response.json();
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : 'Erreur inconnue');
} finally {
setLoading(false);
}
};
fetchJA4Analysis();
}, [ip]);
if (loading) {
return (
<div className="bg-background-secondary rounded-lg p-6">
<div className="text-center text-text-secondary">Chargement...</div>
</div>
);
}
if (error || !data || !data.ja4) {
return (
<div className="bg-background-secondary rounded-lg p-6">
<div className="text-center text-text-secondary">JA4 non disponible</div>
</div>
);
}
return (
<div className="bg-background-secondary rounded-lg p-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-medium text-text-primary">3. JA4 FINGERPRINT ANALYSIS</h3>
{data.shared_ips_count > 50 && (
<span className="bg-threat-high text-white px-3 py-1 rounded text-xs font-medium">
🔴 {data.shared_ips_count} IPs
</span>
)}
</div>
<div className="space-y-6">
{/* JA4 Fingerprint */}
<div>
<div className="text-sm text-text-secondary mb-2">JA4 Fingerprint</div>
<div className="bg-background-card rounded-lg p-3 font-mono text-sm text-text-primary break-all">
{data.ja4}
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* IPs avec même JA4 */}
<div>
<div className="text-sm text-text-secondary mb-2">
IPs avec le MÊME JA4 (24h)
</div>
<div className="text-3xl font-bold text-text-primary mb-2">
{data.shared_ips_count}
</div>
{data.shared_ips_count > 50 && (
<div className="text-threat-high text-sm">
🔴 PATTERN: Même outil/bot sur {data.shared_ips_count} IPs
</div>
)}
</div>
{/* Autres JA4 pour cette IP */}
<div>
<div className="text-sm text-text-secondary mb-2">
Autres JA4 pour cette IP
</div>
{data.other_ja4_for_ip.length > 0 ? (
<div className="space-y-1">
{data.other_ja4_for_ip.slice(0, 3).map((ja4, idx) => (
<div key={idx} className="bg-background-card rounded p-2 font-mono text-xs text-text-primary truncate">
{ja4}
</div>
))}
{data.other_ja4_for_ip.length > 3 && (
<div className="text-text-secondary text-xs">
+{data.other_ja4_for_ip.length - 3} autres
</div>
)}
</div>
) : (
<div className="text-text-secondary text-sm">
1 seul JA4 Comportement stable
</div>
)}
</div>
</div>
{/* Top subnets */}
{data.top_subnets.length > 0 && (
<div>
<div className="text-sm text-text-secondary mb-2">
Top subnets pour ce JA4
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2">
{data.top_subnets.map((subnet, idx) => (
<div
key={idx}
className="bg-background-card rounded-lg p-3 flex items-center justify-between"
>
<div className="font-mono text-sm text-text-primary">{subnet.subnet}</div>
<div className="text-text-primary font-bold">{subnet.count} IPs</div>
</div>
))}
</div>
</div>
)}
</div>
</div>
);
}