fix: correct CampaignsView, analysis.py IPv4 split, entities date filter
- 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>
This commit is contained in:
@ -1,6 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { QuickSearch } from './QuickSearch';
|
||||
|
||||
interface IncidentCluster {
|
||||
id: string;
|
||||
@ -124,10 +123,7 @@ export function IncidentsView() {
|
||||
<p className="text-text-secondary text-sm mt-1">
|
||||
Surveillance en temps réel - 24 dernières heures
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-full md:w-auto">
|
||||
<QuickSearch />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Critical Metrics */}
|
||||
@ -212,8 +208,10 @@ export function IncidentsView() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Priority Incidents */}
|
||||
<div>
|
||||
{/* Main content: incidents list (2/3) + top threats table (1/3) */}
|
||||
<div className="grid grid-cols-3 gap-6 items-start">
|
||||
{/* Incidents list — 2/3 */}
|
||||
<div className="col-span-2">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-xl font-semibold text-text-primary">
|
||||
Incidents Prioritaires
|
||||
@ -367,41 +365,35 @@ export function IncidentsView() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>{/* end col-span-2 */}
|
||||
|
||||
{/* Top Active Threats */}
|
||||
<div className="bg-background-secondary rounded-lg p-6">
|
||||
<h2 className="text-xl font-semibold text-text-primary mb-4">
|
||||
Top Menaces Actives
|
||||
</h2>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead className="bg-background-card">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase">#</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase">Entité</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase">Type</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase">Score</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase">Pays</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase">ASN</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase">Hits/s</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase">Tendance</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-background-card">
|
||||
{clusters.slice(0, 10).map((cluster, index) => (
|
||||
<tr
|
||||
key={cluster.id}
|
||||
className="hover:bg-background-card/50 transition-colors cursor-pointer"
|
||||
onClick={() => navigate(`/investigation/${cluster.subnet?.split('/')[0] || ''}`)}
|
||||
{/* Top threats sidebar — 1/3 */}
|
||||
<div className="sticky top-4">
|
||||
<div className="bg-background-secondary rounded-lg overflow-hidden">
|
||||
<div className="p-4 border-b border-background-card">
|
||||
<h3 className="text-base font-semibold text-text-primary">🔥 Top Menaces</h3>
|
||||
</div>
|
||||
<div className="divide-y divide-background-card">
|
||||
{clusters.slice(0, 12).map((cluster, index) => (
|
||||
<div
|
||||
key={cluster.id}
|
||||
className="px-4 py-3 flex items-center gap-3 hover:bg-background-card/50 transition-colors cursor-pointer"
|
||||
onClick={() => navigate(`/investigation/${cluster.sample_ip || cluster.subnet?.split('/')[0] || ''}`)}
|
||||
>
|
||||
<td className="px-4 py-3 text-text-secondary">{index + 1}</td>
|
||||
<td className="px-4 py-3 font-mono text-sm text-text-primary">
|
||||
{cluster.subnet?.split('/')[0] || 'Unknown'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-text-secondary">IP</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className={`px-2 py-1 rounded text-xs font-bold ${
|
||||
<span className="text-text-disabled text-xs w-4">{index + 1}</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="font-mono text-xs text-text-primary truncate">
|
||||
{cluster.sample_ip || cluster.subnet?.split('/')[0] || 'Unknown'}
|
||||
</div>
|
||||
<div className="text-xs text-text-secondary flex gap-2 mt-0.5">
|
||||
{cluster.countries[0] && (
|
||||
<span>{getCountryFlag(cluster.countries[0].code)} {cluster.countries[0].code}</span>
|
||||
)}
|
||||
<span>AS{cluster.asn || '?'}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-end gap-1">
|
||||
<span className={`px-1.5 py-0.5 rounded text-xs font-bold ${
|
||||
cluster.score > 80 ? 'bg-red-500 text-white' :
|
||||
cluster.score > 60 ? 'bg-orange-500 text-white' :
|
||||
cluster.score > 40 ? 'bg-yellow-500 text-white' :
|
||||
@ -409,33 +401,25 @@ export function IncidentsView() {
|
||||
}`}>
|
||||
{cluster.score}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-text-primary">
|
||||
{cluster.countries[0] && (
|
||||
<>
|
||||
{getCountryFlag(cluster.countries[0].code)} {cluster.countries[0].code}
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-text-primary">
|
||||
AS{cluster.asn || '?'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-text-primary font-bold">
|
||||
{Math.round(cluster.total_detections / 24) || 0}
|
||||
</td>
|
||||
<td className={`px-4 py-3 font-bold ${
|
||||
cluster.trend === 'up' ? 'text-red-500' :
|
||||
cluster.trend === 'down' ? 'text-green-500' :
|
||||
'text-gray-400'
|
||||
}`}>
|
||||
{cluster.trend === 'up' ? '↑' : cluster.trend === 'down' ? '↓' : '→'} {cluster.trend_percentage}%
|
||||
</td>
|
||||
</tr>
|
||||
<span className={`text-xs font-bold ${
|
||||
cluster.trend === 'up' ? 'text-red-500' :
|
||||
cluster.trend === 'down' ? 'text-green-500' :
|
||||
'text-gray-400'
|
||||
}`}>
|
||||
{cluster.trend === 'up' ? '↑' : cluster.trend === 'down' ? '↓' : '→'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{clusters.length === 0 && (
|
||||
<div className="px-4 py-8 text-center text-text-secondary text-sm">
|
||||
Aucune menace active
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>{/* end grid */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user