mirror of
https://github.com/tips-of-mine/GLPI-Plugin-CVE-Prototype.git
synced 2025-06-27 22:58:45 +02:00
497 lines
15 KiB
PHP
497 lines
15 KiB
PHP
<?php
|
|
/**
|
|
* GLPI CVE Plugin - Rule Class
|
|
* Manages CVE processing rules
|
|
*/
|
|
|
|
if (!defined('GLPI_ROOT')) {
|
|
die("Sorry. You can't access this file directly");
|
|
}
|
|
|
|
/**
|
|
* PluginCveCveRule class for managing CVE processing rules
|
|
*/
|
|
class PluginCveCveRule extends CommonDBTM {
|
|
|
|
static $rightname = 'plugin_cve_rule';
|
|
|
|
/**
|
|
* 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 Rule', 'CVE Rules', $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 Rule 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'>";
|
|
|
|
// Rule Name
|
|
echo "<td>" . __('Rule Name', 'cve') . "</td>";
|
|
echo "<td>";
|
|
echo Html::input('name', ['value' => $this->fields['name'], 'size' => 40]);
|
|
echo "</td>";
|
|
|
|
// Priority
|
|
echo "<td>" . __('Priority', 'cve') . "</td>";
|
|
echo "<td>";
|
|
echo Html::input('priority', ['value' => $this->fields['priority'], 'size' => 5, 'type' => 'number', 'min' => 1]);
|
|
echo "<br><i>" . __('Lower numbers are processed first', 'cve') . "</i>";
|
|
echo "</td>";
|
|
|
|
echo "</tr>";
|
|
|
|
echo "<tr class='tab_bg_1'>";
|
|
|
|
// Criteria - Severity
|
|
echo "<td>" . __('Severity', 'cve') . "</td>";
|
|
echo "<td>";
|
|
$criteria = json_decode($this->fields['criteria'], true) ?: [];
|
|
$severity = $criteria['severity'] ?? 'CRITICAL';
|
|
|
|
$severity_options = [
|
|
'CRITICAL' => __('Critical', 'cve'),
|
|
'HIGH' => __('High', 'cve'),
|
|
'MEDIUM' => __('Medium', 'cve'),
|
|
'LOW' => __('Low', 'cve')
|
|
];
|
|
Dropdown::showFromArray('criteria_severity', $severity_options,
|
|
['value' => $severity]);
|
|
echo "</td>";
|
|
|
|
// Status
|
|
echo "<td>" . __('Status', 'cve') . "</td>";
|
|
echo "<td>";
|
|
$status_options = [
|
|
'NEW' => __('New', 'cve'),
|
|
'ANALYZED' => __('Analyzed', 'cve'),
|
|
'ASSIGNED' => __('Assigned', 'cve'),
|
|
'RESOLVED' => __('Resolved', 'cve')
|
|
];
|
|
Dropdown::showFromArray('rule_status', $status_options,
|
|
['value' => $this->fields['status'] ?? 'NEW']);
|
|
echo "</td>";
|
|
|
|
echo "</tr>";
|
|
|
|
echo "<tr class='tab_bg_1'>";
|
|
|
|
// Actions - Create Ticket
|
|
echo "<td>" . __('Create Ticket', 'cve') . "</td>";
|
|
echo "<td>";
|
|
$actions = json_decode($this->fields['actions'], true) ?: [];
|
|
$create_ticket = isset($actions['create_ticket']) ? $actions['create_ticket'] : true;
|
|
|
|
Dropdown::showYesNo('actions_create_ticket', $create_ticket);
|
|
echo "</td>";
|
|
|
|
// Ticket Priority
|
|
echo "<td>" . __('Ticket Priority', 'cve') . "</td>";
|
|
echo "<td>";
|
|
$ticket_priority = $actions['ticket_priority'] ?? 'NORMAL';
|
|
|
|
$priority_options = [
|
|
'VERY HIGH' => __('Very High', 'cve'),
|
|
'HIGH' => __('High', 'cve'),
|
|
'NORMAL' => __('Normal', 'cve'),
|
|
'LOW' => __('Low', 'cve')
|
|
];
|
|
Dropdown::showFromArray('actions_ticket_priority', $priority_options,
|
|
['value' => $ticket_priority]);
|
|
echo "</td>";
|
|
|
|
echo "</tr>";
|
|
|
|
echo "<tr class='tab_bg_1'>";
|
|
|
|
// Actions - Notify Admins
|
|
echo "<td>" . __('Notify Administrators', 'cve') . "</td>";
|
|
echo "<td>";
|
|
$notify_admins = isset($actions['notify_admins']) ? $actions['notify_admins'] : false;
|
|
|
|
Dropdown::showYesNo('actions_notify_admins', $notify_admins);
|
|
echo "</td>";
|
|
|
|
// Actions - Add to Report
|
|
echo "<td>" . __('Add to Vulnerability Report', 'cve') . "</td>";
|
|
echo "<td>";
|
|
$add_to_report = isset($actions['add_to_report']) ? $actions['add_to_report'] : false;
|
|
|
|
Dropdown::showYesNo('actions_add_to_report', $add_to_report);
|
|
echo "</td>";
|
|
|
|
echo "</tr>";
|
|
|
|
echo "<tr class='tab_bg_1'>";
|
|
|
|
// Is Active
|
|
echo "<td>" . __('Active Rule', 'cve') . "</td>";
|
|
echo "<td>";
|
|
Dropdown::showYesNo('is_active', $this->fields['is_active']);
|
|
echo "</td>";
|
|
|
|
echo "<td colspan='2'></td>";
|
|
|
|
echo "</tr>";
|
|
|
|
$this->showFormButtons($options);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Process a CVE with rules
|
|
*
|
|
* @param PluginCveCve $cve CVE to process
|
|
* @return boolean True if any rule was applied
|
|
*/
|
|
static function processCVE(PluginCveCve $cve) {
|
|
$rule = new self();
|
|
|
|
// Get active rules sorted by priority
|
|
$rules = $rule->find(['is_active' => 1], ['priority' => 'ASC']);
|
|
|
|
$rule_applied = false;
|
|
|
|
foreach ($rules as $rule_data) {
|
|
// Load the rule
|
|
$rule->getFromDB($rule_data['id']);
|
|
|
|
// Check if the CVE matches the criteria
|
|
if (self::matchesCriteria($cve, $rule_data)) {
|
|
// Apply the actions
|
|
self::applyActions($cve, $rule_data);
|
|
$rule_applied = true;
|
|
}
|
|
}
|
|
|
|
return $rule_applied;
|
|
}
|
|
|
|
/**
|
|
* Check if a CVE matches rule criteria
|
|
*
|
|
* @param PluginCveCve $cve CVE to check
|
|
* @param array $rule_data Rule data
|
|
* @return boolean True if the CVE matches the criteria
|
|
*/
|
|
private static function matchesCriteria(PluginCveCve $cve, array $rule_data) {
|
|
$criteria = json_decode($rule_data['criteria'], true) ?: [];
|
|
|
|
// Check severity
|
|
if (isset($criteria['severity'])) {
|
|
// Get the numeric values for comparison
|
|
$severity_values = [
|
|
'CRITICAL' => 4,
|
|
'HIGH' => 3,
|
|
'MEDIUM' => 2,
|
|
'LOW' => 1
|
|
];
|
|
|
|
$rule_severity = $severity_values[$criteria['severity']] ?? 0;
|
|
$cve_severity = $severity_values[$cve->fields['severity']] ?? 0;
|
|
|
|
// Match if CVE severity is greater than or equal to rule severity
|
|
if ($cve_severity < $rule_severity) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check affected products if specified
|
|
if (isset($criteria['affected_products']) && !empty($criteria['affected_products'])) {
|
|
$cve_products = json_decode($cve->fields['affected_products'], true) ?: [];
|
|
$rule_products = $criteria['affected_products'];
|
|
|
|
$found = false;
|
|
foreach ($rule_products as $product) {
|
|
if (in_array($product, $cve_products)) {
|
|
$found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!$found) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check CVSS score range if specified
|
|
if (isset($criteria['cvss_min']) && $cve->fields['cvss_score'] < $criteria['cvss_min']) {
|
|
return false;
|
|
}
|
|
|
|
if (isset($criteria['cvss_max']) && $cve->fields['cvss_score'] > $criteria['cvss_max']) {
|
|
return false;
|
|
}
|
|
|
|
// All criteria matched or no criteria specified
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Apply rule actions to a CVE
|
|
*
|
|
* @param PluginCveCve $cve CVE to process
|
|
* @param array $rule_data Rule data
|
|
* @return boolean True if actions were applied successfully
|
|
*/
|
|
private static function applyActions(PluginCveCve $cve, array $rule_data) {
|
|
$actions = json_decode($rule_data['actions'], true) ?: [];
|
|
|
|
// Create a ticket if needed
|
|
if (isset($actions['create_ticket']) && $actions['create_ticket']) {
|
|
$ticket_options = [];
|
|
|
|
// Set ticket priority
|
|
if (isset($actions['ticket_priority'])) {
|
|
switch ($actions['ticket_priority']) {
|
|
case 'VERY HIGH':
|
|
$ticket_options['priority'] = 5;
|
|
break;
|
|
case 'HIGH':
|
|
$ticket_options['priority'] = 4;
|
|
break;
|
|
case 'NORMAL':
|
|
$ticket_options['priority'] = 3;
|
|
break;
|
|
case 'LOW':
|
|
$ticket_options['priority'] = 2;
|
|
break;
|
|
default:
|
|
$ticket_options['priority'] = 3; // Default to normal
|
|
}
|
|
}
|
|
|
|
// Create the ticket
|
|
$cve->createTicket($cve->getID(), $ticket_options);
|
|
}
|
|
|
|
// Send notifications if needed
|
|
if (isset($actions['notify_admins']) && $actions['notify_admins']) {
|
|
// This would implement the notification logic
|
|
// For example: NotificationEvent::raiseEvent('new_cve', $cve);
|
|
}
|
|
|
|
// Add to report if needed
|
|
if (isset($actions['add_to_report']) && $actions['add_to_report']) {
|
|
// This would implement the reporting logic
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 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' => 'name',
|
|
'name' => __('Rule Name', 'cve'),
|
|
'datatype' => 'itemlink',
|
|
'massiveaction' => false
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '2',
|
|
'table' => $this->getTable(),
|
|
'field' => 'priority',
|
|
'name' => __('Priority', 'cve'),
|
|
'datatype' => 'number',
|
|
'min' => 1,
|
|
'massiveaction' => true
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '3',
|
|
'table' => $this->getTable(),
|
|
'field' => 'criteria',
|
|
'name' => __('Criteria', 'cve'),
|
|
'datatype' => 'text',
|
|
'massiveaction' => false
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '4',
|
|
'table' => $this->getTable(),
|
|
'field' => 'actions',
|
|
'name' => __('Actions', 'cve'),
|
|
'datatype' => 'text',
|
|
'massiveaction' => false
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '5',
|
|
'table' => $this->getTable(),
|
|
'field' => 'is_active',
|
|
'name' => __('Active', 'cve'),
|
|
'datatype' => 'bool',
|
|
'massiveaction' => true
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '19',
|
|
'table' => $this->getTable(),
|
|
'field' => 'date_mod',
|
|
'name' => __('Last update', 'cve'),
|
|
'datatype' => 'datetime',
|
|
'massiveaction' => false
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '16',
|
|
'table' => $this->getTable(),
|
|
'field' => 'date_creation',
|
|
'name' => __('Creation date', 'cve'),
|
|
'datatype' => 'datetime',
|
|
'massiveaction' => false
|
|
];
|
|
|
|
return $tab;
|
|
}
|
|
|
|
/**
|
|
* 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,
|
|
`name` varchar(100) NOT NULL,
|
|
`criteria` json DEFAULT NULL,
|
|
`actions` json DEFAULT NULL,
|
|
`priority` int(11) NOT NULL DEFAULT '1',
|
|
`is_active` tinyint(1) NOT NULL DEFAULT '0',
|
|
`status` varchar(20) DEFAULT 'NEW',
|
|
`date_creation` datetime DEFAULT NULL,
|
|
`date_mod` datetime DEFAULT NULL,
|
|
PRIMARY KEY (`id`),
|
|
KEY `name` (`name`),
|
|
KEY `is_active` (`is_active`),
|
|
KEY `priority` (`priority`),
|
|
KEY `date_creation` (`date_creation`),
|
|
KEY `date_mod` (`date_mod`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";
|
|
|
|
$DB->query($query) or die("Error creating $table " . $DB->error());
|
|
|
|
// Add default rules
|
|
$default_rules = [
|
|
[
|
|
'name' => 'Critical Vulnerabilities - Immediate Ticket',
|
|
'criteria' => json_encode(['severity' => 'CRITICAL']),
|
|
'actions' => json_encode([
|
|
'create_ticket' => true,
|
|
'ticket_priority' => 'VERY HIGH',
|
|
'notify_admins' => true
|
|
]),
|
|
'priority' => 1,
|
|
'is_active' => 1,
|
|
'status' => 'NEW',
|
|
'date_creation' => $_SESSION['glpi_currenttime'],
|
|
'date_mod' => $_SESSION['glpi_currenttime']
|
|
],
|
|
[
|
|
'name' => 'High Risk Vulnerabilities - Create Ticket',
|
|
'criteria' => json_encode(['severity' => 'HIGH']),
|
|
'actions' => json_encode([
|
|
'create_ticket' => true,
|
|
'ticket_priority' => 'HIGH',
|
|
'notify_admins' => false
|
|
]),
|
|
'priority' => 2,
|
|
'is_active' => 1,
|
|
'status' => 'NEW',
|
|
'date_creation' => $_SESSION['glpi_currenttime'],
|
|
'date_mod' => $_SESSION['glpi_currenttime']
|
|
],
|
|
[
|
|
'name' => 'Medium Risk - Add to Report',
|
|
'criteria' => json_encode(['severity' => 'MEDIUM']),
|
|
'actions' => json_encode([
|
|
'create_ticket' => false,
|
|
'add_to_report' => true
|
|
]),
|
|
'priority' => 3,
|
|
'is_active' => 1,
|
|
'status' => 'NEW',
|
|
'date_creation' => $_SESSION['glpi_currenttime'],
|
|
'date_mod' => $_SESSION['glpi_currenttime']
|
|
]
|
|
];
|
|
|
|
$rule = new self();
|
|
foreach ($default_rules as $rule_data) {
|
|
$rule->add($rule_data);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
} |