'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 "".__('Alert ID', 'siem-wazuh')." | ";
echo "".__('Rule', 'siem-wazuh')." | ";
echo "".__('Level', 'siem-wazuh')." | ";
echo "".__('Agent', 'siem-wazuh')." | ";
echo "".__('Timestamp', 'siem-wazuh')." | ";
echo "".__('Status')." | ";
echo "".__('Ticket')." | ";
echo "
";
$statuses = self::getStatusArray();
foreach ($iterator as $row) {
echo "";
echo "".$row['alert_id']." | ";
echo "".$row['rule_id']." - ".Html::clean($row['rule_description'])." | ";
echo "".$row['rule_level']." | ";
echo "".$row['agent_name']." (".$row['agent_ip'].") | ";
echo "".Html::convDateTime($row['timestamp'])." | ";
echo "".$statuses[$row['status']]." | ";
echo "";
if ($row['ticket_id']) {
$ticket = new Ticket();
if ($ticket->getFromDB($row['ticket_id'])) {
echo $ticket->getLink();
}
}
echo " | ";
echo "
";
}
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());
}
}