feat: 6 améliorations SOC — synthèse IP, baseline, sophistication, chasse proactive, badge ASN, 2 nouveaux onglets rotation

- 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>
This commit is contained in:
SOC Analyst
2026-03-16 00:43:27 +01:00
parent 8032ebaab8
commit d4c3512572
11 changed files with 815 additions and 6 deletions

View File

@ -451,6 +451,7 @@ export function DetectionsList() {
{detection.asn_number && (
<div className="text-xs text-text-secondary">AS{detection.asn_number}</div>
)}
<AsnRepBadge score={detection.asn_score} label={detection.asn_rep_label} />
</td>
);
}
@ -570,3 +571,27 @@ function getFlag(countryCode: string): string {
const code = countryCode.toUpperCase();
return code.replace(/./g, char => String.fromCodePoint(char.charCodeAt(0) + 127397));
}
// Badge de réputation ASN
function AsnRepBadge({ score, label }: { score?: number | null; label?: string }) {
if (score == null) return null;
let bg: string;
let text: string;
let display: string;
if (score < 0.3) {
bg = 'bg-threat-critical/20';
text = 'text-threat-critical';
} else if (score < 0.6) {
bg = 'bg-threat-medium/20';
text = 'text-threat-medium';
} else {
bg = 'bg-threat-low/20';
text = 'text-threat-low';
}
display = label || (score < 0.3 ? 'malicious' : score < 0.6 ? 'suspect' : 'ok');
return (
<span className={`mt-1 inline-block text-xs px-1.5 py-0.5 rounded ${bg} ${text}`}>
{display}
</span>
);
}