mirror of
https://github.com/tips-of-mine/GLPI-Plugin-CVE-Prototype.git
synced 2025-06-27 22:58:45 +02:00
414 lines
12 KiB
PHP
414 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* GLPI CVE Plugin - Software Vulnerability Alert Class
|
|
* Manages alerts for vulnerable software in inventory
|
|
*/
|
|
|
|
if (!defined('GLPI_ROOT')) {
|
|
die("Sorry. You can't access this file directly");
|
|
}
|
|
|
|
/**
|
|
* PluginCveCveAlert class for managing vulnerability alerts
|
|
*/
|
|
class PluginCveCveAlert extends CommonDBTM {
|
|
|
|
static $rightname = 'plugin_cve_alert';
|
|
|
|
/**
|
|
* Get name of this type by language of the user connected
|
|
*
|
|
* @param integer $nb number of elements
|
|
* @return string name of this type
|
|
*/
|
|
static function getTypeName($nb = 0) {
|
|
return _n('Software Vulnerability Alert', 'Software Vulnerability Alerts', $nb, 'cve');
|
|
}
|
|
|
|
/**
|
|
* Define tabs to display
|
|
*
|
|
* @param array $options
|
|
* @return array containing the tabs
|
|
*/
|
|
function defineTabs($options = []) {
|
|
$tabs = [];
|
|
$this->addDefaultFormTab($tabs);
|
|
$this->addStandardTab('Log', $tabs, $options);
|
|
|
|
return $tabs;
|
|
}
|
|
|
|
/**
|
|
* Display the CVE Alert form
|
|
*
|
|
* @param integer $ID ID of the item
|
|
* @param array $options
|
|
* @return boolean
|
|
*/
|
|
function showForm($ID, $options = []) {
|
|
global $CFG_GLPI;
|
|
|
|
$this->initForm($ID, $options);
|
|
$this->showFormHeader($options);
|
|
|
|
$canedit = $this->can($ID, UPDATE);
|
|
|
|
echo "<tr class='tab_bg_1'>";
|
|
|
|
// Software
|
|
echo "<td>" . __('Software', 'cve') . "</td>";
|
|
echo "<td>";
|
|
if ($this->fields['softwares_id'] > 0) {
|
|
$software = new Software();
|
|
if ($software->getFromDB($this->fields['softwares_id'])) {
|
|
echo $software->getLink();
|
|
} else {
|
|
echo __('Unknown software', 'cve');
|
|
}
|
|
} else {
|
|
echo __('N/A', 'cve');
|
|
}
|
|
echo "</td>";
|
|
|
|
// Version
|
|
echo "<td>" . __('Version', 'cve') . "</td>";
|
|
echo "<td>";
|
|
if ($this->fields['softwareversions_id'] > 0) {
|
|
$version = new SoftwareVersion();
|
|
if ($version->getFromDB($this->fields['softwareversions_id'])) {
|
|
echo $version->getName();
|
|
} else {
|
|
echo __('Unknown version', 'cve');
|
|
}
|
|
} else {
|
|
echo __('N/A', 'cve');
|
|
}
|
|
echo "</td>";
|
|
|
|
echo "</tr>";
|
|
|
|
echo "<tr class='tab_bg_1'>";
|
|
|
|
// CVE
|
|
echo "<td>" . __('CVE', 'cve') . "</td>";
|
|
echo "<td>";
|
|
if ($this->fields['cves_id'] > 0) {
|
|
$cve = new PluginCveCve();
|
|
if ($cve->getFromDB($this->fields['cves_id'])) {
|
|
echo "<a href='" . PluginCveCve::getFormURLWithID($this->fields['cves_id']) . "'>";
|
|
echo $cve->fields['cve_id'];
|
|
echo "</a>";
|
|
} else {
|
|
echo __('Unknown CVE', 'cve');
|
|
}
|
|
} else {
|
|
echo __('N/A', 'cve');
|
|
}
|
|
echo "</td>";
|
|
|
|
// Severity
|
|
echo "<td>" . __('Severity', 'cve') . "</td>";
|
|
echo "<td>";
|
|
echo "<span class='" . PluginCveCve::getSeverityClass($this->fields['severity']) . "'>";
|
|
echo $this->fields['severity'];
|
|
echo "</span>";
|
|
echo "</td>";
|
|
|
|
echo "</tr>";
|
|
|
|
echo "<tr class='tab_bg_1'>";
|
|
|
|
// Status
|
|
echo "<td>" . __('Status', 'cve') . "</td>";
|
|
echo "<td>";
|
|
if ($canedit) {
|
|
$status_options = [
|
|
'NEW' => __('New', 'cve'),
|
|
'PROCESSED' => __('Processed', 'cve'),
|
|
'IGNORED' => __('Ignored', 'cve')
|
|
];
|
|
Dropdown::showFromArray('status', $status_options,
|
|
['value' => $this->fields['status']]);
|
|
} else {
|
|
echo $this->fields['status'];
|
|
}
|
|
echo "</td>";
|
|
|
|
// Associated ticket
|
|
echo "<td>" . __('Ticket', 'cve') . "</td>";
|
|
echo "<td>";
|
|
if ($this->fields['tickets_id'] > 0) {
|
|
$ticket = new Ticket();
|
|
if ($ticket->getFromDB($this->fields['tickets_id'])) {
|
|
echo "<a href='" . Ticket::getFormURLWithID($this->fields['tickets_id']) . "'>";
|
|
echo $ticket->fields['name'] . " (" . $this->fields['tickets_id'] . ")";
|
|
echo "</a>";
|
|
} else {
|
|
echo __('Unknown ticket', 'cve');
|
|
}
|
|
} else {
|
|
if ($canedit && $this->fields['status'] == 'NEW') {
|
|
echo "<button type='submit' name='create_ticket' value='1' class='submit'>";
|
|
echo __('Create Ticket', 'cve');
|
|
echo "</button>";
|
|
} else {
|
|
echo __('No ticket associated', 'cve');
|
|
}
|
|
}
|
|
echo "</td>";
|
|
|
|
echo "</tr>";
|
|
|
|
// Add entity dropdown if needed
|
|
echo "<tr class='tab_bg_1'>";
|
|
echo "<td>" . __('Entity', 'cve') . "</td>";
|
|
echo "<td>";
|
|
echo Dropdown::getDropdownName('glpi_entities', $this->fields['entities_id']);
|
|
echo "</td>";
|
|
|
|
// Creation date
|
|
echo "<td>" . __('Creation Date', 'cve') . "</td>";
|
|
echo "<td>" . Html::convDateTime($this->fields['date_creation']) . "</td>";
|
|
echo "</tr>";
|
|
|
|
$this->showFormButtons($options);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Actions done after the PURGE of the item in the database
|
|
*
|
|
* @return void
|
|
*/
|
|
function post_purgeItem() {
|
|
// Nothing special
|
|
}
|
|
|
|
/**
|
|
* Actions done after the UPDATE of the item in the database
|
|
*
|
|
* @param integer $history store changes history ?
|
|
* @return void
|
|
*/
|
|
function post_updateItem($history = 1) {
|
|
// If status changed to IGNORED, we might want to do something
|
|
if (in_array('status', $this->updates) && $this->fields['status'] == 'IGNORED') {
|
|
// Add a record to ignore this specific software-CVE combination in the future
|
|
// This would be implemented here
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get search function for the class
|
|
*
|
|
* @return array of search options
|
|
*/
|
|
function rawSearchOptions() {
|
|
$tab = [];
|
|
|
|
$tab[] = [
|
|
'id' => 'common',
|
|
'name' => self::getTypeName(2)
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '1',
|
|
'table' => $this->getTable(),
|
|
'field' => 'id',
|
|
'name' => __('ID', 'cve'),
|
|
'massiveaction' => false,
|
|
'datatype' => 'number'
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '2',
|
|
'table' => 'glpi_softwares',
|
|
'field' => 'name',
|
|
'name' => __('Software', 'cve'),
|
|
'massiveaction' => false,
|
|
'datatype' => 'dropdown',
|
|
'joinparams' => [
|
|
'jointype' => 'child',
|
|
'condition' => 'AND NEWTABLE.`id` = REFTABLE.`softwares_id`'
|
|
]
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '3',
|
|
'table' => 'glpi_softwareversions',
|
|
'field' => 'name',
|
|
'name' => __('Version', 'cve'),
|
|
'massiveaction' => false,
|
|
'datatype' => 'dropdown',
|
|
'joinparams' => [
|
|
'jointype' => 'child',
|
|
'condition' => 'AND NEWTABLE.`id` = REFTABLE.`softwareversions_id`'
|
|
]
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '4',
|
|
'table' => 'glpi_plugin_cve_cves',
|
|
'field' => 'cve_id',
|
|
'name' => __('CVE ID', 'cve'),
|
|
'massiveaction' => false,
|
|
'datatype' => 'dropdown',
|
|
'joinparams' => [
|
|
'jointype' => 'child',
|
|
'condition' => 'AND NEWTABLE.`id` = REFTABLE.`cves_id`'
|
|
]
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '5',
|
|
'table' => $this->getTable(),
|
|
'field' => 'severity',
|
|
'name' => __('Severity', 'cve'),
|
|
'datatype' => 'specific',
|
|
'searchtype' => ['equals', 'notequals']
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '6',
|
|
'table' => $this->getTable(),
|
|
'field' => 'status',
|
|
'name' => __('Status', 'cve'),
|
|
'datatype' => 'specific',
|
|
'searchtype' => ['equals', 'notequals']
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '7',
|
|
'table' => 'glpi_tickets',
|
|
'field' => 'name',
|
|
'name' => __('Ticket', 'cve'),
|
|
'massiveaction' => false,
|
|
'datatype' => 'dropdown',
|
|
'joinparams' => [
|
|
'jointype' => 'child',
|
|
'condition' => 'AND NEWTABLE.`id` = REFTABLE.`tickets_id`'
|
|
]
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '8',
|
|
'table' => 'glpi_entities',
|
|
'field' => 'completename',
|
|
'name' => __('Entity', 'cve'),
|
|
'massiveaction' => false,
|
|
'datatype' => 'dropdown'
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '9',
|
|
'table' => $this->getTable(),
|
|
'field' => 'date_creation',
|
|
'name' => __('Creation date', 'cve'),
|
|
'datatype' => 'datetime',
|
|
'massiveaction' => false
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '10',
|
|
'table' => $this->getTable(),
|
|
'field' => 'date_mod',
|
|
'name' => __('Last update', 'cve'),
|
|
'datatype' => 'datetime',
|
|
'massiveaction' => false
|
|
];
|
|
|
|
return $tab;
|
|
}
|
|
|
|
/**
|
|
* Get dashboard count of alerts by severity
|
|
*
|
|
* @return array
|
|
*/
|
|
static function getAlertStats() {
|
|
global $DB;
|
|
|
|
$stats = [];
|
|
|
|
// Count by severity
|
|
$query = "SELECT severity, status, COUNT(*) as count
|
|
FROM `" . self::getTable() . "`
|
|
GROUP BY severity, status";
|
|
|
|
$result = $DB->query($query);
|
|
|
|
$stats['by_severity'] = [
|
|
'CRITICAL' => 0,
|
|
'HIGH' => 0,
|
|
'MEDIUM' => 0,
|
|
'LOW' => 0
|
|
];
|
|
|
|
$stats['by_status'] = [
|
|
'NEW' => 0,
|
|
'PROCESSED' => 0,
|
|
'IGNORED' => 0
|
|
];
|
|
|
|
if ($result) {
|
|
while ($data = $DB->fetchAssoc($result)) {
|
|
// Add to severity stats
|
|
if (isset($stats['by_severity'][$data['severity']])) {
|
|
$stats['by_severity'][$data['severity']] += $data['count'];
|
|
}
|
|
|
|
// Add to status stats
|
|
if (isset($stats['by_status'][$data['status']])) {
|
|
$stats['by_status'][$data['status']] += $data['count'];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get total count
|
|
$query = "SELECT COUNT(*) as total FROM `" . self::getTable() . "`";
|
|
$result = $DB->query($query);
|
|
|
|
$stats['total'] = 0;
|
|
if ($result && $data = $DB->fetchAssoc($result)) {
|
|
$stats['total'] = $data['total'];
|
|
}
|
|
|
|
return $stats;
|
|
}
|
|
|
|
/**
|
|
* Get alerts for dashboard
|
|
*
|
|
* @param integer $limit Maximum number of alerts to return
|
|
* @return array
|
|
*/
|
|
static function getRecentAlerts($limit = 10) {
|
|
global $DB;
|
|
|
|
$alerts = [];
|
|
|
|
$query = "SELECT a.*,
|
|
c.cve_id,
|
|
c.severity AS cve_severity,
|
|
s.name AS software_name,
|
|
v.name AS version_name
|
|
FROM `" . self::getTable() . "` AS a
|
|
LEFT JOIN `glpi_plugin_cve_cves` AS c ON c.id = a.cves_id
|
|
LEFT JOIN `glpi_softwares` AS s ON s.id = a.softwares_id
|
|
LEFT JOIN `glpi_softwareversions` AS v ON v.id = a.softwareversions_id
|
|
ORDER BY a.date_creation DESC
|
|
LIMIT $limit";
|
|
|
|
$result = $DB->query($query);
|
|
|
|
if ($result) {
|
|
while ($data = $DB->fetchAssoc($result)) {
|
|
$alerts[] = $data;
|
|
}
|
|
}
|
|
|
|
return $alerts;
|
|
}
|
|
} |