/** * ============================================================================= * GHAYMAH SIEM DASHBOARD — Frontend Application * ============================================================================= * Connects to the Python SIEM engine API, fetches alerts and malicious IPs, * and renders them in real-time on the dashboard. * ============================================================================= */ (function () { 'use strict'; // ── Configuration ── const CONFIG = { API_BASE: window.location.origin, REFRESH_INTERVAL: 5000, // ms MAX_VISIBLE_ALERTS: 200, }; // ── State ── let allAlerts = []; let allIPs = []; let stats = {}; let currentFilters = { severity: 'all', source: 'all' }; // ── DOM References ── const DOM = { // Stats countCritical: document.getElementById('count-critical'), countHigh: document.getElementById('count-high'), countMedium: document.getElementById('count-medium'), countLow: document.getElementById('count-low'), countTotal: document.getElementById('count-total'), countIPs: document.getElementById('count-ips'), // Lists alertsList: document.getElementById('alerts-list'), ipsList: document.getElementById('ips-list'), ipCountBadge: document.getElementById('ip-count-badge'), // Filters filterSeverity: document.getElementById('filter-severity'), filterSource: document.getElementById('filter-source'), // Modal modalOverlay: document.getElementById('modal-overlay'), modalTitle: document.getElementById('modal-title'), modalBody: document.getElementById('modal-body'), modalClose: document.getElementById('modal-close'), // Status lastUpdate: document.getElementById('last-update'), }; // ── API Fetchers ── async function fetchAlerts() { try { const res = await fetch(`${CONFIG.API_BASE}/api/alerts`); if (res.ok) { allAlerts = await res.json(); renderAlerts(); } } catch (err) { console.warn('[SIEM] Failed to fetch alerts:', err.message); } } async function fetchIPs() { try { const res = await fetch(`${CONFIG.API_BASE}/api/ips`); if (res.ok) { allIPs = await res.json(); renderIPs(); } } catch (err) { console.warn('[SIEM] Failed to fetch IPs:', err.message); } } async function fetchStats() { try { const res = await fetch(`${CONFIG.API_BASE}/api/stats`); if (res.ok) { stats = await res.json(); renderStats(); } } catch (err) { console.warn('[SIEM] Failed to fetch stats:', err.message); } } // ── Renderers ── function renderStats() { const sev = stats.by_severity || {}; animateCounter(DOM.countCritical, sev.CRITICAL || 0); animateCounter(DOM.countHigh, sev.HIGH || 0); animateCounter(DOM.countMedium, sev.MEDIUM || 0); animateCounter(DOM.countLow, (sev.LOW || 0) + (sev.INFO || 0)); animateCounter(DOM.countTotal, stats.total_alerts || 0); animateCounter(DOM.countIPs, (stats.malicious_ips || 0) + (stats.suspicious_ips || 0)); DOM.lastUpdate.textContent = `Updated: ${new Date().toLocaleTimeString()}`; } function animateCounter(el, target) { const current = parseInt(el.textContent) || 0; if (current === target) return; const diff = target - current; const steps = Math.min(Math.abs(diff), 20); const increment = diff / steps; let step = 0; const timer = setInterval(() => { step++; if (step >= steps) { el.textContent = target; clearInterval(timer); } else { el.textContent = Math.round(current + increment * step); } }, 30); } function renderAlerts() { const filtered = allAlerts.filter(alert => { if (currentFilters.severity !== 'all' && alert.severity !== currentFilters.severity) return false; if (currentFilters.source !== 'all' && alert.source !== currentFilters.source) return false; return true; }).slice(0, CONFIG.MAX_VISIBLE_ALERTS); if (filtered.length === 0) { DOM.alertsList.innerHTML = `

🛡️ No alerts matching current filters.

`; return; } DOM.alertsList.innerHTML = filtered.map(alert => `
${escapeHtml(alert.severity)} ${getSourceIcon(alert.source)} ${escapeHtml(alert.source)} ${escapeHtml(alert.rule_name)}
${escapeHtml(alert.description)}
🆔 ${escapeHtml(alert.rule_id)} 🌐 ${escapeHtml(alert.source_ip)} ${alert.port ? `🔌 Port ${alert.port}` : ''} 🕐 ${formatTimestamp(alert.timestamp)}
`).join(''); } function renderIPs() { DOM.ipCountBadge.textContent = allIPs.length; if (allIPs.length === 0) { DOM.ipsList.innerHTML = `

✅ No malicious IPs detected.

`; return; } DOM.ipsList.innerHTML = allIPs.map(ip => { const scoreClass = ip.score >= 70 ? 'score-critical' : ip.score >= 40 ? 'score-high' : 'score-medium'; return `
${ip.score}
${escapeHtml(ip.ip)}
Events: ${ip.total_events} Failed Auth: ${ip.failed_logins} Scans: ${ip.port_scans} Blocked: ${ip.blocked_requests}
`; }).join(''); } // ── Alert Detail Modal ── window.showAlertDetail = function (alertId) { const alert = allAlerts.find(a => a.id === alertId); if (!alert) return; DOM.modalTitle.textContent = `${alert.rule_name} — ${alert.id}`; DOM.modalBody.innerHTML = `
Alert ID ${escapeHtml(alert.id)}
Timestamp ${escapeHtml(alert.timestamp)}
Severity ${escapeHtml(alert.severity)}
Source ${getSourceIcon(alert.source)} ${escapeHtml(alert.source)}
Rule ${escapeHtml(alert.rule_id)} — ${escapeHtml(alert.rule_name)}
Source IP ${escapeHtml(alert.source_ip)}
${alert.destination_ip ? `
Destination IP ${escapeHtml(alert.destination_ip)}
` : ''} ${alert.port ? `
Port ${alert.port}
` : ''} ${alert.count > 1 ? `
Event Count ${alert.count}
` : ''} ${alert.mitre_tactic ? `
MITRE Tactic ${escapeHtml(alert.mitre_tactic)}
` : ''} ${alert.mitre_technique ? `
MITRE Technique ${escapeHtml(alert.mitre_technique)}
` : ''}
Description ${escapeHtml(alert.description)}
${alert.raw_log ? `
Raw Log ${escapeHtml(alert.raw_log)}
` : ''} `; DOM.modalOverlay.classList.add('active'); }; function closeModal() { DOM.modalOverlay.classList.remove('active'); } // ── Utilities ── function escapeHtml(str) { if (typeof str !== 'string') return String(str); const div = document.createElement('div'); div.textContent = str; return div.innerHTML; } function getSourceIcon(source) { const icons = { firewall: '🔥', webserver: '🌐', auth: '🔑' }; return icons[source] || '📋'; } function formatTimestamp(ts) { try { const d = new Date(ts); return d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit' }); } catch { return ts; } } // ── Event Listeners ── DOM.filterSeverity.addEventListener('change', (e) => { currentFilters.severity = e.target.value; renderAlerts(); }); DOM.filterSource.addEventListener('change', (e) => { currentFilters.source = e.target.value; renderAlerts(); }); DOM.modalClose.addEventListener('click', closeModal); DOM.modalOverlay.addEventListener('click', (e) => { if (e.target === DOM.modalOverlay) closeModal(); }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeModal(); }); // ── Initialize ── async function init() { console.log('[SIEM] Initializing Ghaymah SIEM Dashboard...'); await Promise.all([fetchAlerts(), fetchIPs(), fetchStats()]); // Auto-refresh setInterval(async () => { await Promise.all([fetchAlerts(), fetchIPs(), fetchStats()]); }, CONFIG.REFRESH_INTERVAL); console.log('[SIEM] Dashboard initialized. Refreshing every', CONFIG.REFRESH_INTERVAL / 1000, 'seconds.'); } init(); })();