fix: correct AlertSource/AlertSeverity casing to match serde snake_case

Root cause: AlertSource and AlertSeverity use #[serde(rename_all = "snake_case")],
so the JSON values are "ml"/"rule"/"fusion" and "high"/"critical" — not PascalCase.
SOURCE_CONFIG and SEVERITY_CONFIG keys were PascalCase, causing undefined lookups.

Changes:
- Type aliases: 'Ml'|'Rule'|'Fusion' -> 'ml'|'rule'|'fusion'
                'High'|'Critical'    -> 'high'|'critical'
- SOURCE_CONFIG keys: Ml/Rule/Fusion -> ml/rule/fusion
- SEVERITY_CONFIG keys: Critical/High -> critical/high
- All source/severity comparisons updated to lowercase
- Stats filter predicates updated to lowercase
- Filter button arrays updated to lowercase

https://claude.ai/code/session_01Wen51749mAnwBEfh7XmvUw
This commit is contained in:
Claude 2026-05-19 08:52:36 +00:00
parent a163d88f15
commit 7fe77d235b
No known key found for this signature in database

View File

@ -26,8 +26,8 @@ import LoadingSpinner from '../components/LoadingSpinner'
// --- Types (mirrors backend UnifiedAlert) ---
type AlertSource = 'Ml' | 'Rule' | 'Fusion'
type AlertSeverity = 'High' | 'Critical'
type AlertSource = 'ml' | 'rule' | 'fusion'
type AlertSeverity = 'high' | 'critical'
interface UnifiedAlert {
timestamp: number
@ -50,19 +50,19 @@ interface UnifiedAlert {
// --- Display config ---
const SOURCE_CONFIG = {
Ml: {
label: 'AI',
ml: {
label: 'ML',
color: '#7c3aed',
bgColor: 'rgba(124,58,237,0.10)',
icon: faBrain,
},
Rule: {
rule: {
label: 'Rule',
color: '#2563eb',
bgColor: 'rgba(37,99,235,0.10)',
icon: faScroll,
},
Fusion: {
fusion: {
label: 'Fusion',
color: '#dc2626',
bgColor: 'rgba(220,38,38,0.10)',
@ -71,13 +71,13 @@ const SOURCE_CONFIG = {
} as const
const SEVERITY_CONFIG = {
Critical: {
critical: {
label: 'Critical',
color: '#dc2626',
bgColor: 'rgba(220,38,38,0.10)',
icon: faExclamationTriangle,
},
High: {
high: {
label: 'High',
color: '#d97706',
bgColor: 'rgba(217,119,6,0.10)',
@ -383,7 +383,7 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
{(alert.attack_type || alert.rule_msg) && (
<div className="mb-6">
<span className="text-sm font-medium text-gray-600">
{alert.source === 'Ml' ? 'Attack Type' : 'Rule Message'}:
{alert.source === 'ml' ? 'Attack Type' : 'Rule Message'}:
</span>
<div className="mt-1 p-3 bg-gray-50 rounded-lg">
<p className="text-sm text-gray-900">{alert.attack_type ?? alert.rule_msg}</p>
@ -392,7 +392,7 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
)}
{/* ML inference metrics */}
{(alert.source === 'Ml' || alert.source === 'Fusion') && (
{(alert.source === 'ml' || alert.source === 'fusion') && (
<div className="mb-6 space-y-3">
<h4 className="text-sm font-semibold text-gray-700">ML Inference Metrics</h4>
<div className="flex justify-between items-center">
@ -420,7 +420,7 @@ const AlertDetails: React.FC<AlertDetailsProps> = ({ alert, onClose, showNotific
)}
{/* Rule SID */}
{(alert.source === 'Rule' || alert.source === 'Fusion') && alert.rule_sid !== null && (
{(alert.source === 'rule' || alert.source === 'fusion') && alert.rule_sid !== null && (
<div className="mb-6">
<h4 className="text-sm font-semibold text-gray-700 mb-2">Rule Info</h4>
<div className="flex justify-between items-center">
@ -530,11 +530,11 @@ const Detection: React.FC = () => {
const stats = useMemo(
() => ({
total: alerts.length,
critical: alerts.filter(a => a.severity === 'Critical').length,
high: alerts.filter(a => a.severity === 'High').length,
ml: alerts.filter(a => a.source === 'Ml').length,
rule: alerts.filter(a => a.source === 'Rule').length,
fusion: alerts.filter(a => a.source === 'Fusion').length,
critical: alerts.filter(a => a.severity === 'critical').length,
high: alerts.filter(a => a.severity === 'high').length,
ml: alerts.filter(a => a.source === 'ml').length,
rule: alerts.filter(a => a.source === 'rule').length,
fusion: alerts.filter(a => a.source === 'fusion').length,
uniqueIPs: new Set([...alerts.map(a => a.src_ip), ...alerts.map(a => a.dst_ip)]).size,
}),
[alerts]
@ -652,15 +652,15 @@ const Detection: React.FC = () => {
{/* Source filter */}
<div className="flex items-center gap-1.5">
<span className="text-xs text-gray-500">Source:</span>
{(['all', 'Ml', 'Rule', 'Fusion'] as const).map(s => (
{(['all', 'ml', 'rule', 'fusion'] as const).map(s => (
<FilterButton
key={s}
isActive={sourceFilter === s}
onClick={() => setSourceFilter(s)}
activeColor={
s === 'all' ? '#374151'
: s === 'Ml' ? '#7c3aed'
: s === 'Rule' ? '#2563eb'
: s === 'ml' ? '#7c3aed'
: s === 'rule' ? '#2563eb'
: '#dc2626'
}
>
@ -674,13 +674,13 @@ const Detection: React.FC = () => {
{/* Severity filter */}
<div className="flex items-center gap-1.5">
<span className="text-xs text-gray-500">Severity:</span>
{(['all', 'Critical', 'High'] as const).map(s => (
{(['all', 'critical', 'high'] as const).map(s => (
<FilterButton
key={s}
isActive={severityFilter === s}
onClick={() => setSeverityFilter(s)}
activeColor={
s === 'all' ? '#374151' : s === 'Critical' ? '#dc2626' : '#d97706'
s === 'all' ? '#374151' : s === 'critical' ? '#dc2626' : '#d97706'
}
>
{s === 'all' ? 'All' : SEVERITY_CONFIG[s].label}