Skip to content

Commit

Permalink
Add: EPSS scores from referenced CVEs to VTs
Browse files Browse the repository at this point in the history
The VTs can now contain the maximum EPSS scores of the referenced CVE(s)
with the highest severity and of any referenced EPSS score.

This will provide an estimation of how likely a vulnerability detected
by a VT is to be exploited.
  • Loading branch information
timopollmeier committed Jun 5, 2024
1 parent 3026991 commit f7fdd1f
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 12 deletions.
8 changes: 8 additions & 0 deletions public/locales/gsa-de.json
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@
"CVE Filter": "CVE-Filter",
"CVE List": "CVE-Liste",
"CVE Scanner": "CVE-Scanner",
"CVE Severity": "CVE-Schweregrad",
"CVE: {{name}}": "CVE: {{name}}",
"CVEs": "CVEs",
"CVEs by CVSS (Total: {{count}})": "CVEs nach CVSS (Gesamt: {{count}})",
Expand Down Expand Up @@ -487,6 +488,11 @@
"Duration of last Scan": "Dauer des letzten Scans",
"Dynamic": "Dynamisch",
"Dynamic Severity": "Dynamischer Schweregrad",
"EPSS": "EPSS",
"EPSS Percentile": "EPSS-Perzentil",
"EPSS Score": "EPSS-Score",
"EPSS (CVE with highest severity)": "EPSS (CVE mit höchstem Schweregrad)",
"EPSS (highest EPSS score)": "EPSS (Höchster EPSS-Score)",
"ESXi": "ESXi",
"ESXi Credential": "ESXi-Anmeldedaten",
"ESXi authentication was successful": "ESXi-Authentifizierung war erfolgreich",
Expand Down Expand Up @@ -1133,6 +1139,7 @@
"Password only": "Nur Passwort",
"Password: Use existing Password": "Passwort: Verwende bestehendes Passwort",
"Path": "Pfad",
"Percentile": "Perzentil",
"Performance": "Leistungsdaten",
"Period": "Zeitintervall",
"Permission": "Berechtigung",
Expand Down Expand Up @@ -1403,6 +1410,7 @@
"Schedules Filter": "Zeitpläne-Filter",
"Schedules List": "Zeitplanliste",
"Scope": "Reichweite",
"Score": "Score",
"Scoring": "Scoring",
"Search by content": "Suche nach Inhalt",
"SecInfo": "Sicherheitsinfos",
Expand Down
8 changes: 8 additions & 0 deletions src/web/pages/cves/filterdialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ const SORT_FIELDS = [
name: 'severity',
displayName: _l('Severity'),
},
{
name: 'epss_score',
displayName: _l('EPSS Score'),
},
{
name: 'epss_percentile',
displayName: _l('EPSS Percentile'),
},
];

