Files
GLPI-Plugin-CVE-Prototype/inc/cveticket.class.php
2025-05-31 10:03:48 +02:00

494 lines
15 KiB
PHP

<?php
/**
* GLPI CVE Plugin - CVE Ticket Class
* Manages links between CVEs and GLPI Tickets
*/
if (!defined('GLPI_ROOT')) {
die("Sorry. You can't access this file directly");
}
/**
* PluginCveCveTicket class for managing CVE-Ticket relations
*/
class PluginCveCveTicket extends CommonDBRelation {
// From CommonDBRelation
static public $itemtype_1 = 'PluginCveCve';
static public $items_id_1 = 'cves_id';
static public $itemtype_2 = 'Ticket';
static public $items_id_2 = 'tickets_id';
static $rightname = 'plugin_cve_ticket';
/**
* 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('CVE Ticket', 'CVE Tickets', $nb, 'cve');
}
/**
* 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_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' => '3',
'table' => 'glpi_tickets',
'field' => 'name',
'name' => __('Ticket', 'cve'),
'massiveaction' => false,
'datatype' => 'dropdown',
'joinparams' => [
'jointype' => 'child',
'condition' => 'AND NEWTABLE.`id` = REFTABLE.`tickets_id`'
]
];
$tab[] = [
'id' => '4',
'table' => $this->getTable(),
'field' => 'creation_type',
'name' => __('Creation Type', 'cve'),
'massiveaction' => false,
'datatype' => 'specific',
'searchtype' => ['equals', 'notequals']
];
$tab[] = [
'id' => '5',
'table' => $this->getTable(),
'field' => 'date_creation',
'name' => __('Creation date', 'cve'),
'datatype' => 'datetime',
'massiveaction' => false
];
return $tab;
}
/**
* Show tickets for a CVE
*
* @param PluginCveCve $cve CVE object
* @return void
*/
static function showForCVE(PluginCveCve $cve) {
global $DB;
$ID = $cve->getField('id');
if (!$cve->can($ID, READ)) {
return false;
}
$canedit = $cve->can($ID, UPDATE);
$rand = mt_rand();
$iterator = $DB->request([
'SELECT' => [
'glpi_plugin_cve_tickets.*',
'glpi_tickets.name AS ticket_name',
'glpi_tickets.status AS ticket_status',
'glpi_tickets.date AS ticket_date',
'glpi_tickets.priority AS ticket_priority'
],
'FROM' => 'glpi_plugin_cve_tickets',
'LEFT JOIN' => [
'glpi_tickets' => [
'ON' => [
'glpi_plugin_cve_tickets' => 'tickets_id',
'glpi_tickets' => 'id'
]
]
],
'WHERE' => [
'glpi_plugin_cve_tickets.cves_id' => $ID
],
'ORDER' => [
'glpi_tickets.date DESC'
]
]);
$tickets = [];
$used = [];
foreach ($iterator as $data) {
$tickets[$data['id']] = $data;
$used[$data['tickets_id']] = $data['tickets_id'];
}
if ($canedit) {
echo "<div class='firstbloc'>";
echo "<form name='cveticket_form$rand' id='cveticket_form$rand' method='post'
action='" . Toolbox::getItemTypeFormURL(__CLASS__) . "'>";
echo "<table class='tab_cadre_fixe'>";
echo "<tr class='tab_bg_2'><th colspan='2'>" . __('Add a ticket', 'cve') . "</th></tr>";
echo "<tr class='tab_bg_1'><td class='right'>";
echo "<input type='hidden' name='cves_id' value='$ID'>";
Ticket::dropdown([
'used' => $used,
'entity' => $cve->getEntityID(),
'entity_sons' => $cve->isRecursive(),
'displaywith' => ['id']
]);
echo "</td><td class='center'>";
echo "<input type='submit' name='add' value=\"" . _sx('button', 'Add') . "\" class='submit'>";
echo "</td></tr>";
echo "</table>";
Html::closeForm();
echo "</div>";
}
if ($canedit && count($tickets)) {
$massiveactionparams = [
'num_displayed' => min($_SESSION['glpilist_limit'], count($tickets)),
'container' => 'mass' . __CLASS__ . $rand,
'specific_actions' => [
'purge' => _x('button', 'Delete permanently')
]
];
Html::showMassiveActions($massiveactionparams);
}
echo "<table class='tab_cadre_fixehov'>";
$header_begin = "<tr>";
$header_top = '';
$header_bottom = '';
$header_end = '';
if ($canedit && count($tickets)) {
$header_top .= "<th width='10'>" . Html::getCheckAllAsCheckbox('mass' . __CLASS__ . $rand) . "</th>";
$header_bottom .= "<th width='10'>" . Html::getCheckAllAsCheckbox('mass' . __CLASS__ . $rand) . "</th>";
}
$header_end .= "<th>" . __('Ticket', 'cve') . "</th>";
$header_end .= "<th>" . __('Status', 'cve') . "</th>";
$header_end .= "<th>" . __('Priority', 'cve') . "</th>";
$header_end .= "<th>" . __('Opening date', 'cve') . "</th>";
$header_end .= "<th>" . __('Creation type', 'cve') . "</th>";
$header_end .= "</tr>";
echo $header_begin . $header_top . $header_end;
foreach ($tickets as $data) {
echo "<tr class='tab_bg_1'>";
if ($canedit) {
echo "<td width='10'>";
Html::showMassiveActionCheckBox(__CLASS__, $data['id']);
echo "</td>";
}
$ticket = new Ticket();
$ticket->getFromDB($data['tickets_id']);
echo "<td class='center'>";
if ($ticket->can($data['tickets_id'], READ)) {
echo "<a href=\"" . Ticket::getFormURLWithID($data['tickets_id']) . "\">";
echo $data['ticket_name'] . " (" . $data['tickets_id'] . ")";
echo "</a>";
} else {
echo $data['ticket_name'] . " (" . $data['tickets_id'] . ")";
}
echo "</td>";
// Status
echo "<td class='center'>";
echo Ticket::getStatus($data['ticket_status']);
echo "</td>";
// Priority
echo "<td class='center'>";
echo Ticket::getPriorityName($data['ticket_priority']);
echo "</td>";
// Date
echo "<td class='center'>";
echo Html::convDateTime($data['ticket_date']);
echo "</td>";
// Creation type
echo "<td class='center'>";
echo $data['creation_type'] == 'AUTO' ? __('Automatic', 'cve') : __('Manual', 'cve');
echo "</td>";
echo "</tr>";
}
if ($header_bottom) {
echo $header_begin . $header_bottom . $header_end;
}
echo "</table>";
if ($canedit && count($tickets)) {
$massiveactionparams['ontop'] = false;
Html::showMassiveActions($massiveactionparams);
Html::closeForm();
}
}
/**
* Show CVEs for a ticket
*
* @param Ticket $ticket Ticket object
* @return void
*/
static function showForTicket(Ticket $ticket) {
global $DB;
$ID = $ticket->getField('id');
if (!$ticket->can($ID, READ)) {
return false;
}
$canedit = $ticket->can($ID, UPDATE);
$rand = mt_rand();
$iterator = $DB->request([
'SELECT' => [
'glpi_plugin_cve_tickets.*',
'glpi_plugin_cve_cves.cve_id',
'glpi_plugin_cve_cves.severity',
'glpi_plugin_cve_cves.cvss_score',
'glpi_plugin_cve_cves.status AS cve_status'
],
'FROM' => 'glpi_plugin_cve_tickets',
'LEFT JOIN' => [
'glpi_plugin_cve_cves' => [
'ON' => [
'glpi_plugin_cve_tickets' => 'cves_id',
'glpi_plugin_cve_cves' => 'id'
]
]
],
'WHERE' => [
'glpi_plugin_cve_tickets.tickets_id' => $ID
]
]);
$cvetickets = [];
$used = [];
foreach ($iterator as $data) {
$cvetickets[$data['id']] = $data;
$used[$data['cves_id']] = $data['cves_id'];
}
if ($canedit) {
echo "<div class='firstbloc'>";
echo "<form name='ticketcve_form$rand' id='ticketcve_form$rand' method='post'
action='" . Toolbox::getItemTypeFormURL(__CLASS__) . "'>";
echo "<table class='tab_cadre_fixe'>";
echo "<tr class='tab_bg_2'><th colspan='2'>" . __('Add a CVE', 'cve') . "</th></tr>";
echo "<tr class='tab_bg_1'><td class='right'>";
echo "<input type='hidden' name='tickets_id' value='$ID'>";
$cve = new PluginCveCve();
$cve->dropdown([
'name' => 'cves_id',
'entity' => $ticket->getEntityID(),
'used' => $used
]);
echo "</td><td class='center'>";
echo "<input type='submit' name='add' value=\"" . _sx('button', 'Add') . "\" class='submit'>";
echo "</td></tr>";
echo "</table>";
Html::closeForm();
echo "</div>";
}
if ($canedit && count($cvetickets)) {
$massiveactionparams = [
'num_displayed' => min($_SESSION['glpilist_limit'], count($cvetickets)),
'container' => 'mass' . __CLASS__ . $rand,
'specific_actions' => [
'purge' => _x('button', 'Delete permanently')
]
];
Html::showMassiveActions($massiveactionparams);
}
echo "<table class='tab_cadre_fixehov'>";
$header_begin = "<tr>";
$header_top = '';
$header_bottom = '';
$header_end = '';
if ($canedit && count($cvetickets)) {
$header_top .= "<th width='10'>" . Html::getCheckAllAsCheckbox('mass' . __CLASS__ . $rand) . "</th>";
$header_bottom .= "<th width='10'>" . Html::getCheckAllAsCheckbox('mass' . __CLASS__ . $rand) . "</th>";
}
$header_end .= "<th>" . __('CVE ID', 'cve') . "</th>";
$header_end .= "<th>" . __('Severity', 'cve') . "</th>";
$header_end .= "<th>" . __('CVSS Score', 'cve') . "</th>";
$header_end .= "<th>" . __('Status', 'cve') . "</th>";
$header_end .= "<th>" . __('Creation Type', 'cve') . "</th>";
$header_end .= "</tr>";
echo $header_begin . $header_top . $header_end;
foreach ($cvetickets as $data) {
echo "<tr class='tab_bg_1'>";
if ($canedit) {
echo "<td width='10'>";
Html::showMassiveActionCheckBox(__CLASS__, $data['id']);
echo "</td>";
}
$cve = new PluginCveCve();
$cve->getFromDB($data['cves_id']);
echo "<td class='center'>";
if ($cve->can($data['cves_id'], READ)) {
echo "<a href=\"" . PluginCveCve::getFormURLWithID($data['cves_id']) . "\">";
echo $data['cve_id'];
echo "</a>";
} else {
echo $data['cve_id'];
}
echo "</td>";
// Severity
echo "<td class='center'>";
echo "<span class='" . PluginCveCve::getSeverityClass($data['severity']) . "'>";
echo $data['severity'];
echo "</span>";
echo "</td>";
// CVSS Score
echo "<td class='center'>";
echo $data['cvss_score'];
echo "</td>";
// Status
echo "<td class='center'>";
echo "<span class='" . PluginCveCve::getStatusClass($data['cve_status']) . "'>";
echo $data['cve_status'];
echo "</span>";
echo "</td>";
// Creation type
echo "<td class='center'>";
echo $data['creation_type'] == 'AUTO' ? __('Automatic', 'cve') : __('Manual', 'cve');
echo "</td>";
echo "</tr>";
}
if ($header_bottom) {
echo $header_begin . $header_bottom . $header_end;
}
echo "</table>";
if ($canedit && count($cvetickets)) {
$massiveactionparams['ontop'] = false;
Html::showMassiveActions($massiveactionparams);
Html::closeForm();
}
}
/**
* Add events to ticket notifications
*
* @param array $events Events array
* @return array Modified events array
*/
static function addEvents(&$events) {
$events['cve_added'] = __('CVE linked to ticket', 'cve');
return $events;
}
/**
* Install the plugin database schema
*
* @return boolean
*/
static function install(Migration $migration) {
global $DB;
$table = self::getTable();
if (!$DB->tableExists($table)) {
$migration->displayMessage("Installing $table");
$query = "CREATE TABLE IF NOT EXISTS `$table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`cves_id` int(11) NOT NULL,
`tickets_id` int(11) NOT NULL,
`creation_type` enum('AUTO','MANUAL') DEFAULT 'MANUAL',
`date_creation` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `cves_id_tickets_id` (`cves_id`,`tickets_id`),
KEY `cves_id` (`cves_id`),
KEY `tickets_id` (`tickets_id`),
KEY `creation_type` (`creation_type`),
KEY `date_creation` (`date_creation`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";
$DB->query($query) or die("Error creating $table " . $DB->error());
}
return true;
}
/**
* Uninstall the plugin database schema
*
* @return boolean
*/
static function uninstall(Migration $migration) {
global $DB;
$table = self::getTable();
if ($DB->tableExists($table)) {
$migration->displayMessage("Uninstalling $table");
$migration->dropTable($table);
}
return true;
}
}