Files
SIEM---Wazuh/inc/wazuhtab.class.php
2025-08-27 21:17:28 +02:00

454 lines
16 KiB
PHP

<?php
/*
* Plugin SIEM-Wazuh pour GLPI
* Classe de gestion des onglets Wazuh sur les assets
*/
if (!defined('GLPI_ROOT')) {
die("Sorry. You can't access this file directly");
}
class PluginSiemWazuhTab extends CommonGLPI {
static $rightname = 'plugin_siem_wazuh_alert';
/**
* Get tab name for item
*/
function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) {
if (!$withtemplate && Session::haveRight(static::$rightname, READ)) {
if (in_array($item->getType(), ['Computer', 'NetworkEquipment', 'Peripheral', 'Phone', 'Printer'])) {
$nb = 0;
if ($_SESSION['glpishow_count_on_tabs']) {
$nb = self::countForItem($item);
}
return self::createTabEntry(__('Wazuh Alerts', 'siem-wazuh'), $nb);
}
}
return '';
}
/**
* Display tab content
*/
static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) {
if (in_array($item->getType(), ['Computer', 'NetworkEquipment', 'Peripheral', 'Phone', 'Printer'])) {
self::showForItem($item);
}
return true;
}
/**
* Count alerts for item
*/
static function countForItem(CommonGLPI $item) {
global $DB;
$itemtype = $item->getType();
$items_id = $item->getID();
$field_map = [
'Computer' => 'computer_id',
'NetworkEquipment' => 'networkequipment_id',
'Peripheral' => 'peripheral_id',
'Phone' => 'phone_id',
'Printer' => 'printer_id'
];
if (!isset($field_map[$itemtype])) {
return 0;
}
return countElementsInTable('glpi_plugin_siem_wazuh_alerts', [
$field_map[$itemtype] => $items_id
]);
}
/**
* Show alerts for item
*/
static function showForItem(CommonGLPI $item) {
global $DB, $CFG_GLPI;
$itemtype = $item->getType();
$items_id = $item->getID();
$field_map = [
'Computer' => 'computer_id',
'NetworkEquipment' => 'networkequipment_id',
'Peripheral' => 'peripheral_id',
'Phone' => 'phone_id',
'Printer' => 'printer_id'
];
if (!isset($field_map[$itemtype])) {
return;
}
$field = $field_map[$itemtype];
$canupdate = Session::haveRight(static::$rightname, UPDATE);
// Récupération des alertes
$iterator = $DB->request([
'SELECT' => [
'glpi_plugin_siem_wazuh_alerts.*',
'glpi_plugin_siem_wazuh_servers.name AS server_name'
],
'FROM' => 'glpi_plugin_siem_wazuh_alerts',
'LEFT JOIN' => [
'glpi_plugin_siem_wazuh_servers' => [
'ON' => [
'glpi_plugin_siem_wazuh_alerts' => 'wazuh_server_id',
'glpi_plugin_siem_wazuh_servers' => 'id'
]
]
],
'WHERE' => [$field => $items_id],
'ORDER' => ['timestamp DESC', 'rule_level DESC'],
'LIMIT' => 100
]);
$alerts = iterator_to_array($iterator);
echo "<div class='spaced'>";
// Résumé des alertes
self::showAlertsSummary($alerts);
if (count($alerts) > 0) {
// Filtres
self::showAlertsFilters($item, $field);
// Tableau des alertes
self::showAlertsTable($alerts, $canupdate);
// Actions de masse
if ($canupdate && count($alerts) > 0) {
self::showMassActions($item, $field);
}
} else {
echo "<div class='center'>";
echo "<p>" . __('No Wazuh alerts found for this item', 'siem-wazuh') . "</p>";
echo "</div>";
}
echo "</div>";
}
/**
* Show alerts summary
*/
static function showAlertsSummary($alerts) {
$stats = [
'total' => count($alerts),
'new' => 0,
'processed' => 0,
'ticket_created' => 0,
'ignored' => 0,
'critical' => 0,
'high' => 0,
'medium' => 0,
'low' => 0
];
foreach ($alerts as $alert) {
$stats[$alert['status']]++;
$stats[$alert['severity']]++;
}
echo "<div class='alert-summary'>";
echo "<table class='tab_cadre_fixe'>";
echo "<tr class='tab_bg_2'>";
echo "<th colspan='8'>" . __('Alerts Summary', 'siem-wazuh') . "</th>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
echo "<td class='center'><strong>" . $stats['total'] . "</strong><br>" . __('Total', 'siem-wazuh') . "</td>";
echo "<td class='center status-new'><strong>" . $stats['new'] . "</strong><br>" . __('New', 'siem-wazuh') . "</td>";
echo "<td class='center status-processed'><strong>" . $stats['processed'] . "</strong><br>" . __('Processed', 'siem-wazuh') . "</td>";
echo "<td class='center status-ticket'><strong>" . $stats['ticket_created'] . "</strong><br>" . __('Ticket Created', 'siem-wazuh') . "</td>";
echo "<td class='center severity-critical'><strong>" . $stats['critical'] . "</strong><br>" . __('Critical', 'siem-wazuh') . "</td>";
echo "<td class='center severity-high'><strong>" . $stats['high'] . "</strong><br>" . __('High', 'siem-wazuh') . "</td>";
echo "<td class='center severity-medium'><strong>" . $stats['medium'] . "</strong><br>" . __('Medium', 'siem-wazuh') . "</td>";
echo "<td class='center severity-low'><strong>" . $stats['low'] . "</strong><br>" . __('Low', 'siem-wazuh') . "</td>";
echo "</tr>";
echo "</table>";
echo "</div>";
echo "<br>";
}
/**
* Show alerts filters
*/
static function showAlertsFilters($item, $field) {
echo "<div class='alert-filters'>";
echo "<form method='get' action=''>";
echo "<input type='hidden' name='id' value='" . $item->getID() . "'>";
echo "<input type='hidden' name='_glpi_tab' value='PluginSiemWazuhTab$1'>";
echo "<table class='tab_cadre_fixe'>";
echo "<tr class='tab_bg_2'>";
echo "<th colspan='4'>" . __('Filters', 'siem-wazuh') . "</th>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
echo "<td>" . __('Status') . "</td>";
echo "<td>";
$statuses = ['all' => __('All')] + PluginSiemWazuhAlert::getStatusArray();
Dropdown::showFromArray('filter_status', $statuses, [
'value' => $_GET['filter_status'] ?? 'all'
]);
echo "</td>";
echo "<td>" . __('Severity', 'siem-wazuh') . "</td>";
echo "<td>";
$severities = ['all' => __('All')] + PluginSiemWazuhAlert::getSeverityArray();
Dropdown::showFromArray('filter_severity', $severities, [
'value' => $_GET['filter_severity'] ?? 'all'
]);
echo "</td>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
echo "<td>" . __('Date from', 'siem-wazuh') . "</td>";
echo "<td>";
Html::showDateField('filter_date_from', [
'value' => $_GET['filter_date_from'] ?? ''
]);
echo "</td>";
echo "<td>" . __('Date to', 'siem-wazuh') . "</td>";
echo "<td>";
Html::showDateField('filter_date_to', [
'value' => $_GET['filter_date_to'] ?? ''
]);
echo "</td>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
echo "<td colspan='4' class='center'>";
echo "<input type='submit' value='" . _sx('button', 'Filter') . "' class='submit'>";
echo "&nbsp;";
echo "<input type='button' value='" . _sx('button', 'Reset') . "' class='submit' onclick='window.location.href=\"" . $item->getLinkURL() . "&_glpi_tab=PluginSiemWazuhTab$1\"'>";
echo "</td>";
echo "</tr>";
echo "</table>";
echo "</form>";
echo "</div>";
echo "<br>";
}
/**
* Show alerts table
*/
static function showAlertsTable($alerts, $canupdate) {
echo "<div class='alert-list'>";
echo "<form method='post' name='alerts_form' id='alerts_form' action='" . Plugin::getWebDir('siem-wazuh') . "/front/wazuhalert.form.php'>";
echo "<table class='tab_cadre_fixehov'>";
$header = "<tr class='tab_bg_2'>";
if ($canupdate) {
$header .= "<th width='10'>";
$header .= "<input type='checkbox' name='select_all' onclick='checkAll(this)'>";
$header .= "</th>";
}
$header .= "<th>" . __('Alert ID', 'siem-wazuh') . "</th>";
$header .= "<th>" . __('Server', 'siem-wazuh') . "</th>";
$header .= "<th>" . __('Rule', 'siem-wazuh') . "</th>";
$header .= "<th>" . __('Level', 'siem-wazuh') . "</th>";
$header .= "<th>" . __('Agent', 'siem-wazuh') . "</th>";
$header .= "<th>" . __('Timestamp', 'siem-wazuh') . "</th>";
$header .= "<th>" . __('Status') . "</th>";
$header .= "<th>" . __('Severity', 'siem-wazuh') . "</th>";
$header .= "<th>" . __('Ticket') . "</th>";
$header .= "</tr>";
echo $header;
if (count($alerts) == 0) {
echo "<tr class='tab_bg_1'>";
echo "<td colspan='" . ($canupdate ? "10" : "9") . "' class='center'>";
echo __('No alerts found', 'siem-wazuh');
echo "</td>";
echo "</tr>";
}
$statuses = PluginSiemWazuhAlert::getStatusArray();
$severities = PluginSiemWazuhAlert::getSeverityArray();
foreach ($alerts as $alert) {
echo "<tr class='tab_bg_1'>";
if ($canupdate) {
echo "<td>";
echo "<input type='checkbox' name='selected_alerts[]' value='" . $alert['id'] . "'>";
echo "</td>";
}
echo "<td>";
echo "<a href='" . PluginSiemWazuhAlert::getFormURLWithID($alert['id']) . "'>";
echo $alert['alert_id'];
echo "</a>";
echo "</td>";
echo "<td>" . $alert['server_name'] . "</td>";
echo "<td>";
echo "<strong>" . $alert['rule_id'] . "</strong><br>";
echo "<small>" . Html::clean($alert['rule_description']) . "</small>";
echo "</td>";
echo "<td class='center'>";
echo "<span class='rule-level-" . $alert['rule_level'] . "'>";
echo $alert['rule_level'];
echo "</span>";
echo "</td>";
echo "<td>";
echo $alert['agent_name'] . "<br>";
echo "<small>" . $alert['agent_ip'] . "</small>";
echo "</td>";
echo "<td>" . Html::convDateTime($alert['timestamp']) . "</td>";
echo "<td>";
echo "<span class='status-badge status-" . $alert['status'] . "'>";
echo $statuses[$alert['status']];
echo "</span>";
echo "</td>";
echo "<td>";
echo "<span class='severity-badge severity-" . $alert['severity'] . "'>";
echo $severities[$alert['severity']];
echo "</span>";
echo "</td>";
echo "<td>";
if ($alert['ticket_id']) {
$ticket = new Ticket();
if ($ticket->getFromDB($alert['ticket_id'])) {
echo $ticket->getLink();
}
} else {
echo "-";
}
echo "</td>";
echo "</tr>";
}
echo "</table>";
if ($canupdate && count($alerts) > 0) {
echo "<div class='center' style='margin-top: 10px;'>";
echo "<input type='submit' name='mark_processed' value='" . __('Mark as Processed', 'siem-wazuh') . "' class='submit'>";
echo "&nbsp;";
echo "<input type='submit' name='mark_ignored' value='" . __('Mark as Ignored', 'siem-wazuh') . "' class='submit'>";
echo "&nbsp;";
echo "<input type='submit' name='create_tickets' value='" . __('Create Tickets', 'siem-wazuh') . "' class='submit'>";
echo "</div>";
}
echo Html::closeForm();
echo "</div>";
}
/**
* Show mass actions
*/
static function showMassActions($item, $field) {
echo "<br>";
echo "<div class='mass-actions'>";
echo "<table class='tab_cadre_fixe'>";
echo "<tr class='tab_bg_2'>";
echo "<th colspan='2'>" . __('Mass Actions', 'siem-wazuh') . "</th>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
echo "<td>" . __('Sync new alerts', 'siem-wazuh') . "</td>";
echo "<td>";
echo "<button type='button' class='btn btn-primary' onclick='syncAlertsForItem(" . $item->getID() . ", \"" . $item->getType() . "\")'>";
echo __('Sync Now', 'siem-wazuh');
echo "</button>";
echo "</td>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
echo "<td>" . __('Export alerts', 'siem-wazuh') . "</td>";
echo "<td>";
echo "<a href='" . Plugin::getWebDir('siem-wazuh') . "/front/wazuhalert.php?export=1&" . $field . "=" . $item->getID() . "' class='btn'>";
echo __('Export CSV', 'siem-wazuh');
echo "</a>";
echo "</td>";
echo "</tr>";
echo "</table>";
echo "</div>";
}
/**
* Get search options for item
*/
static function getSearchOptionsForItem($itemtype) {
$tab = [];
$tab[5150]['table'] = 'glpi_plugin_siem_wazuh_alerts';
$tab[5150]['field'] = 'id';
$tab[5150]['name'] = __('Wazuh Alerts', 'siem-wazuh');
$tab[5150]['forcegroupby'] = true;
$tab[5150]['usehaving'] = true;
$tab[5150]['datatype'] = 'count';
$tab[5150]['massiveaction'] = false;
$field_map = [
'Computer' => 'computer_id',
'NetworkEquipment' => 'networkequipment_id',
'Peripheral' => 'peripheral_id',
'Phone' => 'phone_id',
'Printer' => 'printer_id'
];
if (isset($field_map[$itemtype])) {
$tab[5150]['linkfield'] = $field_map[$itemtype];
}
return $tab;
}
}
// JavaScript pour les fonctionnalités interactives
echo "<script type='text/javascript'>
function checkAll(checkbox) {
var checkboxes = document.querySelectorAll('input[name=\"selected_alerts[]\"]');
for (var i = 0; i < checkboxes.length; i++) {
checkboxes[i].checked = checkbox.checked;
}
}
function syncAlertsForItem(itemId, itemType) {
if (confirm('" . __('Are you sure you want to sync alerts for this item?', 'siem-wazuh') . "')) {
// AJAX call to sync alerts
fetch('" . Plugin::getWebDir('siem-wazuh') . "/ajax/sync_alerts.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'item_id=' + itemId + '&item_type=' + itemType
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('" . __('Sync completed successfully', 'siem-wazuh') . "');
location.reload();
} else {
alert('" . __('Sync failed:', 'siem-wazuh') . " ' + data.message);
}
})
.catch(error => {
alert('" . __('An error occurred during sync', 'siem-wazuh') . "');
});
}
}
</script>";