diff --git a/CMakeLists.txt b/CMakeLists.txt index 772712ac4..a45eb22f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,7 +101,7 @@ include (CPack) ## Variables -set (GVMD_DATABASE_VERSION 255) +set (GVMD_DATABASE_VERSION 256) set (GVMD_SCAP_DATABASE_VERSION 21) diff --git a/src/manage.c b/src/manage.c index 12ae1c6a0..0af8a5486 100644 --- a/src/manage.c +++ b/src/manage.c @@ -5085,6 +5085,8 @@ manage_sync (sigset_t *sigmask_current, wait_for_pid (scap_pid, "SCAP sync"); wait_for_pid (cert_pid, "CERT sync"); + update_scap_extra (); + lockfile_unlock (&lockfile); } } @@ -5977,6 +5979,54 @@ get_nvt_xml (iterator_t *nvts, int details, int pref_count, free (default_timeout); } + if (nvt_iterator_epss_cve (nvts)) + { + buffer_xml_append_printf + (buffer, + "" + "" + "%0.5f" + "%0.5f" + "", + nvt_iterator_epss_score (nvts), + nvt_iterator_epss_percentile (nvts), + nvt_iterator_epss_cve (nvts)); + + if (nvt_iterator_has_epss_severity (nvts)) + { + buffer_xml_append_printf + (buffer, + "%0.1f", + nvt_iterator_epss_severity (nvts)); + } + + buffer_xml_append_printf + (buffer, + "" + "" + "" + "%0.5f" + "%0.5f" + "", + nvt_iterator_max_epss_score (nvts), + nvt_iterator_max_epss_percentile (nvts), + nvt_iterator_max_epss_cve (nvts)); + + if (nvt_iterator_has_max_epss_severity (nvts)) + { + buffer_xml_append_printf + (buffer, + "%0.1f", + nvt_iterator_max_epss_severity (nvts)); + } + + buffer_xml_append_printf + (buffer, + "" + "" + ""); + } + xml_string_append (buffer, close_tag ? "" : ""); msg = g_string_free (buffer, FALSE); } diff --git a/src/manage.h b/src/manage.h index 018fbfed3..74ec69c88 100644 --- a/src/manage.h +++ b/src/manage.h @@ -1982,6 +1982,36 @@ nvt_iterator_solution_type (iterator_t*); const char* nvt_iterator_solution_method (iterator_t*); +double +nvt_iterator_epss_score (iterator_t*); + +double +nvt_iterator_epss_percentile (iterator_t*); + +const char* +nvt_iterator_epss_cve (iterator_t*); + +double +nvt_iterator_epss_severity (iterator_t*); + +gboolean +nvt_iterator_has_epss_severity (iterator_t*); + +double +nvt_iterator_max_epss_score (iterator_t*); + +double +nvt_iterator_max_epss_percentile (iterator_t*); + +const char* +nvt_iterator_max_epss_cve (iterator_t*); + +double +nvt_iterator_max_epss_severity (iterator_t*); + +gboolean +nvt_iterator_has_max_epss_severity (iterator_t*); + char* nvt_default_timeout (const char *); diff --git a/src/manage_migrators.c b/src/manage_migrators.c index 9d5436716..3053fd2fe 100644 --- a/src/manage_migrators.c +++ b/src/manage_migrators.c @@ -3176,6 +3176,46 @@ migrate_254_to_255 () return 0; } +/** + * @brief Migrate the database from version 255 to version 256. + * + * @return 0 success, -1 error. + */ +int +migrate_255_to_256 () +{ + sql_begin_immediate (); + + /* Ensure that the database is currently version 255. */ + + if (manage_db_version () != 255) + { + sql_rollback (); + return -1; + } + + /* Update the database. */ + + // Add new columns + + sql ("ALTER TABLE nvts ADD COLUMN epss_cve TEXT;"); + sql ("ALTER TABLE nvts ADD COLUMN epss_score DOUBLE PRECISION;"); + sql ("ALTER TABLE nvts ADD COLUMN epss_percentile DOUBLE PRECISION;"); + sql ("ALTER TABLE nvts ADD COLUMN epss_severity DOUBLE PRECISION;"); + sql ("ALTER TABLE nvts ADD COLUMN max_epss_cve TEXT;"); + sql ("ALTER TABLE nvts ADD COLUMN max_epss_score DOUBLE PRECISION;"); + sql ("ALTER TABLE nvts ADD COLUMN max_epss_percentile DOUBLE PRECISION;"); + sql ("ALTER TABLE nvts ADD COLUMN max_epss_severity DOUBLE PRECISION;"); + + /* Set the database version to 256. */ + + set_db_version (256); + + sql_commit (); + + return 0; +} + #undef UPDATE_DASHBOARD_SETTINGS /** @@ -3237,6 +3277,7 @@ static migrator_t database_migrators[] = { {253, migrate_252_to_253}, {254, migrate_253_to_254}, {255, migrate_254_to_255}, + {256, migrate_255_to_256}, /* End marker. */ {-1, NULL}}; diff --git a/src/manage_pg.c b/src/manage_pg.c index 851769370..19d3463f7 100644 --- a/src/manage_pg.c +++ b/src/manage_pg.c @@ -1868,7 +1868,16 @@ create_tables_nvt (const gchar *suffix) " solution_method text," " detection text," " qod integer," - " qod_type text);", + " qod_type text," + " epss_cve TEXT," + " epss_score DOUBLE PRECISION," + " epss_percentile DOUBLE PRECISION," + " epss_severity DOUBLE PRECISION," + " max_epss_cve TEXT," + " max_epss_score DOUBLE PRECISION," + " max_epss_percentile DOUBLE PRECISION," + " max_epss_severity DOUBLE PRECISION" + ");", suffix); } diff --git a/src/manage_sql_nvts.c b/src/manage_sql_nvts.c index 95714193d..adb57348f 100644 --- a/src/manage_sql_nvts.c +++ b/src/manage_sql_nvts.c @@ -41,6 +41,7 @@ #include "manage_preferences.h" #include "manage_sql.h" #include "manage_sql_configs.h" +#include "manage_sql_secinfo.h" #include "sql.h" #include "utils.h" @@ -1214,6 +1215,153 @@ DEF_ACCESS (nvt_iterator_detection, GET_ITERATOR_COLUMN_COUNT + 19); */ DEF_ACCESS (nvt_iterator_solution_method, GET_ITERATOR_COLUMN_COUNT + 20); +/** + * @brief Get the EPSS score selected by severity from an NVT iterator. + * + * @param[in] iterator Iterator. + * + * @return The EPSS score. + */ +double +nvt_iterator_epss_score (iterator_t* iterator) +{ + double ret; + if (iterator->done) return -1; + ret = iterator_double (iterator, GET_ITERATOR_COLUMN_COUNT + 21); + return ret; +} + +/** + * @brief Get the EPSS percentile selected by severity from an NVT iterator. + * + * @param[in] iterator Iterator. + * + * @return The EPSS percentile. + */ +double +nvt_iterator_epss_percentile (iterator_t* iterator) +{ + double ret; + if (iterator->done) return -1; + ret = iterator_double (iterator, GET_ITERATOR_COLUMN_COUNT + 22); + return ret; +} + +/** + * @brief Get the CVE of the EPSS score by severity from an NVT iterator. + * + * @param[in] iterator Iterator. + * + * @return CVE-ID of the EPSS score, or NULL if iteration is complete. + * Freed by cleanup_iterator. + */ +DEF_ACCESS (nvt_iterator_epss_cve, GET_ITERATOR_COLUMN_COUNT + 23); + +/** + * @brief Get the maximum severity of CVEs with EPSS info from an NVT iterator. + * + * @param[in] iterator Iterator. + * + * @return The severity score. + */ +double +nvt_iterator_epss_severity (iterator_t* iterator) +{ + double ret; + if (iterator->done) return -1; + ret = iterator_double (iterator, GET_ITERATOR_COLUMN_COUNT + 24); + return ret; +} + +/** + * @brief Get whether the NVT has a severity for the max severity EPSS score. + * + * @param[in] iterator Iterator. + * + * @return Whether the severity exists. + */ +gboolean +nvt_iterator_has_epss_severity (iterator_t* iterator) +{ + gboolean ret; + if (iterator->done) return -1; + ret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 24) != NULL; + return ret; +} + +/** + * @brief Get the maximum EPSS score from an NVT iterator. + * + * @param[in] iterator Iterator. + * + * @return The maximum EPSS score. + */ +double +nvt_iterator_max_epss_score (iterator_t* iterator) +{ + double ret; + if (iterator->done) return -1; + ret = iterator_double (iterator, GET_ITERATOR_COLUMN_COUNT + 25); + return ret; +} + +/** + * @brief Get the maximum EPSS percentile from an NVT iterator. + * + * @param[in] iterator Iterator. + * + * @return The maximum EPSS percentile. + */ +double +nvt_iterator_max_epss_percentile (iterator_t* iterator) +{ + double ret; + if (iterator->done) return -1; + ret = iterator_double (iterator, GET_ITERATOR_COLUMN_COUNT + 26); + return ret; +} + +/** + * @brief Get the CVE of the maximum EPSS score from an NVT iterator. + * + * @param[in] iterator Iterator. + * + * @return CVE-ID of the maximum EPSS score, or NULL if iteration is complete. + * Freed by cleanup_iterator. + */ +DEF_ACCESS (nvt_iterator_max_epss_cve, GET_ITERATOR_COLUMN_COUNT + 27); + +/** + * @brief Get the severity of the maximum EPSS score from an NVT iterator. + * @param[in] iterator Iterator. + * + * @return The severity score. + */ +double +nvt_iterator_max_epss_severity (iterator_t* iterator) +{ + double ret; + if (iterator->done) return -1; + ret = iterator_double (iterator, GET_ITERATOR_COLUMN_COUNT + 28); + return ret; +} + +/** + * @brief Get whether the NVT has a severity for the max EPSS score. + * + * @param[in] iterator Iterator. + * + * @return Whether the severity exists. + */ +gboolean +nvt_iterator_has_max_epss_severity (iterator_t* iterator) +{ + gboolean ret; + if (iterator->done) return -1; + ret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 28) != NULL; + return ret; +} + /** * @brief Get the default timeout of an NVT. * @@ -2532,6 +2680,9 @@ manage_rebuild (GSList *log_config, const db_conn_info_t *database) break; } + if (ret == 0) + ret = update_scap_extra (); + feed_lockfile_unlock (&lockfile); manage_option_cleanup (); diff --git a/src/manage_sql_nvts.h b/src/manage_sql_nvts.h index e9db88fad..bbe4d39a6 100644 --- a/src/manage_sql_nvts.h +++ b/src/manage_sql_nvts.h @@ -31,7 +31,9 @@ { GET_ITERATOR_FILTER_COLUMNS, "version", "cve", \ "family", "cvss_base", "severity", "cvss", "script_tags", "qod", \ "qod_type", "solution_type", "solution", "summary", "insight", \ - "affected", "impact", "detection", "solution_method", NULL } + "affected", "impact", "detection", "solution_method", "epss_score", \ + "epss_percentile", "max_epss_score", "max_epss_percentile", \ + NULL } /** * @brief NVT iterator columns. @@ -62,6 +64,18 @@ { "impact", NULL, KEYWORD_TYPE_STRING }, \ { "detection", NULL, KEYWORD_TYPE_STRING }, \ { "solution_method", NULL, KEYWORD_TYPE_STRING }, \ + { "coalesce (epss_score, 0.0)", "epss_score", \ + KEYWORD_TYPE_DOUBLE }, \ + { "coalesce (epss_percentile, 0.0)", "epss_percentile", \ + KEYWORD_TYPE_DOUBLE }, \ + { "epss_cve", NULL, KEYWORD_TYPE_STRING }, \ + { "epss_severity", NULL, KEYWORD_TYPE_DOUBLE }, \ + { "coalesce (max_epss_score, 0.0)", "max_epss_score", \ + KEYWORD_TYPE_DOUBLE }, \ + { "coalesce (max_epss_percentile, 0.0)", "max_epss_percentile", \ + KEYWORD_TYPE_DOUBLE }, \ + { "max_epss_cve", NULL, KEYWORD_TYPE_STRING }, \ + { "max_epss_severity", NULL, KEYWORD_TYPE_DOUBLE }, \ { NULL, NULL, KEYWORD_TYPE_UNKNOWN } \ } @@ -94,6 +108,18 @@ { "impact", NULL, KEYWORD_TYPE_STRING }, \ { "detection", NULL, KEYWORD_TYPE_STRING }, \ { "solution_method", NULL, KEYWORD_TYPE_STRING }, \ + { "coalesce (epss_score, 0.0)", "epss_score", \ + KEYWORD_TYPE_DOUBLE }, \ + { "coalesce (epss_percentile, 0.0)", "epss_percentile", \ + KEYWORD_TYPE_DOUBLE }, \ + { "epss_cve", NULL, KEYWORD_TYPE_STRING }, \ + { "epss_severity", NULL, KEYWORD_TYPE_DOUBLE }, \ + { "coalesce (max_epss_score, 0.0)", "max_epss_score", \ + KEYWORD_TYPE_DOUBLE }, \ + { "coalesce (max_epss_percentile, 0.0)", "max_epss_percentile", \ + KEYWORD_TYPE_DOUBLE }, \ + { "max_epss_cve", NULL, KEYWORD_TYPE_STRING }, \ + { "max_epss_severity", NULL, KEYWORD_TYPE_DOUBLE }, \ { NULL, NULL, KEYWORD_TYPE_UNKNOWN } \ } diff --git a/src/manage_sql_secinfo.c b/src/manage_sql_secinfo.c index 455fc3dbc..3de05c6f1 100644 --- a/src/manage_sql_secinfo.c +++ b/src/manage_sql_secinfo.c @@ -3571,6 +3571,65 @@ update_scap_placeholders () " WHERE cpe=cpes.id))" " WHERE cpes.title IS NULL;"); } + +/** + * @brief Update extra data for VTs based on SCAP and SCAP supplement data. + */ +static void +update_vt_scap_extra_data () +{ + g_info ("Assigning EPSS scores to VTs"); + + sql ("UPDATE nvts" + " SET epss_cve = NULL," + " epss_score = NULL," + " epss_percentile = NULL," + " epss_severity = NULL," + " max_epss_cve = NULL," + " max_epss_score = NULL," + " max_epss_percentile = NULL," + " max_epss_severity = NULL;"); + + sql ("WITH epss_candidates AS (" + " SELECT vt_oid, cve, severity, epss, percentile," + " rank() OVER (PARTITION BY vt_oid" + " ORDER BY severity DESC," + " epss DESC," + " scap.cves.modification_time DESC) AS rank" + " FROM (SELECT * FROM vt_refs WHERE type='cve') AS vt_cves" + " JOIN scap.epss_scores ON ref_id = cve" + " LEFT JOIN scap.cves ON scap.cves.name = cve" + " ORDER BY vt_oid" + ") " + "UPDATE nvts" + " SET epss_cve = epss_candidates.cve," + " epss_score = epss_candidates.epss," + " epss_percentile = epss_candidates.percentile," + " epss_severity = epss_candidates.severity" + " FROM epss_candidates" + " WHERE epss_candidates.vt_oid = nvts.oid" + " AND epss_candidates.rank = 1;"); + + sql ("WITH epss_candidates AS (" + " SELECT vt_oid, cve, severity, epss, percentile," + " rank() OVER (PARTITION BY vt_oid" + " ORDER BY epss DESC," + " severity DESC," + " scap.cves.modification_time DESC) AS rank" + " FROM (SELECT * FROM vt_refs WHERE type='cve') AS vt_cves" + " JOIN scap.epss_scores ON ref_id = cve" + " LEFT JOIN scap.cves ON scap.cves.name = cve" + " ORDER BY vt_oid" + ") " + "UPDATE nvts" + " SET max_epss_cve = epss_candidates.cve," + " max_epss_score = epss_candidates.epss," + " max_epss_percentile = epss_candidates.percentile," + " max_epss_severity = epss_candidates.severity" + " FROM epss_candidates" + " WHERE epss_candidates.vt_oid = nvts.oid" + " AND epss_candidates.rank = 1;"); +} /** * @brief Update CERT data that depends on SCAP. @@ -3906,6 +3965,29 @@ update_scap (gboolean reset_scap_db) return 0; } +/** + * @brief Update extra data in the SCAP DB that depends on other feeds. + * + * @return 0 success, -1 error. + */ +int +update_scap_extra () +{ + if (manage_scap_loaded () == 0) + { + g_info ("%s: SCAP database missing, skipping extra data update", + __func__); + return 0; + } + + g_debug ("%s: update SCAP extra data of VTs", __func__); + setproctitle ("Syncing SCAP: Updating VT extra data"); + + update_vt_scap_extra_data (); + + return 0; +} + /** * @brief Sync the SCAP DB. * @@ -3953,6 +4035,9 @@ rebuild_scap () if (ret == 1) ret = 2; + if (ret == 0) + ret = update_scap_extra (); + if (feed_lockfile_unlock (&lockfile)) { g_warning ( diff --git a/src/manage_sql_secinfo.h b/src/manage_sql_secinfo.h index 7e7a83ed6..c0518cc79 100644 --- a/src/manage_sql_secinfo.h +++ b/src/manage_sql_secinfo.h @@ -195,4 +195,7 @@ get_secinfo_commit_size (); void set_secinfo_commit_size (int); +int +update_scap_extra (); + #endif /* not _GVMD_MANAGE_SQL_SECINFO_H */ diff --git a/src/schema_formats/XML/GMP.xml.in b/src/schema_formats/XML/GMP.xml.in index ba7bba56f..4e09742ca 100644 --- a/src/schema_formats/XML/GMP.xml.in +++ b/src/schema_formats/XML/GMP.xml.in @@ -582,6 +582,7 @@ along with this program. If not, see . timeout default_timeout solution + epss preferences @@ -680,6 +681,112 @@ along with this program. If not, see . + + epss + Exploit Prediction Scoring System (EPSS) info if available + + max_severity + max_epss + + + max_severity + + EPSS info of the referenced CVE with the highest severity + + + In case there are multiple CVEs referenced by the NVT tied for the + highest severity, they are also sorted by EPSS score and modification + time and the first one is chosen. + + + score + percentile + cve + + + score + EPSS score of the CVE + + decimal + + + + percentile + EPSS percentile of the CVE + + decimal + + + + cve + The representative CVE chosen + + + id + CVE-ID of the CVE + text + + severity + + + severity + Severity (CVSS) score of the CVE if available + + severity + + + + + + max_epss + + EPSS info of the referenced CVE with the highest EPSS score + + + In case there are multiple CVEs referenced by the NVT tied for the + highest EPSS score, they are also sorted by severity and modification + time and the first one is chosen. + + + score + percentile + cve + + + score + EPSS score of the CVE + + decimal + + + + percentile + EPSS percentile of the CVE + + decimal + + + + cve + The representative CVE chosen + + + id + CVE-ID of the CVE + text + + severity + + + severity + Severity (CVSS) score of the CVE if available + + severity + + + + + preferences The list of preferences @@ -12199,6 +12306,34 @@ END:VCALENDAR integer Version of the NVT (deprected) + + epss_score + decimal + + EPSS score of highest severity CVE (see epss/max_severity) + + + + epss_percentile + decimal + + EPSS percentile of highest severity CVE (see epss/max_severity) + + + + max_epss_score + decimal + + Highest EPSS score of referenced CVEs (see epss/max_epss) + + + + max_epss_percentile + decimal + + Highest EPSS percentile of referenced CVEs (see epss/max_epss) + + type is "cve" @@ -12222,6 +12357,16 @@ END:VCALENDAR iso_time Time the CVE was published, alias for created + + epss_score + decimal + EPSS score the CVE + + + epss_percentile + decimal + EPSS percentile of the CVE + type is "cpe" @@ -13793,6 +13938,7 @@ END:VCALENDAR timeout default_timeout solution + epss preferences @@ -13969,6 +14115,112 @@ END:VCALENDAR text + + epss + Exploit Prediction Scoring System (EPSS) info if available + + max_severity + max_epss + + + max_severity + + EPSS info of the referenced CVE with the highest severity + + + In case there are multiple CVEs referenced by the NVT tied for the + highest severity, they are also sorted by EPSS score and modification + time and the first one is chosen. + + + score + percentile + cve + + + score + EPSS score of the CVE + + decimal + + + + percentile + EPSS percentile of the CVE + + decimal + + + + cve + The representative CVE chosen + + + id + CVE-ID of the CVE + text + + severity + + + severity + Severity (CVSS) score of the CVE if available + + severity + + + + + + max_epss + + EPSS info of the referenced CVE with the highest EPSS score + + + In case there are multiple CVEs referenced by the NVT tied for the + highest EPSS score, they are also sorted by severity and modification + time and the first one is chosen. + + + score + percentile + cve + + + score + EPSS score of the CVE + + decimal + + + + percentile + EPSS percentile of the CVE + + decimal + + + + cve + The representative CVE chosen + + + id + CVE-ID of the CVE + text + + severity + + + severity + Severity (CVSS) score of the CVE if available + + severity + + + + + preferences List of preferences of the NVT