secutorpro's picture
🐳 27/02 - 18:31 - ok en application aussi
2ed297b verified
// Audio Context for Sound Effects
let audioCtx = null;
let soundEnabled = false;
let ws = null; // WebSocket connection
// Authentication State
let currentUser = null;
let sessionId = null;
let accessLevel = null;
const ACTIVE_SESSIONS = new Map();
// API Configuration
const API_BASE = 'http://localhost:3000/api';
document.addEventListener('DOMContentLoaded', () => {
// Initialize Audio on first interaction (browser policy)
document.body.addEventListener('click', initAudio, { once: true });
document.getElementById('btn-sound').addEventListener('click', toggleSound);
// Setup Login Form
setupAuthentication();
// Terminal Setup
setupTerminal();
setupAIChat();
// Feather Icons re-render
if (typeof feather !== 'undefined') {
feather.replace();
}
});
// Authentication System
function setupAuthentication() {
const loginForm = document.getElementById('login-form');
if (loginForm) {
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
const user = document.getElementById('login-user').value;
const pass = document.getElementById('login-pass').value;
await authenticateUser(user, pass);
});
}
}
function checkAuthentication() {
const session = localStorage.getItem('cybershield_session');
if (session) {
const sessionData = JSON.parse(session);
if (sessionData.expiry > Date.now()) {
restoreSession(sessionData);
} else {
localStorage.removeItem('cybershield_session');
showLoginModal();
}
} else {
showLoginModal();
}
}
function showLoginModal() {
document.getElementById('login-modal').classList.remove('hidden');
document.getElementById('main-container').classList.add('blur-sm');
}
function hideLoginModal() {
document.getElementById('login-modal').classList.add('hidden');
document.getElementById('main-container').classList.remove('blur-sm');
document.getElementById('main-container').classList.add('pt-8');
}
async function authenticateUser(username, password) {
// Simulation d'authentification (en production: appel API sécurisé)
if (username.startsWith('ADMIN_') && password.length >= 8) {
currentUser = username;
sessionId = 'AUTH-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
accessLevel = 'ADMIN_T3';
// Store session
const sessionData = {
user: currentUser,
sessionId: sessionId,
level: accessLevel,
expiry: Date.now() + (8 * 60 * 60 * 1000) // 8 heures
};
localStorage.setItem('cybershield_session', JSON.stringify(sessionData));
// Update UI
document.getElementById('current-user').textContent = currentUser;
document.getElementById('session-id').textContent = sessionId;
document.getElementById('session-bar').classList.remove('hidden');
hideLoginModal();
// Log access
addLogEntry(`Authentification réussie: ${username} (Niveau ${accessLevel})`, 'success', 'AUTH');
// Initialize Systems after auth
initializeSystems();
// Start access monitoring
startAccessMonitoring();
startEgressMonitoring();
startIngressMonitoring();
playBeep(800, 'sine', 0.2);
return true;
} else {
addLogEntry(`Tentative d'authentification échouée: ${username}`, 'error', 'AUTH');
alert('Identifiants invalides. Format requis: ADMIN_xxxx');
return false;
}
}
function restoreSession(sessionData) {
currentUser = sessionData.user;
sessionId = sessionData.sessionId;
accessLevel = sessionData.level;
document.getElementById('current-user').textContent = currentUser;
document.getElementById('session-id').textContent = sessionId;
document.getElementById('session-bar').classList.remove('hidden');
document.getElementById('main-container').classList.add('pt-8');
initializeSystems();
startAccessMonitoring();
startEgressMonitoring();
startIngressMonitoring();
}
function logout() {
if (confirm('Confirmer la déconnexion ?')) {
localStorage.removeItem('cybershield_session');
addLogEntry(`Déconnexion: ${currentUser}`, 'info', 'AUTH');
location.reload();
}
}
function forceLogoutAll() {
if (confirm('ÊTES-VOUS SÛR ? Cette action déconnectera tous les utilisateurs actifs.')) {
broadcastAlertToServer({
type: 'FORCE_LOGOUT_ALL',
severity: 'CRITICAL',
description: 'Déconnexion forcée de toutes les sessions par administrateur',
timestamp: new Date().toISOString()
});
addLogEntry('Déconnexion forcée de toutes les sessions', 'error', 'AUTH');
}
}
function initializeSystems() {
// Initialize UI
initLiveChart();
initMap();
// Connect WebSocket for real-time alerts
connectWebSocket();
// Start Real-time Data Fetching from Backend
fetchStats();
setInterval(fetchStats, 1000);
startVulnStream();
startInfraStream();
startMapStream();
initCognitiveEngine();
startCognitiveStream();
startLogStream();
// Initialize USB monitoring simulation
simulateUSBMonitoring();
}
// Access Control Monitoring
function startAccessMonitoring() {
// Simulate active sessions monitoring
updateSessionsTable();
setInterval(updateSessionsTable, 5000);
// Simulate file access monitoring
simulateFileAccessMonitoring();
}
function updateSessionsTable() {
const tbody = document.getElementById('sessions-table');
if (!tbody) return;
const sessions = [
{ user: currentUser, ip: '192.168.1.100', time: new Date().toLocaleTimeString(), level: 'ADMIN_T3', status: 'Active' },
{ user: 'AGENT_4521', ip: '192.168.1.45', time: '09:15:32', level: 'OPERATOR', status: 'Active' },
{ user: 'TECH_8842', ip: '10.0.0.15', time: '08:30:12', level: 'TECHNICIAN', status: 'Idle' }
];
tbody.innerHTML = sessions.map(s => `
<tr class="hover:bg-gray-800/30 transition">
<td class="px-6 py-3 font-mono text-white">${s.user}</td>
<td class="px-6 py-3 font-mono text-cyber-primary">${s.ip}</td>
<td class="px-6 py-3 text-gray-400">${s.time}</td>
<td class="px-6 py-3"><span class="text-xs ${s.level === 'ADMIN_T3' ? 'text-red-400' : 'text-yellow-400'} border border-gray-700 px-2 py-1 rounded">${s.level}</span></td>
<td class="px-6 py-3"><span class="text-xs ${s.status === 'Active' ? 'text-green-400' : 'text-gray-500'} flex items-center"><span class="w-2 h-2 ${s.status === 'Active' ? 'bg-green-400 animate-pulse' : 'bg-gray-500'} rounded-full mr-2"></span>${s.status}</span></td>
<td class="px-6 py-3">
${s.user !== currentUser ? `<button onclick="terminateSession('${s.user}')" class="text-red-400 hover:text-red-300 text-xs border border-red-500/30 px-2 py-1 rounded hover:bg-red-500/20">Terminer</button>` : '<span class="text-xs text-gray-600">Current</span>'}
</td>
</tr>
`).join('');
document.getElementById('active-sessions').textContent = sessions.length;
document.getElementById('admin-sessions').textContent = sessions.filter(s => s.level === 'ADMIN_T3').length;
}
function terminateSession(username) {
addLogEntry(`Session terminée: ${username}`, 'warning', 'AUTH');
updateSessionsTable();
}
// USB Device Monitoring
function simulateUSBMonitoring() {
const usbDevices = [
{ name: 'SanDisk Cruzer 32GB', vid: '0781', pid: '5567', status: 'blocked', type: 'storage' },
{ name: 'Logitech Mouse', vid: '046d', pid: 'c52b', status: 'allowed', type: 'hid' },
{ name: 'Unknown Device', vid: '1234', pid: '5678', status: 'pending', type: 'unknown' }
];
const container = document.getElementById('usb-monitor');
if (!container) return;
container.innerHTML = usbDevices.map(dev => `
<div class="p-3 bg-gray-900 rounded border ${dev.status === 'blocked' ? 'border-red-500/50 bg-red-900/10' : (dev.status === 'pending' ? 'border-yellow-500/50' : 'border-green-500/30')} flex justify-between items-center animate-fade-in">
<div class="flex items-center">
<i data-feather="usb" class="w-4 h-4 ${dev.status === 'blocked' ? 'text-red-400' : (dev.status === 'pending' ? 'text-yellow-400' : 'text-green-400')} mr-2"></i>
<div>
<div class="text-sm text-gray-300">${dev.name}</div>
<div class="text-[10px] text-gray-500">VID:${dev.vid} PID:${dev.pid} | ${dev.type}</div>
</div>
</div>
<div class="flex items-center space-x-2">
<span class="text-xs ${dev.status === 'blocked' ? 'text-red-400' : (dev.status === 'pending' ? 'text-yellow-400' : 'text-green-400')} uppercase">${dev.status}</span>
${dev.status === 'pending' ? `
<button onclick="authorizeUSB('${dev.vid}', '${dev.pid}')" class="text-green-400 hover:text-green-300 text-xs border border-green-500/30 px-2 py-1 rounded">Autoriser</button>
<button onclick="blockUSB('${dev.vid}', '${dev.pid}')" class="text-red-400 hover:text-red-300 text-xs border border-red-500/30 px-2 py-1 rounded">Bloquer</button>
` : ''}
</div>
</div>
`).join('');
if (typeof feather !== 'undefined') feather.replace();
}
function authorizeUSB(vid, pid) {
addLogEntry(`USB Autorisé: VID:${vid} PID:${pid}`, 'success', 'USB');
simulateUSBMonitoring();
}
function blockUSB(vid, pid) {
addLogEntry(`USB Bloqué: VID:${vid} PID:${pid}`, 'error', 'USB');
simulateUSBMonitoring();
}
function authorizeAllUSB() {
addLogEntry('Tous les périphériques USB autorisés', 'warning', 'USB');
simulateUSBMonitoring();
}
function blockAllUSB() {
addLogEntry('Tous les périphériques USB bloqués', 'error', 'USB');
simulateUSBMonitoring();
}
// File Access Monitoring
function simulateFileAccessMonitoring() {
const sensitiveFiles = [
{ file: '/secure/taj/TAJ_2024.db', user: 'AGENT_4521', action: 'READ', time: new Date().toLocaleTimeString() },
{ file: '/secure/dgfip/FICoba_National.db', user: 'ADMIN_SYS', action: 'READ', time: new Date().toLocaleTimeString() },
{ file: '/secure/fpr/FPR_Access.log', user: 'SYSTEM', action: 'WRITE', time: new Date().toLocaleTimeString() }
];
const container = document.getElementById('file-access-log');
if (!container) return;
container.innerHTML = sensitiveFiles.map(f => `
<div class="flex justify-between items-center p-2 bg-gray-900/50 rounded border-l-2 ${f.action === 'WRITE' ? 'border-red-500' : 'border-cyber-primary'} mb-1">
<div class="flex items-center space-x-2">
<i data-feather="file" class="w-3 h-3 text-gray-400"></i>
<span class="text-gray-300">${f.file.split('/').pop()}</span>
<span class="text-[10px] text-gray-500">${f.user}</span>
</div>
<span class="text-[10px] ${f.action === 'WRITE' ? 'text-red-400' : 'text-cyber-primary'} border border-gray-700 px-1 rounded">${f.action}</span>
</div>
`).join('');
}
// Ingress Monitoring (what enters)
function startIngressMonitoring() {
const container = document.getElementById('ingress-logs');
if (!container) return;
const ingressEvents = [
{ type: 'CONNECTION', src: '185.220.101.42:4444', dst: '192.168.1.100:443', status: 'BLOCKED', proto: 'TCP' },
{ type: 'DOWNLOAD', file: 'update.exe', size: '2.4MB', status: 'SCANNING', user: 'AGENT_4521' },
{ type: 'EMAIL', from: 'suspect@phishing.ru', to: 'admin@interieur.fr', status: 'QUARANTINED', subject: 'Urgent: Mise à jour' },
{ type: 'AUTH', user: 'TECH_8842', src: '10.0.0.15', status: 'SUCCESS', method: 'RSA-Key' }
];
setInterval(() => {
const evt = ingressEvents[Math.floor(Math.random() * ingressEvents.length)];
const time = new Date().toLocaleTimeString();
let colorClass = 'text-gray-400';
let statusColor = 'text-gray-500';
if (evt.status === 'BLOCKED' || evt.status === 'QUARANTINED') {
colorClass = 'text-red-400';
statusColor = 'text-red-400';
} else if (evt.status === 'SUCCESS') {
colorClass = 'text-green-400';
statusColor = 'text-green-400';
} else if (evt.status === 'SCANNING') {
colorClass = 'text-yellow-400';
statusColor = 'text-yellow-400';
}
const line = document.createElement('div');
line.className = `font-mono text-xs ${colorClass} border-l-2 ${evt.status === 'BLOCKED' ? 'border-red-500 bg-red-900/10' : 'border-gray-700'} pl-2 py-1`;
line.innerHTML = `
<span class="text-gray-600">[${time}]</span>
<span class="uppercase font-bold ml-1">${evt.type}</span>
<span class="ml-2">${evt.src || evt.file || evt.from}${evt.dst || evt.user || evt.to}</span>
<span class="${statusColor} ml-2 border border-gray-700 px-1 rounded text-[10px]">${evt.status}</span>
`;
container.prepend(line);
if (container.children.length > 50) container.lastChild.remove();
if (evt.status === 'BLOCKED' || evt.status === 'QUARANTINED') {
addLogEntry(`Ingress bloqué: ${evt.type} depuis ${evt.src || evt.from}`, 'error', 'INGRESS');
}
}, 3000);
}
// Egress Monitoring (what exits - Data Exfiltration Prevention)
function startEgressMonitoring() {
let totalVolume = 0;
setInterval(() => {
// Simulate egress traffic
const volume = Math.random() * 50; // MB
totalVolume += volume;
const isBlocked = Math.random() > 0.8;
const isSuspicious = Math.random() > 0.7;
const blockedEl = document.getElementById('blocked-files');
const suspectEl = document.getElementById('suspect-conn');
const volumeEl = document.getElementById('egress-volume');
const rateEl = document.getElementById('egress-rate');
if (blockedEl && isBlocked) {
blockedEl.textContent = parseInt(blockedEl.textContent || 0) + 1;
addEgressAlert('Tentative d\'exfiltration bloquée', 'Fichier TAJ détecté dans flux sortant', 'CRITICAL');
}
if (suspectEl && isSuspicious) {
suspectEl.textContent = parseInt(suspectEl.textContent || 0) + 1;
addEgressAlert('Connexion sortante suspecte', 'DNS tunneling détecté vers 185.220.101.42', 'HIGH');
}
// Update stats
if (volumeEl) volumeEl.textContent = totalVolume.toFixed(1) + ' MB';
if (rateEl) rateEl.textContent = '+' + volume.toFixed(1) + ' MB/s';
// Update analysis
updateEgressAnalysis();
}, 2000);
}
function addEgressAlert(title, detail, severity) {
const container = document.getElementById('egress-alerts-log');
const alertsEl = document.getElementById('egress-alerts');
if (!container || !alertsEl) return;
const alerts = parseInt(alertsEl.textContent || 0);
alertsEl.textContent = alerts + 1;
const line = document.createElement('div');
line.className = `p-2 rounded border-l-2 ${severity === 'CRITICAL' ? 'border-red-500 bg-red-900/20' : 'border-yellow-500 bg-yellow-900/10'} animate-fade-in`;
line.innerHTML = `
<div class="flex justify-between items-start">
<div>
<div class="text-sm font-bold ${severity === 'CRITICAL' ? 'text-red-400' : 'text-yellow-400'}">${title}</div>
<div class="text-xs text-gray-400">${detail}</div>
</div>
<span class="text-[10px] text-gray-500">${new Date().toLocaleTimeString()}</span>
</div>
`;
container.prepend(line);
if (container.children.length > 20) container.lastChild.remove();
// Global alert
if (severity === 'CRITICAL') {
addLogEntry(`EGRESS ALERT: ${title} - ${detail}`, 'error', 'DLP');
playAlertSound();
}
}
function updateEgressAnalysis() {
const container = document.getElementById('egress-analysis');
if (!container) return;
const processes = [
{ name: 'chrome.exe', status: 'Autorisé', width: 15, color: 'bg-green-500', dest: 'google.com', size: '1.2 MB' },
{ name: 'outlook.exe', status: 'Limité', width: 45, color: 'bg-yellow-500', dest: 'smtp.office365.com', size: '8.4 MB' },
{ name: 'svchost.exe', status: 'Surveillance', width: 30, color: 'bg-orange-500', dest: 'windowsupdate.com', size: '2.1 MB' },
{ name: 'unknown_process', status: 'Bloqué', width: 0, color: 'bg-red-500', dest: 'BLOCKED', size: '0 MB' }
];
container.innerHTML = processes.map(p => `
<div class="p-3 bg-gray-900 rounded border border-gray-700">
<div class="flex justify-between mb-2">
<span class="text-sm text-gray-300">${p.name}</span>
<span class="text-xs ${p.status === 'Bloqué' ? 'text-red-400' : (p.status === 'Limité' ? 'text-yellow-400' : 'text-green-400')}">${p.status}</span>
</div>
<div class="w-full bg-gray-800 rounded-full h-2">
<div class="${p.color} h-2 rounded-full transition-all duration-1000" style="width: ${p.width}%"></div>
</div>
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>${p.dest}</span>
<span>${p.size}</span>
</div>
</div>
`).join('');
}
function enableStrictMode() {
if (confirm('Activer le mode STRICT ? Tous les flux sortants non essentiels seront bloqués.')) {
addLogEntry('MODE STRICT activé - Egress filtering renforcé', 'error', 'DLP');
addEgressAlert('Mode Strict Activé', 'Tous les transferts > 1Mo nécessitent autorisation', 'INFO');
}
}
function clearEgressAlerts() {
const alertsEl = document.getElementById('egress-alerts');
if (alertsEl) alertsEl.textContent = '0';
const container = document.getElementById('egress-alerts-log');
if (container) container.innerHTML = '';
}
function refreshAccessLogs() {
addLogEntry('Rafraîchissement des logs d\'accès...', 'info', 'AUTH');
updateSessionsTable();
simulateFileAccessMonitoring();
simulateUSBMonitoring();
}
// WebSocket Connection for Critical Alerts
function connectWebSocket() {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.hostname}:3000`;
console.log('🔌 Connexion WebSocket:', wsUrl);
ws = new WebSocket(wsUrl);
ws.onopen = () => {
console.log('🔌 Connecté au SOC temps réel');
updateSystemStatus('online');
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
handleRealtimeAlert(data);
};
ws.onerror = (error) => {
console.error('WebSocket erreur:', error);
updateSystemStatus('error');
};
ws.onclose = () => {
console.log('Déconnecté - Reconnexion dans 5s...');
updateSystemStatus('offline');
setTimeout(connectWebSocket, 5000);
};
}
function handleRealtimeAlert(data) {
// Vérifier que data existe
if (!data) return;
// Jouer son d'alerte si critique
if (data.severity === 'CRITICAL' || data.severity === 'HIGH') {
playAlertSound();
showToastAlert(data);
// Notification urgente pour administration
if (data.type && (data.type.includes('EXFILTRATION') || data.type.includes('EMAIL') || data.type.includes('SENSITIVE'))) {
showEmergencyAlert(data);
}
}
// Mettre à jour l'interface selon le type avec vérifications null
switch(data.type) {
case 'SUSPICIOUS_CONNECTION':
if (data.data && data.data.peer) {
addLogEntry(`Connexion suspecte: ${data.data.peer}:${data.data.peerPort || 'N/A'}`, 'error', data.data.peer);
}
break;
case 'BRUTE_FORCE_DETECTED':
const count = data.count || 0;
const ip = (data.ips && data.ips.length > 0) ? data.ips[0] : 'UNKNOWN';
addLogEntry(`Brute Force (${count} tentatives)`, 'error', ip);
incrementThreatCounter(count);
break;
case 'IP_BLOCKED':
addLogEntry(`IP bloquée: ${data.ip || 'UNKNOWN'}`, 'success', 'SYSTEM');
break;
case 'PROCESS_ALERT':
if (data.data && data.data.length > 0 && data.data[0].name) {
addLogEntry(`Processus suspect: ${data.data[0].name}`, 'warning', 'LOCAL');
}
break;
// NOUVEAUX: Alertes spécifiques administration française
case 'DATA_EXFILTRATION_DETECTED':
addLogEntry(`🚨 EXFILTRATION: ${data.description || 'Données sensibles'}`, 'error', 'TAJ/FPR');
incrementThreatCounter(10);
break;
case 'EMAIL_COMPROMISE':
if (data.data) {
const user = data.data.user || 'UNKNOWN';
const country = data.data.country || 'UNKNOWN';
const ip = data.data.ip || 'UNKNOWN';
addLogEntry(`📧 MESSAGERIE COMPROMISE: ${user} depuis ${country}`, 'error', ip);
}
break;
case 'SENSITIVE_FILE_ACCESS':
if (data.data) {
const isBanking = data.data.type === 'FINANCES';
const prefix = isBanking ? '🏦 FICoba/DGFiP:' : '🔒';
const file = data.data.file || 'UNKNOWN';
const volume = data.data.volume || 'N/A';
addLogEntry(`${prefix} ACCÈS ${file} (${volume})`, 'error', data.data.type || 'SYSTEM');
if (isBanking) {
const records = data.data.records || 'N/A';
showEmergencyAlert({
type: 'FICOBa_BREACH_ATTEMPT',
severity: 'CRITICAL',
description: `Tentative d'accès au fichier des comptes bancaires: ${file}. ${records} comptes potentiellement concernés.`,
timestamp: data.timestamp || new Date().toISOString()
});
}
}
break;
case 'CREDENTIAL_STOLEN':
if (data.data) {
const user = data.data.user || 'UNKNOWN';
const source = data.data.source || 'UNKNOWN';
addLogEntry(`🔑 CODE ACCÈS VOLÉ: ${user} via ${source}`, 'warning', 'AUTH');
}
break;
case 'USB_DETECTED':
if (data.device) {
addLogEntry(`USB Détecté: ${data.device.model || 'Unknown'}`, 'info', 'USB');
}
break;
case 'USB_BLOCKED':
if (data.device) {
addLogEntry(`USB Bloqué: ${data.device.model || 'Unknown'}`, 'error', 'USB');
}
break;
case 'FILE_ACCESS':
case 'FILE_MODIFIED':
case 'SENSITIVE_FILE_CREATED':
if (data.file) {
addLogEntry(`Fichier: ${data.type} - ${data.file}`, data.severity === 'CRITICAL' ? 'error' : 'warning', 'FS');
}
break;
}
}
// NOUVEAU: Alerte d'urgence en plein écran pour incidents critiques
function showEmergencyAlert(data) {
if (!data) return;
const existing = document.getElementById('emergency-alert');
if (existing) existing.remove();
const alertType = data.type || 'UNKNOWN_ALERT';
const description = data.description || 'Menace détectée sur infrastructure sensible';
const alert = document.createElement('div');
alert.id = 'emergency-alert';
alert.className = 'fixed inset-0 bg-red-900/90 z-[100] flex items-center justify-center backdrop-blur-sm animate-fade-in';
alert.innerHTML = `
<div class="bg-black border-2 border-red-500 rounded-xl p-8 max-w-2xl shadow-[0_0_50px_rgba(239,68,68,0.5)]">
<div class="flex items-center mb-4">
<i data-feather="alert-octagon" class="w-12 h-12 text-red-500 mr-4 animate-pulse"></i>
<div>
<h2 class="text-2xl font-bold text-red-500 uppercase">Alerte Critique Administration</h2>
<p class="text-red-300 font-mono">${new Date().toLocaleTimeString('fr-FR')}</p>
</div>
</div>
<div class="bg-red-950/50 p-4 rounded border border-red-500/30 mb-6">
<p class="text-white text-lg font-bold mb-2">${alertType.replace(/_/g, ' ')}</p>
<p class="text-gray-300">${description}</p>
</div>
<div class="flex space-x-4">
<button onclick="this.closest('#emergency-alert').remove()" class="flex-1 bg-red-600 hover:bg-red-700 text-white py-3 rounded font-bold transition">
Acquitter l'alerte
</button>
<button onclick="isolateSystem(); this.closest('#emergency-alert').remove()" class="flex-1 bg-gray-800 hover:bg-gray-700 border border-red-500 text-red-400 py-3 rounded font-bold transition">
Isoler le Système
</button>
</div>
</div>
`;
document.body.appendChild(alert);
if (typeof feather !== 'undefined') feather.replace();
}
// NOUVEAU: Fonctions de réponse aux incidents
async function blockIP(ip) {
try {
const response = await fetch('http://localhost:3000/api/block-ip', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ip: ip, reason: 'Attaque administration française' })
});
const data = await response.json();
addLogEntry(`IP ${ip} bloquée définitivement`, 'success', 'FIREWALL');
} catch (e) {
addLogEntry(`Échec blocage IP ${ip}`, 'error', 'SYSTEM');
}
}
function isolateSystem() {
addLogEntry('🔒 ISOLEMENT DU SYSTÈME ENGAGÉ', 'error', 'ADMIN');
showToastAlert({
type: 'SYSTEM_ISOLATION',
severity: 'CRITICAL',
description: 'Réseau segmenté, accès externes coupés',
timestamp: new Date().toISOString()
});
}
function revokeCredentials() {
addLogEntry('🔑 RÉVOCATION MASSIVE DES ACCÈS', 'warning', 'ADMIN');
alert('Tous les tokens de session ont été révoqués. Reconnexion requise pour tous les agents.');
}
function exportReport() {
const report = {
date: new Date().toISOString(),
administration: 'Ministère de l\'Intérieur (Simulation)',
incidents: [], // Récupérer depuis les logs visibles si besoin
status: 'Rapport généré par CyberShield SOC'
};
console.log('Rapport exporté:', report);
addLogEntry('📄 Rapport d\'incident exporté', 'info', 'REPORT');
alert('Rapport d\'incident exporté vers le SI-Central');
}
function showToastAlert(data) {
if (!data) return;
// Créer une notification toast pour alertes critiques
const toast = document.createElement('div');
toast.className = 'fixed top-4 right-4 bg-red-600 text-white px-6 py-4 rounded-lg shadow-2xl z-50 animate-slide-in border-l-4 border-red-300 flex items-center';
toast.style.animation = 'slideInRight 0.3s ease-out';
toast.innerHTML = `
<i data-feather="alert-triangle" class="mr-3"></i>
<div>
<div class="font-bold text-sm uppercase">${data.type || 'ALERTE'}</div>
<div class="text-xs opacity-90">${data.timestamp ? new Date(data.timestamp).toLocaleTimeString('fr-FR') : new Date().toLocaleTimeString('fr-FR')}</div>
</div>
`;
document.body.appendChild(toast);
if (typeof feather !== 'undefined') feather.replace();
setTimeout(() => {
toast.remove();
}, 5000);
}
function updateSystemStatus(status) {
const statusEl = document.getElementById('status-text');
if (!statusEl) return;
const indicator = statusEl.parentElement.nextElementSibling;
if (status === 'online') {
statusEl.innerText = 'Connecté - Temps Réel (PROD)';
statusEl.className = 'text-green-400 font-mono font-bold';
if (indicator) indicator.className = 'w-2 h-2 bg-green-400 rounded-full animate-pulse';
} else if (status === 'error') {
statusEl.innerText = 'Erreur Connexion';
statusEl.className = 'text-red-400 font-mono';
if (indicator) indicator.className = 'w-2 h-2 bg-red-500 rounded-full';
} else {
statusEl.innerText = 'Hors ligne';
statusEl.className = 'text-gray-400 font-mono';
if (indicator) indicator.className = 'w-2 h-2 bg-gray-500 rounded-full';
}
}
function incrementThreatCounter(count = 1) {
// Trouve le compteur spécifique des menaces (premier counter)
const counter = document.querySelector('.counter[data-target]') || document.querySelector('.counter');
if (counter) {
let current = parseInt(counter.innerText.replace(/,/g, '')) || 0;
current += count;
counter.innerText = current.toLocaleString();
}
}
// --- Sound Manager ---
function initAudio() {
if (!audioCtx) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
}
function toggleSound() {
soundEnabled = !soundEnabled;
const btn = document.getElementById('btn-sound');
const icon = btn.querySelector('i');
if (soundEnabled) {
icon.setAttribute('data-feather', 'volume-2');
icon.classList.replace('text-gray-400', 'text-cyber-primary');
playBeep(600, 'sine', 0.1); // Confirmation beep
} else {
icon.setAttribute('data-feather', 'volume-x');
icon.classList.replace('text-cyber-primary', 'text-gray-400');
}
feather.replace();
}
function playAlertSound() {
if (!soundEnabled || !audioCtx) return;
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(440, audioCtx.currentTime);
osc.frequency.exponentialRampToValueAtTime(880, audioCtx.currentTime + 0.1);
gain.gain.setValueAtTime(0.1, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.3);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start();
osc.stop(audioCtx.currentTime + 0.3);
}
// 0. Navigation Logic (SPA)
function navigateTo(viewId) {
// Hide all views
document.querySelectorAll('[id^="view-"]').forEach(el => el.classList.add('hidden'));
// Show selected
document.getElementById(`view-${viewId}`).classList.remove('hidden');
// Reset Nav Styles
document.querySelectorAll('.nav-link').forEach(el => {
el.classList.remove('active', 'bg-gray-800/50', 'text-cyber-primary', 'border-gray-700');
el.classList.add('text-gray-400');
});
// Set Active Nav
const activeNav = document.getElementById(`nav-${viewId}`);
if(activeNav) {
activeNav.classList.add('active', 'bg-gray-800/50', 'text-cyber-primary', 'border-gray-700');
activeNav.classList.remove('text-gray-400');
}
// Re-init icons
feather.replace();
}
// 1. Real-time Chart Initialization avec données réseau réelles
let trafficHistory = [];
function initLiveChart() {
const chartContainer = document.getElementById('liveChart');
const barCount = 50;
// Create initial bars
for(let i = 0; i < barCount; i++) {
const bar = document.createElement('div');
bar.className = 'bg-cyber-primary w-1.5 rounded-t-sm opacity-60 transition-all duration-300 ease-in-out hover:opacity-100 hover:bg-white';
bar.style.height = '10%';
chartContainer.appendChild(bar);
trafficHistory.push(10);
}
}
function updateTrafficChart(currentTraffic) {
const chartContainer = document.getElementById('liveChart');
if (!chartContainer || chartContainer.children.length === 0) return;
// Décaler l'historique
trafficHistory.shift();
trafficHistory.push(currentTraffic);
// Mettre à jour les barres visuellement
const bars = chartContainer.children;
const maxTraffic = Math.max(...trafficHistory, 100); // Échelle dynamique
for (let i = 0; i < bars.length; i++) {
const percentage = (trafficHistory[i] / maxTraffic) * 100;
bars[i].style.height = `${Math.max(percentage, 5)}%`;
// Coloration selon l'intensité
if (percentage > 80) {
bars[i].className = 'bg-red-500 w-1.5 rounded-t-sm shadow-[0_0_10px_rgba(239,68,68,0.8)] transition-all duration-300';
} else if (percentage > 50) {
bars[i].className = 'bg-yellow-400 w-1.5 rounded-t-sm transition-all duration-300';
} else {
bars[i].className = 'bg-cyber-primary w-1.5 rounded-t-sm opacity-60 transition-all duration-300 ease-in-out hover:opacity-100 hover:bg-white';
}
}
}
// 2. Fetch Statistics from Backend API avec données réelles
let previousAlertCount = 0;
async function fetchStats() {
try {
const response = await fetch('http://localhost:3000/api/stats');
if (!response.ok) throw new Error('Network response was not ok');
const data = await response.json();
// Vérifier nouvelles alertes critiques
if (data.criticalAlerts > previousAlertCount) {
playAlertSound();
// Notification visuelle urgente
document.querySelectorAll('.counter')[3].parentElement.parentElement.classList.add('pulse-alert');
setTimeout(() => {
document.querySelectorAll('.counter')[3].parentElement.parentElement.classList.remove('pulse-alert');
}, 2000);
}
previousAlertCount = data.criticalAlerts;
// Update Dashboard Data avec animation
updateCounterAnimated(document.querySelector('.counter'), data.threatsBlocked);
// Santé serveur avec couleur dynamique
const healthEl = document.querySelectorAll('.counter')[1].nextElementSibling;
healthEl.innerText = data.serverHealth + "%";
if (data.serverHealth < 50) healthEl.className = 'text-3xl font-bold text-red-500 mt-2';
else if (data.serverHealth < 80) healthEl.className = 'text-3xl font-bold text-yellow-400 mt-2';
else healthEl.className = 'text-3xl font-bold text-cyber-secondary mt-2';
// Trafic réseau
document.querySelectorAll('.counter')[2].previousElementSibling.lastElementChild.innerText = data.networkTraffic + " MB/s";
updateTrafficChart(data.networkTraffic);
// Alertes avec formatage
const alertEl = document.querySelectorAll('.counter')[3];
alertEl.innerText = data.criticalAlerts.toString().padStart(2, '0');
if (data.criticalAlerts > 0) {
alertEl.classList.add('text-red-500');
}
} catch (error) {
console.error('Erreur fetch stats:', error);
updateSystemStatus('error');
}
}
function updateCounterAnimated(element, targetValue) {
if (!element) return;
const current = parseInt(element.innerText.replace(/,/g, '')) || 0;
if (current !== targetValue) {
element.innerText = targetValue.toLocaleString();
// Effet visuel de mise à jour
element.style.transform = 'scale(1.1)';
setTimeout(() => element.style.transform = 'scale(1)', 200);
}
}
// 3. Gestion des logs temps réel
function addLogEntry(message, type, ip) {
const container = document.getElementById('logContainer');
if (!container) return;
const logLine = document.createElement('div');
logLine.className = 'log-entry flex justify-between border-b border-gray-900 pb-1 mb-1 animate-fade-in';
let colorClass = 'text-gray-400';
if(type === 'error') colorClass = 'text-red-400';
if(type === 'success') colorClass = 'text-green-400';
if(type === 'warning') colorClass = 'text-yellow-400';
const time = new Date().toLocaleTimeString('fr-FR', { hour12: false });
logLine.innerHTML = `
<span class="${colorClass} font-mono text-xs">[${time}] ${message} (${ip || 'SYSTEM'})</span>
<span class="text-gray-600 text-xs">${type.toUpperCase()}</span>
`;
container.prepend(logLine);
// Garder seulement les 50 dernières entrées
while(container.children.length > 50) {
container.lastElementChild.remove();
}
}
async function startLogStream() {
// Récupération initiale puis via WebSocket principalement
try {
const response = await fetch('http://localhost:3000/api/log');
const data = await response.json();
addLogEntry(data.msg, data.type, data.ip);
} catch (e) {
addLogEntry('Initialisation du système de logs...', 'info', 'LOCAL');
}
}
// Fallback to simulate data if user doesn't run the server
function simulateFallbackData() {
// Just ensures the chart keeps moving if no server is present
const chartContainer = document.getElementById('liveChart');
if(!chartContainer.children.length) initLiveChart();
const bars = chartContainer.children;
for(let i = 0; i < bars.length - 1; i++) {
bars[i].style.height = bars[i+1].style.height;
}
const lastBar = bars[bars.length - 1];
const newHeight = Math.floor(Math.random() * 90) + 10;
lastBar.style.height = `${newHeight}%`;
if(newHeight > 80) {
lastBar.className = 'bg-red-500 w-2 rounded-t-sm shadow-[0_0_10px_rgba(239,68,68,0.8)] transition-all duration-300';
} else if (newHeight > 50) {
lastBar.className = 'bg-yellow-400 w-2 rounded-t-sm transition-all duration-300';
} else {
lastBar.className = 'bg-cyber-primary w-2 rounded-t-sm opacity-50 transition-all duration-300';
}
}
// --- NEW FEATURES: MAP, VULNS, INFRA ---
// 4. Threat Map Logic
function initMap() {
// Clear existing svg lines if any
const svg = document.getElementById('map-svg-layer');
while (svg.lastChild) {
svg.removeChild(svg.lastChild);
}
}
async function startMapStream() {
const container = document.getElementById('geo-log-container');
const mapArea = document.getElementById('attack-map-container');
if (!container || !mapArea) return;
if (!document.getElementById('map-svg-layer')) {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.id = "map-svg-layer";
svg.style.position = "absolute";
svg.style.top = "0";
svg.style.left = "0";
svg.style.width = "100%";
svg.style.height = "100%";
svg.style.pointerEvents = "none";
svg.style.zIndex = "5";
mapArea.appendChild(svg);
}
const svg = document.getElementById('map-svg-layer');
const fetchMapData = async () => {
try {
const response = await fetch('http://localhost:3000/api/geo');
const data = await response.json();
// Add Log avec badge pays
const logLine = document.createElement('div');
logLine.className = 'border-b border-gray-900 pb-1 text-xs font-mono animate-fade-in flex justify-between items-center';
logLine.innerHTML = `
<div>
<span class="bg-red-500/20 text-red-400 px-1 rounded text-[10px] mr-1">${data.country}</span>
<span class="text-gray-300">${data.type}</span>
<span class="text-gray-500">from ${data.ip}</span>
</div>
<span class="text-[10px] text-gray-600">${new Date(data.timestamp).toLocaleTimeString('fr-FR')}</span>
`;
container.prepend(logLine);
if(container.children.length > 20) container.lastChild.remove();
// Animation sur la carte
const node = document.createElement('div');
node.className = 'geo-node';
const top = Math.random() * 80 + 10;
const left = Math.random() * 80 + 10;
node.style.top = `${top}%`;
node.style.left = `${left}%`;
mapArea.appendChild(node);
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
line.setAttribute("x1", `${left}%`);
line.setAttribute("y1", `${top}%`);
line.setAttribute("x2", "50%");
line.setAttribute("y2", "50%");
line.setAttribute("stroke", data.real ? "#ff0000" : "#00f0ff");
line.setAttribute("stroke-width", "2");
line.setAttribute("stroke-opacity", "0.8");
line.setAttribute("stroke-dasharray", "5,5");
const animate = document.createElementNS("http://www.w3.org/2000/svg", "animate");
animate.setAttribute("attributeName", "stroke-dashoffset");
animate.setAttribute("from", "100");
animate.setAttribute("to", "0");
animate.setAttribute("dur", "0.5s");
animate.setAttribute("repeatCount", "1");
line.appendChild(animate);
svg.appendChild(line);
// Impact visuel au centre (cible)
const target = document.createElement('div');
target.className = 'absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-4 h-4 bg-red-500 rounded-full animate-ping';
mapArea.appendChild(target);
setTimeout(() => {
node.remove();
line.remove();
target.remove();
}, 2000);
} catch(e) {
console.error('Erreur carte:', e);
}
};
// Plus fréquent si données réelles disponibles
setInterval(fetchMapData, 2000);
}
// --- 7. Terminal Logic ---
function toggleAIChat() {
const modal = document.getElementById('ai-chat-modal');
if (modal.classList.contains('hidden')) {
// Opening
modal.classList.remove('hidden');
// Small delay to allow CSS display property to apply before removing transform/opacity
setTimeout(() => {
modal.classList.remove('translate-y-4', 'opacity-0');
}, 10);
document.getElementById('ai-input').focus();
} else {
// Closing
modal.classList.add('translate-y-4', 'opacity-0');
// Wait for transition to finish before setting display: none
setTimeout(() => {
modal.classList.add('hidden');
}, 300);
}
}
function setupAIChat() {
const input = document.getElementById('ai-input');
input.addEventListener('keypress', function (e) {
if (e.key === 'Enter') sendAIMessage();
});
}
function sendAIMessage() {
const input = document.getElementById('ai-input');
const text = input.value.trim();
if(!text) return;
const history = document.getElementById('chat-history');
// User Message
const userMsg = `
<div class="flex items-start justify-end">
<div class="bg-cyber-primary/20 p-2 rounded-lg rounded-tr-none border border-cyber-primary/30 text-white max-w-[90%]">
${text}
</div>
</div>`;
history.innerHTML += userMsg;
input.value = '';
history.scrollTop = history.scrollHeight;
// AI Simulation Response
setTimeout(() => {
let response = "Analyse en cours...";
const lowerText = text.toLowerCase();
if(lowerText.includes("menace") || lowerText.includes("threat")) {
response = "J'ai analysé les 30 dernières minutes. Une tentative d'intrusion de type 'Brute Force' a été détectée sur le port 22. Elle a été bloquée automatiquement.";
} else if (lowerText.includes("serveur") || lowerText.includes("server")) {
response = "Les serveurs、主 인프라 sont sains. L'utilisation du CPU est stable à 24%. Aucun goulot d'étranglement détecté.";
} else if (lowerText.includes("optimiser") || lowerText.includes("optimize")) {
response = "Recommandation: Activer le protocole de mise en cache avancées sur le noeud DB-01 pour réduire la latence de 15%.";
} else {
response = "Données non traitées. Voulez-vous que je lance une analyse approfondie du pare-feu ?";
}
const aiMsg = `
<div class="flex items-start">
<div class="bg-purple-900/30 p-2 rounded-lg rounded-tl-none border border-purple-500/30 text-gray-300 max-w-[90%]">
${response}
</div>
</div>`;
history.innerHTML += aiMsg;
history.scrollTop = history.scrollHeight;
feather.replace();
}, 800);
}
function toggleTerminal() {
const modal = document.getElementById('terminal-modal');
const input = document.getElementById('terminal-input');
if (modal.classList.contains('hidden')) {
modal.classList.remove('hidden');
input.focus();
} else {
modal.classList.add('hidden');
}
}
function setupTerminal() {
const input = document.getElementById('terminal-input');
const output = document.getElementById('terminal-output');
input.addEventListener('keypress', function (e) {
if (e.key === 'Enter') {
const command = input.value.trim();
if (command) {
printToTerminal(`root@sys:~$ ${command}`);
executeCommand(command);
}
input.value = '';
}
});
}
function printToTerminal(text, color = 'text-green-400') {
const output = document.getElementById('terminal-output');
const p = document.createElement('p');
p.className = color + " font-mono";
p.innerText = text;
output.appendChild(p);
output.scrollTop = output.scrollHeight;
}
async function executeCommand(cmd) {
const output = document.getElementById('terminal-output');
const args = cmd.split(' ');
const command = args[0].toLowerCase();
// Realistic SOC terminal commands
switch(command) {
case 'help':
case '?':
printToTerminal("CYBERSHIELD SOC v3.0 - Commandes disponibles:", "text-cyber-primary");
printToTerminal(" netstat - Affiche les connexions actives");
printToTerminal(" ps - Liste des processus suspects");
printToTerminal(" kill [PID] - Termine un processus");
printToTerminal(" block [IP] - Bloque une adresse IP au firewall");
printToTerminal(" isolate - Mode confinement d'urgence");
printToTerminal(" scan [target] - Scan de vulnérabilités rapide");
printToTerminal(" clear - Nettoie le terminal");
printToTerminal(" status - État des systèmes");
break;
case 'netstat':
printToTerminal("Connexions réseau actives:", "text-yellow-400");
setTimeout(() => {
printToTerminal("tcp ESTABLISHED 192.168.1.100:443 -> 185.220.101.42:4444 [SUSPECT]", "text-red-400");
printToTerminal("tcp ESTABLISHED 192.168.1.5:22 -> 10.0.0.15:22 [SSH]", "text-green-400");
printToTerminal("udp OPEN 0.0.0.0:53 -> 8.8.8.8:53 [DNS]", "text-gray-400");
printToTerminal("tcp LISTEN 0.0.0.0:8080 -> 0.0.0.0:0 [HTTP]", "text-gray-400");
}, 300);
break;
case 'ps':
printToTerminal("Processus en cours:", "text-yellow-400");
setTimeout(() => {
printToTerminal("PID USER CPU MEM COMMAND", "text-gray-500");
printToTerminal("1 root 0.1 0.2 /sbin/init", "text-gray-400");
printToTerminal("1842 www-data 45.2 12.5 /usr/bin/python3 -m http.server [SUSPECT]", "text-red-400");
printToTerminal("2156 mysql 2.1 8.4 mysqld --daemonize", "text-gray-400");
printToTerminal("3421 root 0.0 0.1 /bin/bash /tmp/.hidden/script.sh [MALICIOUS]", "text-red-400 font-bold");
}, 300);
break;
case 'kill':
if (args[1]) {
printToTerminal(`Envoi du signal SIGTERM au processus ${args[1]}...`, "text-yellow-400");
setTimeout(() => {
printToTerminal(`Processus ${args[1]} terminé.`, "text-green-400");
addLogEntry(`Processus ${args[1]} terminé manuellement via terminal`, 'warning', 'ADMIN');
}, 500);
} else {
printToTerminal("Usage: kill [PID]", "text-red-400");
}
break;
case 'block':
if (args[1]) {
const ip = args[1];
if (/^(\d{1,3}\.){3}\d{1,3}$/.test(ip)) {
printToTerminal(`Blocage de l'IP ${ip}...`, "text-yellow-400");
setTimeout(() => {
printToTerminal(`iptables -A INPUT -s ${ip} -j DROP`, "text-gray-500");
printToTerminal(`IP ${ip} bloquée avec succès.`, "text-green-400");
blockIP(ip);
}, 600);
} else {
printToTerminal("IP invalide.", "text-red-400");
}
} else {
printToTerminal("Usage: block [IP]", "text-red-400");
}
break;
case 'isolate':
printToTerminal("INITIATION DU CONFINEMENT D'URGENCE...", "text-red-400 font-bold");
setTimeout(() => {
printToTerminal("[-] Coupure des interfaces réseau externes...", "text-yellow-400");
printToTerminal("[-] Isolation du VLAN sensibles...", "text-yellow-400");
printToTerminal("[-] Preservation des logs forensiques...", "text-yellow-400");
printToTerminal("[✓] SYSTÈME ISOLÉ - Mode quarantaine actif", "text-green-400 font-bold");
isolateSystem();
}, 1000);
break;
case 'scan':
const target = args[1] || 'localhost';
printToTerminal(`Lancement du scan de vulnérabilités sur ${target}...`, "text-yellow-400");
setTimeout(() => {
printToTerminal("[*] Scan des ports ouverts...", "text-gray-500");
printToTerminal("[!] Port 22/tcp open SSH - Version outdated", "text-orange-400");
printToTerminal("[!] Port 3389/tcp open RDP - Vulnérable à BlueKeep", "text-red-400");
printToTerminal("[*] Scan terminé. 2 vulnérabilités trouvées.", "text-green-400");
}, 2000);
break;
case 'clear':
output.innerHTML = '';
break;
case 'uptime':
const days = Math.floor(Math.random() * 100);
const hours = Math.floor(Math.random() * 24);
printToTerminal(`System uptime: ${days} days, ${hours} hours`, "text-gray-400");
break;
case 'status':
printToTerminal("Checking systems...", "text-yellow-400");
setTimeout(() => {
printToTerminal("[✓] Firewall: ACTIVE", "text-green-400");
printToTerminal("[✓] IDS/IPS: ONLINE", "text-green-400");
printToTerminal("[✓] SIEM: CONNECTED", "text-green-400");
printToTerminal("[✓] Threat Intel: FEED ACTIVE", "text-green-400");
printToTerminal("[!] Backup: SYNCING...", "text-orange-400");
}, 800);
break;
default:
printToTerminal(`Command not found: ${cmd}. Type 'help' for available commands.`, "text-red-400");
}
}
// Dark Web Monitor Functions
function investigateLeak(leakType) {
if (!leakType) return;
addLogEntry(`Investigation lancée: ${leakType.toUpperCase()}`, 'info', 'DARKWEB');
showToastAlert({
type: 'DARKWEB_INVESTIGATION',
severity: 'HIGH',
description: `Analyse forensique de la fuite ${leakType} en cours...`,
timestamp: new Date().toISOString()
});
}
// Packet Capture Simulation
let packetCaptureActive = false;
function startPacketCapture() {
const container = document.getElementById('packet-analyzer');
packetCaptureActive = !packetCaptureActive;
if (packetCaptureActive) {
addLogEntry('Capture de paquets démarrée sur interface eth0', 'info', 'NETWORK');
container.innerHTML = '<div class="text-green-400">Capture active...</div>';
const protocols = ['TCP', 'UDP', 'ICMP', 'DNS'];
const flags = ['SYN', 'ACK', 'PSH', 'FIN', 'RST'];
const interval = setInterval(() => {
if (!packetCaptureActive) {
clearInterval(interval);
return;
}
const proto = protocols[Math.floor(Math.random() * protocols.length)];
const src = `192.168.1.${Math.floor(Math.random() * 255)}`;
const dst = Math.random() > 0.8 ? `185.220.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}` : `10.0.0.${Math.floor(Math.random() * 50)}`;
const port = [4444, 5555, 80, 443, 22, 3389][Math.floor(Math.random() * 6)];
const flag = flags[Math.floor(Math.random() * flags.length)];
const isSuspicious = dst.startsWith('185.220') || port === 4444;
const colorClass = isSuspicious ? 'text-red-400' : 'text-gray-400';
const alert = isSuspicious ? ' [!SUSPICIOUS]' : '';
const line = document.createElement('div');
line.className = `font-mono text-xs ${colorClass} border-l-2 ${isSuspicious ? 'border-red-500 bg-red-900/10' : 'border-gray-700'} pl-2 mb-1`;
line.innerHTML = `[${new Date().toLocaleTimeString()}] ${proto} ${src} -> ${dst}:${port} [${flag}]${alert}`;
container.prepend(line);
if (container.children.length > 50) container.lastChild.remove();
}, 200);
} else {
addLogEntry('Capture de paquets arrêtée', 'info', 'NETWORK');
container.innerHTML = '<div class="text-gray-500">Capture arrêtée. En attente...</div>';
}
}
// Emergency Lockdown
function emergencyLockdown() {
if (!confirm('ÊTES-VOUS SÛR ? Cette action coupera TOUTES les connexions réseau et isolera le système.')) return;
addLogEntry('🔒 CONFINEMENT D\'URGENCE ACTIVÉ PAR ADMINISTRATEUR', 'error', 'EMERGENCY');
// Update all network status to offline
document.getElementById('status-eth').innerText = 'COUPÉ';
document.getElementById('status-eth').className = 'text-red-500 font-mono animate-pulse';
document.getElementById('icon-eth').classList.add('text-red-500');
document.getElementById('status-wifi').innerText = 'COUPÉ';
document.getElementById('status-wifi').className = 'text-red-500 font-mono animate-pulse';
document.getElementById('icon-wifi').classList.add('text-red-500');
// Show emergency modal
const modal = document.createElement('div');
modal.className = 'fixed inset-0 bg-red-900/95 z-[200] flex items-center justify-center';
modal.innerHTML = `
<div class="text-center">
<i data-feather="shield-off" class="w-24 h-24 text-white mx-auto mb-4 animate-pulse"></i>
<h2 class="text-4xl font-bold text-white mb-4">SYSTÈME EN CONFINEMENT</h2>
<p class="text-red-200 text-lg mb-8">Toutes les interfaces réseau ont été désactivées.</p>
<button onclick="this.closest('.fixed').remove()" class="bg-white text-red-900 px-8 py-3 rounded font-bold text-lg hover:bg-gray-200 transition">
Acquitter
</button>
</div>
`;
document.body.appendChild(modal);
feather.replace();
playAlertSound();
}
// Memory Analysis Simulation
function startMemoryAnalysis() {
addLogEntry('Analyse mémoire RAM démarrée...', 'info', 'FORENSICS');
showToastAlert({
type: 'FORENSICS_MEMORY',
severity: 'INFO',
description: 'Dump mémoire en cours - Recherche d\'artefacts malveillants',
timestamp: new Date().toISOString()
});
setTimeout(() => {
addLogEntry('Artefact détecté: Processus injecté dans lsass.exe', 'error', 'FORENSICS');
}, 3000);
}
// Export Forensic Report
function exportForensicReport() {
const report = {
case_id: 'FR-2025-0129-FICOBa',
date: new Date().toISOString(),
type: 'Breach Investigation',
iocs: ['185.220.101.42', 'ms-office-update.org', 'd41d8cd98f00b204e9800998ecf8427e'],
timeline: '28-29 Jan 2025',
analyst: 'CyberShield SOC'
};
const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `forensic_report_${report.case_id}.json`;
a.click();
addLogEntry(`Rapport forensique exporté: ${report.case_id}`, 'success', 'FORENSICS');
}
// Add to Blocklist
function addToBlocklist() {
const iocs = ['185.220.101.42', 'ms-office-update.org'];
iocs.forEach(ioc => {
if (ioc.includes('.')) {
addLogEntry(`IoC ajouté aux listes de blocage: ${ioc}`, 'success', 'THREAT_INTEL');
}
});
showToastAlert({
type: 'IOC_BLOCKLIST',
severity: 'SUCCESS',
description: '3 indicateurs ajoutés aux listes de blocage',
timestamp: new Date().toISOString()
});
}
// Data Exfiltration Visualization
function initExfilChart() {
const canvas = document.getElementById('dataFlowCanvas');
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
// S'assurer que le canvas a une taille valide
canvas.width = canvas.offsetWidth || 400;
canvas.height = canvas.offsetHeight || 200;
let dataPoints = [];
const maxPoints = 50;
const normalEl = document.getElementById('normal-traffic');
const suspiciousEl = document.getElementById('suspicious-outbound');
const encryptedEl = document.getElementById('encrypted-ratio');
setInterval(() => {
// Simulate data flow
const normal = Math.random() * 2 + 1; // 1-3 MB/s normal
const suspicious = Math.random() > 0.7 ? Math.random() * 20 + 10 : 0; // Spikes
if (normalEl) normalEl.innerText = normal.toFixed(1) + ' MB/s';
if (suspiciousEl) suspiciousEl.innerText = suspicious > 0 ? suspicious.toFixed(1) + ' MB/s' : '0 MB/s';
if (encryptedEl) encryptedEl.innerText = Math.floor(Math.random() * 15 + 80) + '%';
dataPoints.push({ normal, suspicious });
if (dataPoints.length > maxPoints) dataPoints.shift();
// Clear canvas
ctx.fillStyle = '#0a0a12';
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (dataPoints.length < 2) return;
const step = canvas.width / maxPoints;
// Draw normal traffic
ctx.beginPath();
ctx.strokeStyle = '#00ff9d';
ctx.lineWidth = 2;
dataPoints.forEach((p, i) => {
const y = canvas.height - (p.normal / 25 * canvas.height);
if (i === 0) ctx.moveTo(0, y);
else ctx.lineTo(i * step, y);
});
ctx.stroke();
// Draw suspicious spikes
ctx.beginPath();
ctx.strokeStyle = '#ef4444';
ctx.lineWidth = 2;
dataPoints.forEach((p, i) => {
if (p.suspicious > 0) {
const y = canvas.height - (p.suspicious / 25 * canvas.height);
ctx.moveTo(i * step, canvas.height);
ctx.lineTo(i * step, y);
}
});
ctx.stroke();
}, 1000);
}
// Initialize exfil chart when view loads
document.addEventListener('DOMContentLoaded', () => {
setTimeout(initExfilChart, 1000);
});
// 5. Vulnerabilities Logic avec actions réelles
async function startVulnStream() {
const tableBody = document.getElementById('vuln-table-body');
const fetchVulns = async () => {
try {
const response = await fetch('http://localhost:3000/api/vulns');
if (!response.ok) throw new Error('Server unavailable');
const data = await response.json();
// Update Counts avec animation couleur
const critEl = document.getElementById('vuln-critical-count');
critEl.innerText = data.stats.critical;
if (data.stats.critical > 0) critEl.classList.add('text-red-500', 'animate-pulse');
document.getElementById('vuln-high-count').innerText = data.stats.high;
document.getElementById('vuln-med-count').innerText = data.stats.medium;
// Render List
tableBody.innerHTML = '';
data.list.forEach(vuln => {
let color = 'text-blue-400';
let badgeClass = 'bg-blue-500/20 text-blue-400 border-blue-500/30';
if(vuln.score >= 9) {
color = 'text-red-400 font-bold';
badgeClass = 'bg-red-500/20 text-red-400 border-red-500/30 animate-pulse';
} else if(vuln.score >= 7) {
color = 'text-orange-400';
badgeClass = 'bg-orange-500/20 text-orange-400 border-orange-500/30';
}
const row = document.createElement('tr');
row.className = 'hover:bg-gray-800/30 transition border-b border-gray-800/50';
row.innerHTML = `
<td class="px-6 py-3 font-mono text-white">${vuln.id}</td>
<td class="px-6 py-3">${vuln.component}</td>
<td class="px-6 py-3 ${color}">${vuln.score}</td>
<td class="px-6 py-3">
<span class="px-2 py-1 rounded text-xs border ${badgeClass}">${vuln.status}</span>
</td>
<td class="px-6 py-3">
<button onclick="applyPatch('${vuln.id}')" class="text-cyber-primary hover:text-white hover:bg-cyber-primary/20 px-2 py-1 rounded transition text-xs border border-cyber-primary/30">
PATCH
</button>
</td>
`;
tableBody.appendChild(row);
});
feather.replace();
} catch(e) {
console.error('Erreur vulnérabilités:', e);
}
};
fetchVulns();
setInterval(fetchVulns, 10000); // Toutes les 10s
}
// Fonction pour simuler/appliquer un patch
async function applyPatch(cveId) {
if (!confirm(`Confirmer l'application du patch pour ${cveId} ?`)) return;
addLogEntry(`Application du patch ${cveId} en cours...`, 'info', 'ADMIN');
try {
// Ici: appel API réel pour déclencher patch management
const response = await fetch('http://localhost:3000/api/patch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ cve: cveId })
});
if (response.ok) {
addLogEntry(`Patch ${cveId} appliqué avec succès`, 'success', 'SYSTEM');
playBeep(800, 'sine', 0.2); // Son de succès
}
} catch (e) {
addLogEntry(`Échec application patch ${cveId}`, 'error', 'SYSTEM');
}
}
// --- COGNITIVE ENGINE LOGIC ---
function initCognitiveEngine() {
const chartContainer = document.getElementById('predictionChart');
const barCount = 30;
for(let i = 0; i < barCount; i++) {
const bar = document.createElement('div');
bar.className = 'bg-purple-500/40 w-2 rounded-t-sm transition-all duration-500';
// Simulate a prediction curve
let h = 20 + (i * 2) + (Math.random() * 10 - 5);
if (h > 90) h = 90;
bar.style.height = `${h}%`;
// Random anomaly
if (Math.random() > 0.95) {
bar.classList.add('bg-red-500', 'shadow-[0_0_10px_rgba(239,68,68,0.8)]');
}
chartContainer.appendChild(bar);
}
}
function startCognitiveStream() {
const container = document.getElementById('cognitive-log');
const insights = [
{ type: 'WARNING', text: 'Anomalie de trafic détectée: Protocol SSH handshake failed.', score: '92%' },
{ type: 'INFO', text: 'Mise à jour des signatures de pare-feu terminée.', score: '100%' },
{ type: 'ANALYSIS', text: 'Pattern reconnu: Attaque DDoS de faible niveau (Type UDP Flood).', score: '88%' },
{ type: 'OPTIMIZATION', text: 'Optimisation dynamique: Cache Redis activé sur Cluster A.', score: 'N/A' },
{ type: 'DETECTION', text: 'Compte utilisateur "admin" connecté depuis une localisation inhabituelle.', score: '45%' }
];
setInterval(() => {
// Randomly pick an insight
const item = insights[Math.floor(Math.random() * insights.length)];
const div = document.createElement('div');
div.className = "border-b border-gray-800 pb-2 animate-fade-in";
let color = "text-gray-400";
if(item.type === 'WARNING') color = "text-red-400";
if(item.type === 'OPTIMIZATION') color = "text-green-400";
div.innerHTML = `
<div class="flex justify-between">
<span class="${color} font-bold">[${item.type}]</span>
<span class="text-gray-600 font-mono">${item.score}</span>
</div>
<p class="text-gray-300 mt-1">${item.text}</p>
`;
container.prepend(div);
if(container.children.length > 10) container.lastChild.remove();
}, 5000);
}
function generateCognitiveInsight() {
const container = document.getElementById('cognitive-log');
const div = document.createElement('div');
div.className = "border-l-2 border-purple-500 pl-3 py-1 bg-purple-900/10 animate-pulse";
div.innerHTML = `
<span class="text-purple-400 text-xs font-bold uppercase">Re-Analyse Terminée</span>
<p class="text-white text-sm mt-1">Aucun risque critique identifié. Efficacité du système: 99.8%</p>
`;
container.prepend(div);
}
// 6. Infrastructure Logic avec données réelles système
async function startInfraStream() {
const container = document.getElementById('server-rack-container');
const fetchInfra = async () => {
try {
const response = await fetch('http://localhost:3000/api/infra');
if (!response.ok) throw new Error('Network response was not ok');
const data = await response.json();
if(container.children.length === 0) {
data.servers.forEach(srv => {
const card = document.createElement('div');
card.className = 'server-card p-4 rounded-xl shadow-lg relative';
card.id = `srv-${srv.id}`;
container.appendChild(card);
});
}
data.servers.forEach(srv => {
const card = document.getElementById(`srv-${srv.id}`);
if(card) {
const loadColor = srv.load > 80 ? 'bg-red-500 shadow-[0_0_10px_rgba(239,68,68,0.5)]' :
(srv.load > 50 ? 'bg-yellow-400' : 'bg-cyber-secondary');
const tempColor = srv.temp > 70 ? 'text-red-400 animate-pulse' :
(srv.temp > 60 ? 'text-yellow-400' : 'text-white');
// Badge de statut dynamique
const statusBadge = srv.status === 'Online'
? '<span class="flex items-center text-green-400"><span class="w-1.5 h-1.5 bg-green-400 rounded-full mr-1 animate-pulse"></span>Online</span>'
: '<span class="flex items-center text-red-400"><span class="w-1.5 h-1.5 bg-red-500 rounded-full mr-1"></span>Warning</span>';
card.innerHTML = `
<div class="flex justify-between items-center mb-4">
<div class="flex items-center">
<i data-feather="server" class="w-4 h-4 text-gray-400 mr-2"></i>
<h4 class="font-bold text-white text-sm">${srv.name}</h4>
</div>
<span class="text-xs">${statusBadge}</span>
</div>
<div class="space-y-3">
<div>
<div class="flex justify-between text-xs text-gray-400 mb-1">
<span>CPU Load</span>
<span class="font-mono ${srv.load > 80 ? 'text-red-400' : ''}">${srv.load}%</span>
</div>
<div class="w-full bg-gray-800 rounded-full h-1.5 overflow-hidden">
<div class="${loadColor} h-1.5 rounded-full transition-all duration-700" style="width: ${Math.min(srv.load, 100)}%"></div>
</div>
</div>
<div class="grid grid-cols-3 gap-2 mt-4">
<div class="bg-gray-900/50 p-2 rounded border border-gray-800">
<p class="text-[10px] text-gray-500 uppercase">RAM</p>
<p class="text-xs font-mono text-white">${srv.ram} GB</p>
</div>
<div class="bg-gray-900/50 p-2 rounded border border-gray-800">
<p class="text-[10px] text-gray-500 uppercase">Temp</p>
<p class="text-xs font-mono ${tempColor}">${srv.temp}°C</p>
</div>
<div class="bg-gray-900/50 p-2 rounded border border-gray-800">
<p class="text-[10px] text-gray-500 uppercase">Proc</p>
<p class="text-xs font-mono text-white">${srv.processes}</p>
</div>
</div>
${srv.load > 80 ? '<div class="mt-2 text-[10px] text-red-400 border border-red-500/30 bg-red-500/10 px-2 py-1 rounded text-center">SURCHARGE CPU DETECTÉE</div>' : ''}
</div>
`;
}
});
feather.replace();
} catch(e) {
console.error('Erreur infrastructure:', e);
}
};
fetchInfra();
setInterval(fetchInfra, 3000); // Rafraîchissement toutes les 3s
}