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

1095 lines
34 KiB
PHP

<?php
/**
* GLPI CVE Plugin - CVE Source Class
* Manages external CVE data sources
*/
if (!defined('GLPI_ROOT')) {
die("Sorry. You can't access this file directly");
}
/**
* PluginCveCveSource class for managing CVE data sources
*/
class PluginCveCveSource extends CommonDBTM {
static $rightname = 'plugin_cve_source';
/**
* 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 Source', 'CVE Sources', $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 Source 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'>";
// Source Name
echo "<td>" . __('Source Name', 'cve') . "</td>";
echo "<td>";
echo Html::input('name', ['value' => $this->fields['name'], 'size' => 40]);
echo "</td>";
// Active
echo "<td>" . __('Active', 'cve') . "</td>";
echo "<td>";
Dropdown::showYesNo('is_active', $this->fields['is_active']);
echo "</td>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
// API URL
echo "<td>" . __('API URL', 'cve') . "</td>";
echo "<td>";
echo Html::input('url', ['value' => $this->fields['url'], 'size' => 60]);
echo "</td>";
// Sync Frequency
echo "<td>" . __('Sync Frequency (hours)', 'cve') . "</td>";
echo "<td>";
echo Html::input('sync_frequency', ['value' => $this->fields['sync_frequency'], 'type' => 'number', 'min' => 1, 'max' => 168]);
echo "</td>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
// API Key
echo "<td>" . __('API Key', 'cve') . "</td>";
echo "<td>";
echo Html::input('api_key', ['value' => $this->fields['api_key'], 'size' => 40, 'type' => 'password']);
echo "<br><i>" . __('Leave empty to keep current value', 'cve') . "</i>";
echo "</td>";
// Last Sync
echo "<td>" . __('Last Sync', 'cve') . "</td>";
echo "<td>";
if (empty($this->fields['last_sync'])) {
echo __('Never', 'cve');
} else {
echo Html::convDateTime($this->fields['last_sync']);
}
echo "</td>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
// Sync Status
echo "<td>" . __('Sync Status', 'cve') . "</td>";
echo "<td>";
$status_options = [
'SUCCESS' => __('Success', 'cve'),
'FAILED' => __('Failed', 'cve'),
'IN_PROGRESS' => __('In Progress', 'cve'),
'PENDING' => __('Pending', 'cve')
];
$sync_status = $this->fields['sync_status'] ?? 'PENDING';
echo $status_options[$sync_status] ?? __('Unknown', 'cve');
echo "</td>";
// Source Type
echo "<td>" . __('Source Type', 'cve') . "</td>";
echo "<td>";
$type_options = [
'NVD' => 'National Vulnerability Database (NVD)',
'MITRE' => 'MITRE CVE Database',
'CISA' => 'CISA Known Exploited Vulnerabilities (KEV)',
'CUSTOM' => __('Custom', 'cve')
];
Dropdown::showFromArray('source_type', $type_options,
['value' => $this->fields['source_type'] ?? 'CUSTOM']);
echo "</td>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
// Source Format
echo "<td>" . __('Data Format', 'cve') . "</td>";
echo "<td>";
$format_options = [
'JSON' => 'JSON',
'XML' => 'XML',
'CSV' => 'CSV'
];
Dropdown::showFromArray('data_format', $format_options,
['value' => $this->fields['data_format'] ?? 'JSON']);
echo "</td>";
// Description
echo "<td>" . __('Description', 'cve') . "</td>";
echo "<td>";
echo "<textarea name='description' cols='45' rows='3'>".$this->fields['description']."</textarea>";
echo "</td>";
echo "</tr>";
$this->showFormButtons($options);
// Add a Sync Now button if we're editing an existing source
if ($ID > 0 && $canedit) {
echo "<div class='center'>";
echo "<form method='post' action='".Plugin::getWebDir('cve')."/ajax/sync_now.php'>";
echo Html::hidden('id', ['value' => $ID]);
Html::showSecurityToken();
echo "<input type='submit' name='sync_now' value=\"" . __('Sync Now', 'cve') . "\" class='submit'>";
Html::closeForm();
echo "</div>";
}
return true;
}
/**
* Pre-process input data before adding
*
* @param array $input Input data
* @return array|false Processed input data or false on error
*/
function prepareInputForAdd($input) {
// Set creation date if not provided
if (!isset($input['date_creation'])) {
$input['date_creation'] = $_SESSION['glpi_currenttime'];
}
// Set default values
if (!isset($input['is_active'])) {
$input['is_active'] = 0;
}
if (!isset($input['sync_frequency'])) {
$input['sync_frequency'] = 24; // Default: once a day
}
if (!isset($input['sync_status'])) {
$input['sync_status'] = 'PENDING';
}
// Validate required fields
if (empty($input['name'])) {
Session::addMessageAfterRedirect(
__('Source name is required', 'cve'),
true,
ERROR
);
return false;
}
if (empty($input['url'])) {
Session::addMessageAfterRedirect(
__('API URL is required', 'cve'),
true,
ERROR
);
return false;
}
// Validate URL format
if (!filter_var($input['url'], FILTER_VALIDATE_URL)) {
Session::addMessageAfterRedirect(
__('Invalid URL format', 'cve'),
true,
ERROR
);
return false;
}
return $input;
}
/**
* Pre-process input data before updating
*
* @param array $input Input data
* @return array|false Processed input data or false on error
*/
function prepareInputForUpdate($input) {
// Set modification date
$input['date_mod'] = $_SESSION['glpi_currenttime'];
// Don't overwrite API key if empty (keep the old one)
if (isset($input['api_key']) && empty($input['api_key'])) {
unset($input['api_key']);
}
// Validate required fields if they're being updated
if (isset($input['name']) && empty($input['name'])) {
Session::addMessageAfterRedirect(
__('Source name is required', 'cve'),
true,
ERROR
);
return false;
}
if (isset($input['url']) && empty($input['url'])) {
Session::addMessageAfterRedirect(
__('API URL is required', 'cve'),
true,
ERROR
);
return false;
}
// Validate URL format if it's being updated
if (isset($input['url']) && !filter_var($input['url'], FILTER_VALIDATE_URL)) {
Session::addMessageAfterRedirect(
__('Invalid URL format', 'cve'),
true,
ERROR
);
return false;
}
return $input;
}
/**
* 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' => __('Source Name', 'cve'),
'datatype' => 'itemlink',
'massiveaction' => false
];
$tab[] = [
'id' => '2',
'table' => $this->getTable(),
'field' => 'url',
'name' => __('API URL', 'cve'),
'datatype' => 'string',
'massiveaction' => false
];
$tab[] = [
'id' => '3',
'table' => $this->getTable(),
'field' => 'is_active',
'name' => __('Active', 'cve'),
'datatype' => 'bool',
'massiveaction' => true
];
$tab[] = [
'id' => '4',
'table' => $this->getTable(),
'field' => 'sync_frequency',
'name' => __('Sync Frequency (hours)', 'cve'),
'datatype' => 'number',
'min' => 1,
'max' => 168,
'step' => 1,
'toadd' => [0 => __('Never', 'cve')],
'massiveaction' => true
];
$tab[] = [
'id' => '5',
'table' => $this->getTable(),
'field' => 'last_sync',
'name' => __('Last Sync', 'cve'),
'datatype' => 'datetime',
'massiveaction' => false
];
$tab[] = [
'id' => '6',
'table' => $this->getTable(),
'field' => 'sync_status',
'name' => __('Sync Status', 'cve'),
'datatype' => 'specific',
'searchtype' => ['equals', 'notequals']
];
$tab[] = [
'id' => '7',
'table' => $this->getTable(),
'field' => 'source_type',
'name' => __('Source Type', 'cve'),
'datatype' => 'specific',
'searchtype' => ['equals', 'notequals']
];
$tab[] = [
'id' => '19',
'table' => $this->getTable(),
'field' => 'date_mod',
'name' => __('Last update', 'cve'),
'datatype' => 'datetime',
'massiveaction' => false
];
$tab[] = [
'id' => '121',
'table' => $this->getTable(),
'field' => 'date_creation',
'name' => __('Creation date', 'cve'),
'datatype' => 'datetime',
'massiveaction' => false
];
return $tab;
}
/**
* Synchronize CVE data from a specific source
*
* @param integer $source_id ID of the source to sync
* @return boolean Success status
*/
function syncNow($source_id) {
global $DB;
// Get source information
$this->getFromDB($source_id);
// Check if source is active
if (!$this->fields['is_active']) {
Toolbox::logInFile('cve_plugin', sprintf('Sync skipped for source %s (ID: %d) because it is inactive',
$this->fields['name'], $source_id));
return false;
}
// Update the sync status to in progress
$this->update([
'id' => $source_id,
'sync_status' => 'IN_PROGRESS',
]);
try {
// Process based on source type
switch ($this->fields['source_type']) {
case 'NVD':
$success = $this->syncFromNVD();
break;
case 'MITRE':
$success = $this->syncFromMITRE();
break;
case 'CISA':
$success = $this->syncFromCISA();
break;
case 'CUSTOM':
default:
$success = $this->syncFromCustomURL();
break;
}
// Update the sync status
$this->update([
'id' => $source_id,
'sync_status' => $success ? 'SUCCESS' : 'FAILED',
'last_sync' => $_SESSION['glpi_currenttime']
]);
return $success;
} catch (Exception $e) {
// Log the error
Toolbox::logInFile('cve_plugin', sprintf('Error during sync of source %s (ID: %d): %s',
$this->fields['name'], $source_id, $e->getMessage()), true);
// Update the sync status to failed
$this->update([
'id' => $source_id,
'sync_status' => 'FAILED',
'last_sync' => $_SESSION['glpi_currenttime']
]);
return false;
}
}
/**
* Sync from NVD API
*
* @return boolean Success
*/
private function syncFromNVD() {
// Example NVD API: https://services.nvd.nist.gov/rest/json/cves/2.0
// Limited to demonstration - would need proper API key and pagination handling
$url = $this->fields['url'];
$api_key = $this->fields['api_key'];
// Construct API request with API key if provided
$options = [
'http' => [
'method' => 'GET',
'header' => [
'User-Agent: GLPI-CVE-Plugin/1.0',
]
]
];
if (!empty($api_key)) {
$options['http']['header'][] = 'apiKey: ' . $api_key;
}
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === false) {
Toolbox::logInFile('cve_plugin', 'Failed to retrieve data from NVD API', true);
return false;
}
// Parse JSON response
$data = json_decode($result, true);
if (json_last_error() !== JSON_ERROR_NONE) {
Toolbox::logInFile('cve_plugin', 'Error parsing JSON from NVD API: ' . json_last_error_msg(), true);
return false;
}
// Process the results - this would be more complex in a real implementation
if (isset($data['vulnerabilities'])) {
return $this->processCVEData($data['vulnerabilities']);
}
return false;
}
/**
* Sync from MITRE API
*
* @return boolean Success
*/
private function syncFromMITRE() {
// Simplified for demonstration
$url = $this->fields['url'];
$options = [
'http' => [
'method' => 'GET',
'header' => [
'User-Agent: GLPI-CVE-Plugin/1.0',
'Accept: application/json'
]
]
];
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === false) {
Toolbox::logInFile('cve_plugin', 'Failed to retrieve data from MITRE API', true);
return false;
}
// Parse JSON response
$data = json_decode($result, true);
if (json_last_error() !== JSON_ERROR_NONE) {
Toolbox::logInFile('cve_plugin', 'Error parsing JSON from MITRE API: ' . json_last_error_msg(), true);
return false;
}
// Process the results - format would depend on actual MITRE API structure
if (isset($data['cveItems'])) {
return $this->processCVEData($data['cveItems']);
}
return false;
}
/**
* Sync from CISA KEV Catalog
*
* @return boolean Success
*/
private function syncFromCISA() {
// Example for CISA Known Exploited Vulnerabilities catalog
$url = $this->fields['url'];
$options = [
'http' => [
'method' => 'GET',
'header' => 'User-Agent: GLPI-CVE-Plugin/1.0'
]
];
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === false) {
Toolbox::logInFile('cve_plugin', 'Failed to retrieve data from CISA KEV Catalog', true);
return false;
}
// Parse JSON response
$data = json_decode($result, true);
if (json_last_error() !== JSON_ERROR_NONE) {
Toolbox::logInFile('cve_plugin', 'Error parsing JSON from CISA KEV Catalog: ' . json_last_error_msg(), true);
return false;
}
// Process the results - format would depend on actual CISA API structure
if (isset($data['vulnerabilities'])) {
return $this->processCVEData($data['vulnerabilities'], true); // Mark as actively exploited
}
return false;
}
/**
* Sync from a custom URL
*
* @return boolean Success
*/
private function syncFromCustomURL() {
$url = $this->fields['url'];
$api_key = $this->fields['api_key'];
$format = $this->fields['data_format'] ?? 'JSON';
// Construct API request
$options = [
'http' => [
'method' => 'GET',
'header' => 'User-Agent: GLPI-CVE-Plugin/1.0'
]
];
// Add API key if provided
if (!empty($api_key)) {
if (strpos($url, '?') !== false) {
// URL already has parameters
$url .= '&apiKey=' . urlencode($api_key);
} else {
// URL has no parameters yet
$url .= '?apiKey=' . urlencode($api_key);
}
}
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === false) {
Toolbox::logInFile('cve_plugin', 'Failed to retrieve data from custom URL: ' . $url, true);
return false;
}
// Process based on format
switch (strtoupper($format)) {
case 'JSON':
$data = json_decode($result, true);
if (json_last_error() !== JSON_ERROR_NONE) {
Toolbox::logInFile('cve_plugin', 'Error parsing JSON from custom URL: ' . json_last_error_msg(), true);
return false;
}
break;
case 'XML':
libxml_use_internal_errors(true);
$xml = simplexml_load_string($result);
if ($xml === false) {
$errors = libxml_get_errors();
$error_msg = '';
foreach ($errors as $error) {
$error_msg .= $error->message . "\n";
}
libxml_clear_errors();
Toolbox::logInFile('cve_plugin', 'Error parsing XML from custom URL: ' . $error_msg, true);
return false;
}
$data = json_decode(json_encode($xml), true);
break;
case 'CSV':
// Simple CSV parsing - would need more robust handling in production
$lines = explode("\n", $result);
$headers = str_getcsv(array_shift($lines));
$data = [];
foreach ($lines as $line) {
if (empty(trim($line))) continue;
$row = array_combine($headers, str_getcsv($line));
if ($row) {
$data[] = $row;
}
}
break;
default:
Toolbox::logInFile('cve_plugin', 'Unsupported format: ' . $format, true);
return false;
}
// Process the data - this would need custom mapping based on the data structure
return $this->processCustomData($data);
}
/**
* Process CVE data from standard sources
*
* @param array $vulnerabilities Array of vulnerability data
* @param bool $actively_exploited Whether these vulnerabilities are actively exploited
* @return boolean Success
*/
private function processCVEData($vulnerabilities, $actively_exploited = false) {
global $DB;
// Initialize counters for logging
$count_added = 0;
$count_updated = 0;
$cve = new PluginCveCve();
foreach ($vulnerabilities as $vuln) {
// Extract data based on source structure
// This is a simplified example - real implementation would need to handle various formats
// Basic info we expect to find in most sources
$cve_id = $vuln['cveId'] ?? $vuln['id'] ?? $vuln['cve_id'] ?? null;
$description = $vuln['description'] ?? $vuln['summary'] ?? $vuln['desc'] ?? '';
$cvss_score = $vuln['cvssScore'] ?? $vuln['impact'] ?? $vuln['baseScore'] ?? null;
$cvss_vector = $vuln['cvssVector'] ?? $vuln['attackVector'] ?? '';
// Try to determine severity if not directly provided
$severity = $vuln['severity'] ?? '';
if (empty($severity) && !empty($cvss_score)) {
if ($cvss_score >= 9.0) $severity = 'CRITICAL';
else if ($cvss_score >= 7.0) $severity = 'HIGH';
else if ($cvss_score >= 4.0) $severity = 'MEDIUM';
else $severity = 'LOW';
}
// Dates
$published_date = $vuln['publishedDate'] ?? $vuln['published'] ?? null;
$modified_date = $vuln['lastModifiedDate'] ?? $vuln['modified'] ?? null;
// References and products
$references = $vuln['references'] ?? [];
$affected_products = $vuln['affectedProducts'] ?? $vuln['products'] ?? [];
// Skip if we don't have a valid CVE ID
if (empty($cve_id)) {
continue;
}
// Prepare the data
$cve_data = [
'cve_id' => $cve_id,
'description' => $description,
'cvss_score' => $cvss_score,
'cvss_vector' => $cvss_vector,
'severity' => $severity,
'status' => 'NEW', // Default for newly imported CVEs
'entities_id' => 0, // Root entity
'is_recursive' => 1 // Available in all sub-entities
];
// Add dates if available
if (!empty($published_date)) {
$cve_data['published_date'] = date('Y-m-d H:i:s', strtotime($published_date));
}
if (!empty($modified_date)) {
$cve_data['modified_date'] = date('Y-m-d H:i:s', strtotime($modified_date));
}
// Add references and affected products as JSON
if (!empty($references)) {
$cve_data['references'] = json_encode($references);
}
if (!empty($affected_products)) {
$cve_data['affected_products'] = json_encode($affected_products);
}
// Mark as actively exploited if from CISA KEV
if ($actively_exploited) {
$cve_data['status'] = 'ANALYZED'; // Increase priority
$cve_data['is_exploited'] = 1;
}
// Check if this CVE already exists
$existing_id = $cve->getFromDBbyRequest([
'WHERE' => ['cve_id' => $cve_id]
]);
if ($existing_id) {
// Update existing CVE
$cve_data['id'] = $cve->getID();
// Only update certain fields, preserve status if already processed
unset($cve_data['status']);
if ($cve->update($cve_data)) {
$count_updated++;
}
} else {
// Add new CVE
if ($cve->add($cve_data)) {
$count_added++;
// Process the new CVE with rules
PluginCveCveRule::processCVE($cve);
}
}
}
// Log results
Toolbox::logInFile('cve_plugin', sprintf('Source sync completed: %d CVEs added, %d CVEs updated',
$count_added, $count_updated));
return true;
}
/**
* Process custom format data
*
* @param array $data Array of custom data
* @return boolean Success
*/
private function processCustomData($data) {
global $DB;
// This is a placeholder for processing custom data formats
// Real implementation would need to map custom data structure to CVE fields
Toolbox::logInFile('cve_plugin', 'Processing custom data format');
// Initialize counters for logging
$count_added = 0;
$count_updated = 0;
$cve = new PluginCveCve();
// Example mapping function - would need customization per source
foreach ($data as $item) {
// Try to map fields based on common patterns
$cve_id = null;
// Look for CVE ID pattern in various fields
foreach ($item as $key => $value) {
if (is_string($value) && preg_match('/CVE-\d{4}-\d{4,7}/', $value)) {
$cve_id = $value;
break;
}
if (strtolower($key) === 'id' || strtolower($key) === 'cve_id' || strtolower($key) === 'vuln_id') {
if (is_string($value) && strpos($value, 'CVE-') === 0) {
$cve_id = $value;
break;
}
}
}
// Skip if we couldn't find a CVE ID
if (empty($cve_id)) {
continue;
}
// Try to map other fields
$description = $item['description'] ?? $item['summary'] ?? $item['details'] ?? '';
$severity = $item['severity'] ?? $item['criticality'] ?? $item['risk'] ?? '';
// Prepare the data
$cve_data = [
'cve_id' => $cve_id,
'description' => $description,
'severity' => strtoupper($severity),
'status' => 'NEW',
'entities_id' => 0, // Root entity
'is_recursive' => 1 // Available in all sub-entities
];
// Additional processing would be done here
// Check if this CVE already exists
$existing_id = $cve->getFromDBbyRequest([
'WHERE' => ['cve_id' => $cve_id]
]);
if ($existing_id) {
// Update existing CVE
$cve_data['id'] = $cve->getID();
// Only update certain fields, preserve status if already processed
unset($cve_data['status']);
if ($cve->update($cve_data)) {
$count_updated++;
}
} else {
// Add new CVE
if ($cve->add($cve_data)) {
$count_added++;
// Process the new CVE with rules
PluginCveCveRule::processCVE($cve);
}
}
}
// Log results
Toolbox::logInFile('cve_plugin', sprintf('Custom source sync completed: %d CVEs added, %d CVEs updated',
$count_added, $count_updated));
return true;
}
/**
* Cron task for syncing CVE sources
*
* @param CronTask $task CronTask object
* @return integer
*/
static function cronSyncSources($task) {
global $DB;
$task->log("Starting CVE sources synchronization");
$task->setVolume(0);
$source = new self();
// Get all active sources due for sync
$query = "SELECT id FROM `" . self::getTable() . "`
WHERE `is_active` = 1
AND (
`last_sync` IS NULL
OR `last_sync` < DATE_SUB(NOW(), INTERVAL `sync_frequency` HOUR)
)";
$result = $DB->query($query);
$success_count = 0;
$processed_count = 0;
if ($result) {
while ($data = $DB->fetchAssoc($result)) {
$task->log("Processing source ID: " . $data['id']);
$processed_count++;
if ($source->syncNow($data['id'])) {
$success_count++;
}
}
}
$task->setVolume($processed_count);
if ($processed_count > 0) {
$task->log("Synchronization completed: $success_count/$processed_count sources successfully synced");
if ($success_count > 0) {
// Trigger inventory analysis after successful sync
$inventory_task = new CronTask();
if ($inventory_task->getFromDBbyName('PluginCveCveInventory', 'AnalyzeInventory')) {
$inventory_task->update([
'id' => $inventory_task->fields['id'],
'state' => CronTask::STATE_WAITING
]);
}
return 1; // At least one source was successfully synced
}
} else {
$task->log("No sources to synchronize");
}
return 0; // No sources were successfully synced
}
/**
* Dropdown options for source types
*
* @param array $options
* @return string
*/
static function getTypesDropdown($name = 'source_type', $options = []) {
$params['value'] = 0;
$params['toadd'] = [];
$params['width'] = '80%';
if (is_array($options) && count($options)) {
foreach ($options as $key => $val) {
$params[$key] = $val;
}
}
$items = [
'NVD' => 'National Vulnerability Database (NVD)',
'MITRE' => 'MITRE CVE Database',
'CISA' => 'CISA Known Exploited Vulnerabilities (KEV)',
'CUSTOM' => __('Custom', 'cve')
];
return Dropdown::showFromArray($name, $items, $params);
}
/**
* Check if user has the right to perform an action
*
* @param $action integer ID of the action
* @param $right string|integer Expected right [default READ]
*
* @return boolean
*/
static function canAction($action, $right = READ) {
return Session::haveRight(self::$rightname, $right);
}
/**
* Add default CVE sources on plugin installation
*
* @return void
*/
static function addDefaultSources() {
$source = new self();
// Check if any sources already exist
if (countElementsInTable(self::getTable()) == 0) {
// Add NVD API
$source->add([
'name' => 'National Vulnerability Database (NVD)',
'url' => 'https://services.nvd.nist.gov/rest/json/cves/2.0',
'source_type' => 'NVD',
'data_format' => 'JSON',
'is_active' => 1,
'sync_frequency' => 6,
'description' => 'Official US government repository of standards-based vulnerability data',
'date_creation' => $_SESSION['glpi_currenttime']
]);
// Add MITRE CVE List
$source->add([
'name' => 'MITRE CVE Database',
'url' => 'https://cveawg.mitre.org/api/cve',
'source_type' => 'MITRE',
'data_format' => 'JSON',
'is_active' => 1,
'sync_frequency' => 12,
'description' => 'MITRE Corporation\'s list of Common Vulnerabilities and Exposures',
'date_creation' => $_SESSION['glpi_currenttime']
]);
// Add CISA KEV Catalog
$source->add([
'name' => 'CISA Known Exploited Vulnerabilities (KEV)',
'url' => 'https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json',
'source_type' => 'CISA',
'data_format' => 'JSON',
'is_active' => 1,
'sync_frequency' => 24,
'description' => 'U.S. CISA catalog of known exploited vulnerabilities',
'date_creation' => $_SESSION['glpi_currenttime']
]);
}
}
/**
* 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,
`url` varchar(255) NOT NULL,
`api_key` varchar(255) DEFAULT NULL,
`source_type` varchar(20) DEFAULT 'CUSTOM',
`data_format` varchar(20) DEFAULT 'JSON',
`is_active` tinyint(1) NOT NULL DEFAULT '0',
`sync_frequency` int(11) NOT NULL DEFAULT '24',
`last_sync` datetime DEFAULT NULL,
`sync_status` enum('SUCCESS','FAILED','IN_PROGRESS','PENDING') DEFAULT 'PENDING',
`description` text DEFAULT NULL,
`date_creation` datetime DEFAULT NULL,
`date_mod` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`),
KEY `is_active` (`is_active`),
KEY `source_type` (`source_type`),
KEY `last_sync` (`last_sync`),
KEY `sync_status` (`sync_status`),
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 sources
self::addDefaultSources();
}
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;
}
}