mirror of
https://github.com/tips-of-mine/GLPI-Plugin-CVE-Prototype.git
synced 2025-06-28 07:08:44 +02:00
Start repository
This commit is contained in:
414
inc/cvealert.class.php
Normal file
414
inc/cvealert.class.php
Normal file
@ -0,0 +1,414 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user