'common', 'name' => __('Characteristics') ]; $tab[] = [ 'id' => '1', 'table' => $this->getTable(), 'field' => 'alert_id', 'name' => __('Alert ID', 'siem-wazuh'), 'datatype' => 'itemlink', 'massiveaction' => false ]; $tab[] = [ 'id' => '2', 'table' => 'glpi_plugin_siem_wazuh_servers', 'field' => 'name', 'name' => __('Wazuh Server', 'siem-wazuh'), 'datatype' => 'dropdown', 'massiveaction' => false, 'linkfield' => 'wazuh_server_id' ]; $tab[] = [ 'id' => '3', 'table' => $this->getTable(), 'field' => 'rule_id', 'name' => __('Rule ID', 'siem-wazuh'), 'datatype' => 'integer', 'massiveaction' => false ]; $tab[] = [ 'id' => '4', 'table' => $this->getTable(), 'field' => 'rule_level', 'name' => __('Rule Level', 'siem-wazuh'), 'datatype' => 'integer', 'massiveaction' => false ]; $tab[] = [ 'id' => '5', 'table' => $this->getTable(), 'field' => 'rule_description', 'name' => __('Rule Description', 'siem-wazuh'), 'datatype' => 'text', 'massiveaction' => false ]; $tab[] = [ 'id' => '6', 'table' => $this->getTable(), 'field' => 'agent_name', 'name' => __('Agent Name', 'siem-wazuh'), 'datatype' => 'string', 'massiveaction' => false ]; $tab[] = [ 'id' => '7', 'table' => $this->getTable(), 'field' => 'agent_ip', 'name' => __('Agent IP', 'siem-wazuh'), 'datatype' => 'string', 'massiveaction' => false ]; $tab[] = [ 'id' => '8', 'table' => $this->getTable(), 'field' => 'timestamp', 'name' => __('Alert Timestamp', 'siem-wazuh'), 'datatype' => 'datetime', 'massiveaction' => false ]; $tab[] = [ 'id' => '9', 'table' => $this->getTable(), 'field' => 'status', 'name' => __('Status'), 'datatype' => 'specific', 'searchtype' => 'equals', 'massiveaction' => true ]; $tab[] = [ 'id' => '10', 'table' => $this->getTable(), 'field' => 'severity', 'name' => __('Severity', 'siem-wazuh'), 'datatype' => 'specific', 'searchtype' => 'equals', 'massiveaction' => true ]; $tab[] = [ 'id' => '11', 'table' => 'glpi_computers', 'field' => 'name', 'name' => __('Associated Computer', 'siem-wazuh'), 'datatype' => 'dropdown', 'massiveaction' => false, 'linkfield' => 'computer_id' ]; $tab[] = [ 'id' => '12', 'table' => 'glpi_networkequipments', 'field' => 'name', 'name' => __('Associated Network Equipment', 'siem-wazuh'), 'datatype' => 'dropdown', 'massiveaction' => false, 'linkfield' => 'networkequipment_id' ]; $tab[] = [ 'id' => '13', 'table' => 'glpi_tickets', 'field' => 'name', 'name' => __('Associated Ticket', 'siem-wazuh'), 'datatype' => 'dropdown', 'massiveaction' => false, 'linkfield' => 'ticket_id' ]; $tab[] = [ 'id' => '30', 'table' => $this->getTable(), 'field' => 'date_creation', 'name' => __('Creation date'), 'datatype' => 'datetime', 'massiveaction' => false ]; $tab[] = [ 'id' => '31', 'table' => $this->getTable(), 'field' => 'date_mod', 'name' => __('Last update'), 'datatype' => 'datetime', 'massiveaction' => false ]; return $tab; } /** * Display specific values */ static function getSpecificValueToDisplay($field, $values, array $options = []) { if (!is_array($values)) { $values = [$field => $values]; } switch ($field) { case 'status': $statuses = self::getStatusArray(); return $statuses[$values[$field]] ?? $values[$field]; case 'severity': $severities = self::getSeverityArray(); return $severities[$values[$field]] ?? $values[$field]; } return parent::getSpecificValueToDisplay($field, $values, $options); } /** * Get status array */ static function getStatusArray() { return [ 'new' => __('New', 'siem-wazuh'), 'processed' => __('Processed', 'siem-wazuh'), 'ignored' => __('Ignored', 'siem-wazuh'), 'ticket_created' => __('Ticket Created', 'siem-wazuh') ]; } /** * Get severity array */ static function getSeverityArray() { return [ 'low' => __('Low', 'siem-wazuh'), 'medium' => __('Medium', 'siem-wazuh'), 'high' => __('High', 'siem-wazuh'), 'critical' => __('Critical', 'siem-wazuh') ]; } /** * Display form */ function showForm($ID, $options = []) { $this->initForm($ID, $options); $this->showFormHeader($options); echo ""; echo "".__('Alert ID', 'siem-wazuh').""; echo "".$this->fields["alert_id"].""; echo "".__('Rule ID', 'siem-wazuh').""; echo "".$this->fields["rule_id"].""; echo ""; echo ""; echo "".__('Rule Level', 'siem-wazuh').""; echo ""; echo $this->fields["rule_level"]; if ($this->fields["rule_level"]) { $severity = self::getSeverityFromLevel($this->fields["rule_level"]); $severities = self::getSeverityArray(); echo " ".$severities[$severity].""; } echo ""; echo "".__('Agent Name', 'siem-wazuh').""; echo "".$this->fields["agent_name"].""; echo ""; echo ""; echo "".__('Agent IP', 'siem-wazuh').""; echo "".$this->fields["agent_ip"].""; echo "".__('Timestamp', 'siem-wazuh').""; echo "".Html::convDateTime($this->fields["timestamp"]).""; echo ""; echo ""; echo "".__('Status').""; echo ""; $statuses = self::getStatusArray(); Dropdown::showFromArray('status', $statuses, ['value' => $this->fields["status"]]); echo ""; echo "".__('Severity', 'siem-wazuh').""; echo ""; $severities = self::getSeverityArray(); Dropdown::showFromArray('severity', $severities, ['value' => $this->fields["severity"]]); echo ""; echo ""; echo ""; echo "".__('Rule Description', 'siem-wazuh').""; echo ""; echo nl2br(Html::entities_deep($this->fields["rule_description"])); echo ""; echo ""; // Affichage des associations if ($this->fields["computer_id"]) { $computer = new Computer(); $computer->getFromDB($this->fields["computer_id"]); echo ""; echo "".__('Associated Computer').""; echo ""; echo $computer->getLink(); echo ""; echo ""; echo ""; } if ($this->fields["networkequipment_id"]) { $netequip = new NetworkEquipment(); $netequip->getFromDB($this->fields["networkequipment_id"]); echo ""; echo "".__('Associated Network Equipment').""; echo ""; echo $netequip->getLink(); echo ""; echo ""; echo ""; } if ($this->fields["ticket_id"]) { $ticket = new Ticket(); $ticket->getFromDB($this->fields["ticket_id"]); echo ""; echo "".__('Associated Ticket').""; echo ""; echo $ticket->getLink(); echo ""; echo ""; echo ""; } // Affichage des données brutes if (!empty($this->fields["raw_data"])) { echo ""; echo ""; echo "
"; echo "".__('Raw Alert Data', 'siem-wazuh').""; echo "
";
            echo json_encode(json_decode($this->fields["raw_data"]), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
            echo "
"; echo "
"; echo ""; echo ""; } $this->showFormButtons($options); return true; } /** * Process alerts from Wazuh API */ function processAlerts($server_id, $alerts_data) { $processed = 0; $config = new PluginSiemWazuhConfig(); foreach ($alerts_data as $alert_data) { $alert_id = $alert_data['id'] ?? $alert_data['_id'] ?? null; if (!$alert_id) continue; // Vérifier si l'alerte existe déjà if ($this->getFromDBByCrit([ 'wazuh_server_id' => $server_id, 'alert_id' => $alert_id ])) { continue; // Alerte déjà traitée } // Extraire les informations de l'alerte $rule_id = $alert_data['rule']['id'] ?? null; $rule_level = $alert_data['rule']['level'] ?? 0; $rule_description = $alert_data['rule']['description'] ?? ''; $agent_name = $alert_data['agent']['name'] ?? ''; $agent_ip = $alert_data['agent']['ip'] ?? ''; $timestamp = $alert_data['timestamp'] ?? $alert_data['@timestamp'] ?? date('Y-m-d H:i:s'); // Convertir le timestamp au format MySQL if (strpos($timestamp, 'T') !== false) { $timestamp = str_replace('T', ' ', substr($timestamp, 0, 19)); } // Déterminer la sévérité $severity = self::getSeverityFromLevel($rule_level); // Créer l'alerte $alert_input = [ 'wazuh_server_id' => $server_id, 'alert_id' => $alert_id, 'rule_id' => $rule_id, 'rule_level' => $rule_level, 'rule_description' => $rule_description, 'agent_id' => $alert_data['agent']['id'] ?? '', 'agent_name' => $agent_name, 'agent_ip' => $agent_ip, 'timestamp' => $timestamp, 'raw_data' => json_encode($alert_data), 'severity' => $severity, 'status' => 'new' ]; // Tenter d'associer avec un asset $this->associateWithAsset($alert_input, $agent_name, $agent_ip); if ($alert_id = $this->add($alert_input)) { $processed++; // Créer automatiquement un ticket si configuré if ($config->getConfiguration('auto_create_ticket', 1)) { $this->createTicketFromAlert($alert_id); } } } return $processed; } /** * Associate alert with GLPI asset */ private function associateWithAsset(&$alert_input, $agent_name, $agent_ip) { global $DB; // Recherche par nom d'agent if (!empty($agent_name)) { // Recherche dans les ordinateurs $computer = $DB->request([ 'SELECT' => ['id'], 'FROM' => 'glpi_computers', 'WHERE' => [ 'name' => $agent_name, 'is_deleted' => 0 ], 'LIMIT' => 1 ])->current(); if ($computer) { $alert_input['computer_id'] = $computer['id']; return; } // Recherche dans les équipements réseau $netequip = $DB->request([ 'SELECT' => ['id'], 'FROM' => 'glpi_networkequipments', 'WHERE' => [ 'name' => $agent_name, 'is_deleted' => 0 ], 'LIMIT' => 1 ])->current(); if ($netequip) { $alert_input['networkequipment_id'] = $netequip['id']; return; } } // Recherche par adresse IP if (!empty($agent_ip)) { // Recherche dans les ports réseau $port = $DB->request([ 'SELECT' => ['items_id', 'itemtype'], 'FROM' => 'glpi_networkports', 'INNER JOIN' => [ 'glpi_ipaddresses' => [ 'ON' => [ 'glpi_networkports' => 'id', 'glpi_ipaddresses' => 'items_id' ] ] ], 'WHERE' => [ 'glpi_ipaddresses.name' => $agent_ip, 'glpi_ipaddresses.itemtype' => 'NetworkPort' ], 'LIMIT' => 1 ])->current(); if ($port) { if ($port['itemtype'] == 'Computer') { $alert_input['computer_id'] = $port['items_id']; } elseif ($port['itemtype'] == 'NetworkEquipment') { $alert_input['networkequipment_id'] = $port['items_id']; } } } } /** * Create ticket from alert */ function createTicketFromAlert($alert_id) { if (!$this->getFromDB($alert_id)) { return false; } $server = new PluginSiemWazuhServer(); if (!$server->getFromDB($this->fields['wazuh_server_id'])) { return false; } $ticket = new Ticket(); $config = new PluginSiemWazuhConfig(); // Déterminer la priorité basée sur le niveau de règle $priority = self::getPriorityFromLevel($this->fields['rule_level']); $ticket_input = [ 'name' => sprintf(__('Wazuh Alert: %s', 'siem-wazuh'), $this->fields['rule_description']), 'content' => $this->generateTicketContent(), 'type' => $server->fields['ticket_type'] ?: 1, 'itilcategories_id' => $server->fields['ticket_category'], 'priority' => $priority, 'urgency' => $priority, 'impact' => $priority, 'status' => CommonITILObject::INCOMING, '_add_validation' => 0 ]; // Associer le ticket à l'asset si trouvé if ($this->fields['computer_id']) { $ticket_input['_link']['items_id'] = $this->fields['computer_id']; $ticket_input['_link']['itemtype'] = 'Computer'; } elseif ($this->fields['networkequipment_id']) { $ticket_input['_link']['items_id'] = $this->fields['networkequipment_id']; $ticket_input['_link']['itemtype'] = 'NetworkEquipment'; } $ticket_id = $ticket->add($ticket_input); if ($ticket_id) { // Mettre à jour l'alerte $this->update([ 'id' => $alert_id, 'ticket_id' => $ticket_id, 'status' => 'ticket_created' ]); return $ticket_id; } return false; } /** * Generate ticket content from alert */ private function generateTicketContent() { $content = "[WAZUH-ALERT]\n\n"; $content .= sprintf(__('Alert ID: %s', 'siem-wazuh'), $this->fields['alert_id']) . "\n"; $content .= sprintf(__('Rule ID: %s', 'siem-wazuh'), $this->fields['rule_id']) . "\n"; $content .= sprintf(__('Rule Level: %s', 'siem-wazuh'), $this->fields['rule_level']) . "\n"; $content .= sprintf(__('Agent: %s (%s)', 'siem-wazuh'), $this->fields['agent_name'], $this->fields['agent_ip']) . "\n"; $content .= sprintf(__('Timestamp: %s', 'siem-wazuh'), $this->fields['timestamp']) . "\n\n"; $content .= sprintf(__('Description: %s', 'siem-wazuh'), $this->fields['rule_description']) . "\n\n"; if ($this->fields['raw_data']) { $content .= __('Raw Alert Data:', 'siem-wazuh') . "\n"; $content .= "```json\n"; $content .= json_encode(json_decode($this->fields['raw_data']), JSON_PRETTY_PRINT); $content .= "\n```"; } return $content; } /** * Get severity from rule level */ static function getSeverityFromLevel($level) { if ($level >= 12) return 'critical'; if ($level >= 10) return 'high'; if ($level >= 7) return 'medium'; return 'low'; } /** * Get priority from rule level */ static function getPriorityFromLevel($level) { if ($level >= 12) return 5; // Very high if ($level >= 10) return 4; // High if ($level >= 7) return 3; // Medium if ($level >= 5) return 2; // Low return 1; // Very low } /** * Show alerts for a server */ static function showForServer($server_id) { global $DB; $iterator = $DB->request([ 'FROM' => 'glpi_plugin_siem_wazuh_alerts', 'WHERE' => ['wazuh_server_id' => $server_id], 'ORDER' => 'timestamp DESC', 'LIMIT' => 50 ]); echo "
"; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; $statuses = self::getStatusArray(); foreach ($iterator as $row) { echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; } echo "
".__('Alert ID', 'siem-wazuh')."".__('Rule', 'siem-wazuh')."".__('Level', 'siem-wazuh')."".__('Agent', 'siem-wazuh')."".__('Timestamp', 'siem-wazuh')."".__('Status')."".__('Ticket')."
".$row['alert_id']."".$row['rule_id']." - ".Html::clean($row['rule_description'])."".$row['rule_level']."".$row['agent_name']." (".$row['agent_ip'].")".Html::convDateTime($row['timestamp'])."".$statuses[$row['status']].""; if ($row['ticket_id']) { $ticket = new Ticket(); if ($ticket->getFromDB($row['ticket_id'])) { echo $ticket->getLink(); } } echo "
"; echo "
"; } /** * Cron function to sync alerts */ static function cronSyncAlerts($task = null) { global $DB; $config = new PluginSiemWazuhConfig(); if (!$config->getConfiguration('sync_enabled', 1)) { return 0; // Synchronisation désactivée } $iterator = $DB->request([ 'FROM' => 'glpi_plugin_siem_wazuh_servers', 'WHERE' => ['is_active' => 1] ]); $total_processed = 0; foreach ($iterator as $server_data) { $server = new PluginSiemWazuhServer(); $server->fields = $server_data; $result = $server->syncAlerts(); if ($result['success']) { $matches = []; if (preg_match('/(\d+)/', $result['message'], $matches)) { $total_processed += (int)$matches[1]; } } } if ($task) { $task->addVolume($total_processed); return 1; } return $total_processed; } /** * Cron function to cleanup old alerts */ static function cronCleanupOldAlerts($task = null) { global $DB; $config = new PluginSiemWazuhConfig(); $retention_days = $config->getConfiguration('alert_retention_days', 90); $query = "DELETE FROM glpi_plugin_siem_wazuh_alerts WHERE date_creation < DATE_SUB(NOW(), INTERVAL $retention_days DAY)"; $result = $DB->query($query); $deleted = $DB->affectedRows(); if ($task) { $task->addVolume($deleted); return 1; } return $deleted; } /** * Get menu content */ static function getMenuContent() { $menu = []; if (Session::haveRight(static::$rightname, READ)) { $menu['title'] = self::getMenuName(); $menu['page'] = "/plugins/siem-wazuh/front/wazuhalert.php"; $menu['links']['search'] = "/plugins/siem-wazuh/front/wazuhalert.php"; } return $menu; } /** * Get menu name */ static function getMenuName() { return self::getTypeName(Session::getPluralNumber()); } }