export default createFilterDialog({
Expand Down
2 changes: 1 addition & 1 deletion src/web/pages/cves/table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const Header = ({
title={_('Severity')}
/>
<TableHead colSpan="2">
{"EPSS"}
{_("EPSS")}
</TableHead>
{isDefined(actionsColumn) ? (
actionsColumn
Expand Down
25 changes: 25 additions & 0 deletions src/web/pages/nvts/__tests__/detailspage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,23 @@ const nvt = NVT.fromElement({
_type: 'VendorFix',
__text: 'This is a description',
},
epss: {
max_severity: {
score: 0.8765,
percentile: 0.9,
cve: {
_id: 'CVE-2020-1234',
severity: 10.0,
},
},
max_epss: {
score: 0.9876,
percentile: 0.8,
cve: {
_id: 'CVE-2020-5678',
},
},
},
timeout: '',
refs: {
ref: [
Expand Down Expand Up @@ -308,6 +325,14 @@ describe('Nvt Detailspage tests', () => {
expect(element).toHaveTextContent('CVSS Origin');
expect(element).toHaveTextContent('N/A');

expect(element).toHaveTextContent('EPSS (CVE with highest severity)');
expect(element).toHaveTextContent('EPSS Score');
expect(element).toHaveTextContent('0.87650');
expect(element).toHaveTextContent('EPSS Percentile');
expect(element).toHaveTextContent('0.90000');
expect(element).toHaveTextContent('EPSS (highest EPSS score)');
expect(element).toHaveTextContent('0.98760');

expect(element).toHaveTextContent('Insight');
expect(element).toHaveTextContent('Foo');

Expand Down
36 changes: 29 additions & 7 deletions src/web/pages/nvts/__tests__/listpage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ const nvt = NVT.fromElement({
_type: 'VendorFix',
__text: 'This is a description',
},
epss: {
max_severity: {
score: 0.8765,
percentile: 0.9,
cve: {
_id: 'CVE-2020-1234',
severity: 10.0,
},
},
max_epss: {
score: 0.9876,
percentile: 0.8,
cve: {
_id: 'CVE-2020-5678',
},
},
},
refs: {
ref: [
{_type: 'cve', _id: 'CVE-2020-1234'},
Expand Down Expand Up @@ -192,16 +209,21 @@ describe('NvtsPage tests', () => {
expect(header[4]).toHaveTextContent('CVE');
expect(header[6]).toHaveTextContent('Severity');
expect(header[7]).toHaveTextContent('QoD');
expect(header[8]).toHaveTextContent('EPSS');
expect(header[9]).toHaveTextContent('Score');
expect(header[10]).toHaveTextContent('Percentile');

const row = baseElement.querySelectorAll('tr');

expect(row[1]).toHaveTextContent('foo');
expect(row[1]).toHaveTextContent('bar');
expect(row[1]).toHaveTextContent('Mon, Jun 24, 2019 1:55 PM CEST');
expect(row[1]).toHaveTextContent('Mon, Jun 24, 2019 12:12 PM CEST');
expect(row[1]).toHaveTextContent('CVE-2020-1234');
expect(row[1]).toHaveTextContent('CVE-2020-5678');
expect(row[1]).toHaveTextContent('80 %');
expect(row[2]).toHaveTextContent('foo');
expect(row[2]).toHaveTextContent('bar');
expect(row[2]).toHaveTextContent('Mon, Jun 24, 2019 1:55 PM CEST');
expect(row[2]).toHaveTextContent('Mon, Jun 24, 2019 12:12 PM CEST');
expect(row[2]).toHaveTextContent('CVE-2020-1234');
expect(row[2]).toHaveTextContent('CVE-2020-5678');
expect(row[2]).toHaveTextContent('80 %');
expect(row[2]).toHaveTextContent('0.87650');
expect(row[2]).toHaveTextContent('0.90000');
});

test('should allow to bulk action on page contents', async () => {
Expand Down
17 changes: 17 additions & 0 deletions src/web/pages/nvts/__tests__/row.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@ const entity = NVT.fromElement({
_type: 'VendorFix',
__text: 'This is a description',
},
epss: {
max_severity: {
score: 0.8765,
percentile: 0.9,
cve: {
_id: 'CVE-2020-1234',
severity: 10.0,
},
},
max_epss: {
score: 0.9876,
percentile: 0.8,
cve: {
_id: 'CVE-2020-5678',
},
},
},
refs: {
ref: [
{_type: 'cve', _id: 'CVE-2020-1234'},
Expand Down
81 changes: 80 additions & 1 deletion src/web/pages/nvts/details.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import React from 'react';

import _ from 'gmp/locale';

import {isDefined} from 'gmp/utils/identity';
import {isDefined, isNumber} from 'gmp/utils/identity';

import {TAG_NA} from 'gmp/models/nvt';

Expand All @@ -44,9 +44,11 @@ import TableRow from 'web/components/table/row';
import References from './references';
import Solution from './solution';
import Pre from './preformatted';
import CveLink from "web/components/link/cvelink.jsx";

const NvtDetails = ({entity, links = true}) => {
const {
epss,
tags = {},
severity,
qod,
Expand All @@ -67,6 +69,9 @@ const NvtDetails = ({entity, links = true}) => {
<DetailsBlock title={_('Scoring')}>
<InfoTable>
<TableBody>
<TableData>
<b>{_('CVSS')}</b>
</TableData>
<TableRow>
<TableData>{_('CVSS Base')}</TableData>
<TableData>
Expand Down Expand Up @@ -104,6 +109,80 @@ const NvtDetails = ({entity, links = true}) => {
)}
</TableData>
</TableRow>
{ isDefined(epss?.max_severity) &&
<>
<TableData colSpan="2">
<b>{_('EPSS (CVE with highest severity)')}</b>
</TableData>
<TableRow>
<TableData>{_('EPSS Score')}</TableData>
<TableData>
{isNumber(epss?.max_severity?.score)
? epss?.max_severity?.score.toFixed(5) : _("N/A")}
</TableData>
</TableRow>
<TableRow>
<TableData>{_('EPSS Percentile')}</TableData>
<TableData>
{isNumber(epss?.max_severity?.percentile)
? epss?.max_severity?.percentile.toFixed(5) : _("N/A")}
</TableData>
</TableRow>
<TableRow>
<TableData>{_('CVE')}</TableData>
<TableData>
<CveLink id={epss?.max_severity?.cve?._id}>
{epss?.max_severity?.cve?._id}
</CveLink>
</TableData>
</TableRow>
<TableRow>
<TableData>{_('CVE Severity')}</TableData>
<Severitybar
severity={isDefined(epss?.max_severity?.cve?.severity)
? epss?.max_severity?.cve?.severity : _("N/A")}
/>
</TableRow>
</>
}
{ isDefined(epss?.max_epss) &&
<>
<TableData colSpan="2">
<b>{_('EPSS (highest EPSS score)')}</b>
</TableData>
<TableRow>
<TableData>{_('EPSS Score')}</TableData>
<TableData>
{isNumber(epss?.max_epss?.score)
? epss?.max_epss?.score.toFixed(5) : _("N/A")}
</TableData>
</TableRow>
<TableRow>
<TableData>{_('EPSS Percentile')}</TableData>
<TableData>
{isNumber(epss?.max_epss?.percentile)
? epss?.max_epss?.percentile.toFixed(5) : _("N/A")}
</TableData>
</TableRow>
<TableRow>
<TableData>{_('CVE')}</TableData>
<TableData>
<CveLink id={epss?.max_epss?.cve?._id}>
{epss?.max_epss?.cve?._id}
</CveLink>
</TableData>
</TableRow>
<TableRow>
<TableData>{_('CVE Severity')}</TableData>
<TableData>
<Severitybar
severity={isDefined(epss?.max_epss?.cve?.severity)
? epss?.max_epss?.cve?.severity : _("N/A")}
/>
</TableData>
</TableRow>
</>
}
</TableBody>
</InfoTable>
</DetailsBlock>
Expand Down
8 changes: 8 additions & 0 deletions src/web/pages/nvts/filterdialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ const SORT_FIELDS = [
name: 'qod',
displayName: _l('QoD'),
},
{
name: 'epss_score',
displayName: _l('EPSS Score'),
},
{
name: 'epss_percentile',
displayName: _l('EPSS Percentile'),
},
];

export default createFilterDialog({
Expand Down
11 changes: 10 additions & 1 deletion src/web/pages/nvts/row.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import React from 'react';

import Filter from 'gmp/models/filter.js';

import {isDefined} from 'gmp/utils/identity';
import {isDefined, isNumber} from 'gmp/utils/identity';

import SeverityBar from 'web/components/bar/severitybar';

Expand All @@ -42,6 +42,7 @@ import EntitiesActions from 'web/entities/actions';
import {RowDetailsToggle} from 'web/entities/row';

import PropTypes from 'web/utils/proptypes';
import {_} from "gmp/locale/lang.js";

const Row = ({
actionsComponent: ActionsComponent = EntitiesActions,
Expand Down Expand Up @@ -101,6 +102,14 @@ const Row = ({
<TableData align="end">
{entity.qod && <Qod value={entity.qod.value} />}
</TableData>
<TableData>
{isNumber(entity?.epss?.max_severity?.score)
? entity.epss?.max_severity?.score.toFixed(5) : _("N/A")}
</TableData>
<TableData>
{isNumber(entity?.epss?.max_severity?.percentile)
? entity.epss?.max_severity?.percentile.toFixed(5) : _("N/A")}
</TableData>
<ActionsComponent {...props} entity={entity} />
</TableRow>
);
Expand Down
Loading

0 comments on commit f7fdd1f

Please sign in to comment.