454 lines
16 KiB
PHP
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 " ";
|
|
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 " ";
|
|
echo "<input type='submit' name='mark_ignored' value='" . __('Mark as Ignored', 'siem-wazuh') . "' class='submit'>";
|
|
echo " ";
|
|
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>"; |