Skip to content

Commit

Permalink
Add: Support CVSS 4.0 fields in CVEs.
Browse files Browse the repository at this point in the history
CVSS 4.0 metrics can now be displayed in CVE details.
  • Loading branch information
a-h-abdelsalam committed Jun 10, 2024
1 parent d3e9a3e commit 126712d
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 46 deletions.
19 changes: 18 additions & 1 deletion src/gmp/models/cve.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import {
parseCvssV2BaseFromVector,
parseCvssV3BaseFromVector,
} from 'gmp/parser/cvss';

import {parseCvssV4MetricsFromVector} from 'gmp/parser/cvssV4';

import Info from './info';

class Cve extends Info {
Expand Down Expand Up @@ -63,7 +66,21 @@ class Cve extends Info {
if (isEmpty(ret.cvss_vector)) {
ret.cvss_vector = '';
}
if (ret.cvss_vector.includes('CVSS:3')) {
if (ret.cvss_vector.includes('CVSS:4')) {
const {AV, AC, AT, PR, UI, VC, VI, VA, SC, SI, SA} =
parseCvssV4MetricsFromVector(ret.cvss_vector);
ret.cvssAttackVector = AV;
ret.cvssAttackComplexity = AC;
ret.cvssAttackRequirements = AT;
ret.cvssPrivilegesRequired = PR;
ret.cvssUserInteraction = UI;
ret.cvssConfidentialityVS = VC;
ret.cvssIntegrityVS = VI;
ret.cvssAvailabilityVS = VA;
ret.cvssConfidentialitySS = SC;
ret.cvssIntegritySS = SI;
ret.cvssAvailabilitySS = SA;
} else if (ret.cvss_vector.includes('CVSS:3')) {
const {
attackVector,
attackComplexity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import {describe, test, expect} from '@gsa/testing';

import {
calculateVector,
expectedMetricOptionsOrdered,
parseCvssV4MetricsFromVector,
processVector,
removeUnusedMetrics,
} from 'web/pages/extras/cvssV4/utils';
import {expectedMetricOptionsOrdered} from 'web/pages/extras/cvssV4/cvssConfig';
} from '../cvssV4';

describe('CVSS V4.0 Utils', () => {
describe('CVSSV4 parser', () => {
describe('calculateVector', () => {
test('should correctly calculate the CVSS vector', () => {
const cvssVectorObject = {
Expand Down Expand Up @@ -94,4 +95,42 @@ describe('CVSS V4.0 Utils', () => {
);
});
});
describe('parseCvssV4MetricsFromVector', () => {
test('should return metric labels from CVSS vector', () => {
const vectorString =
'CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:H/VA:N/SC:H/SI:L/SA:N';
const result = parseCvssV4MetricsFromVector(vectorString);
expect(result).toEqual({
AV: 'Network',
AC: 'Low',
AT: 'None',
PR: 'None',
UI: 'None',
VC: 'Low',
VI: 'High',
VA: 'None',
SC: 'High',
SI: 'Low',
SA: 'None',
});
});

test('should return only valid metric labels', () => {
const incompleteVectorString =
'CVSS:4.0/AV:Q/AC:L/AT:N/PR:N/UI:N/VC:N/CR';
const result = parseCvssV4MetricsFromVector(incompleteVectorString);
expect(result).toEqual({
AC: 'Low',
AT: 'None',
PR: 'None',
UI: 'None',
VC: 'None',
});
});

test('should return an empty object', () => {
expect(parseCvssV4MetricsFromVector('')).toEqual({});
expect(parseCvssV4MetricsFromVector()).toEqual({});
});
});
});
118 changes: 117 additions & 1 deletion src/web/pages/extras/cvssV4/utils.js → src/gmp/parser/cvssV4.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,100 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import {isDefined} from 'gmp/utils/identity';
import {CVSS40} from '@pandatix/js-cvss';
import {expectedMetricOptionsOrdered} from './cvssConfig';

export const expectedMetricOptionsOrdered = [
['AV', 'N', 'A', 'L', 'P'],
['AC', 'L', 'H'],
['AT', 'N', 'P'],
['PR', 'N', 'L', 'H'],
['UI', 'N', 'P', 'A'],
['VC', 'N', 'L', 'H'],
['VI', 'N', 'L', 'H'],
['VA', 'N', 'L', 'H'],
['SC', 'N', 'L', 'H'],
['SI', 'N', 'L', 'H'],
['SA', 'N', 'L', 'H'],
['E', 'X', 'A', 'P', 'U'],
['CR', 'X', 'H', 'M', 'L'],
['IR', 'X', 'H', 'M', 'L'],
['AR', 'X', 'H', 'M', 'L'],
['MAV', 'X', 'N', 'A', 'L', 'P'],
['MAC', 'X', 'L', 'H'],
['MAT', 'X', 'N', 'P'],
['MPR', 'X', 'N', 'L', 'H'],
['MUI', 'X', 'N', 'P', 'A'],
['MVC', 'X', 'H', 'L', 'N'],
['MVI', 'X', 'H', 'L', 'N'],
['MVA', 'X', 'H', 'L', 'N'],
['MSC', 'X', 'H', 'L', 'N'],
['MSI', 'X', 'S', 'H', 'L', 'N'],
['MSA', 'X', 'S', 'H', 'L', 'N'],
['S', 'X', 'N', 'P'],
['AU', 'X', 'N', 'Y'],
['R', 'X', 'A', 'U', 'I'],
['V', 'X', 'D', 'C'],
['RE', 'X', 'L', 'M', 'H'],
['U', 'X', 'Clear', 'Green', 'Amber', 'Red'],
];

const cvss4MetricValueToLabels = {
AV: {
N: 'Network',
A: 'Adjacent',
L: 'Local',
P: 'Physical',
},
AC: {
L: 'Low',
H: 'High',
},
AT: {
N: 'None',
P: 'Present',
},
PR: {
N: 'None',
L: 'Low',
H: 'High',
},
UI: {
N: 'None',
P: 'Passive',
A: 'Active',
},
VC: {
N: 'None',
L: 'Low',
H: 'High',
},
VI: {
N: 'None',
L: 'Low',
H: 'High',
},
VA: {
N: 'None',
L: 'Low',
H: 'High',
},
SC: {
N: 'None',
L: 'Low',
H: 'High',
},
SI: {
N: 'None',
L: 'Low',
H: 'High',
},
SA: {
N: 'None',
L: 'Low',
H: 'High',
},
};

/**
* This function calculates the CVSS vector from a set of metrics.
Expand Down Expand Up @@ -92,3 +184,27 @@ export const calculateScoreSafely = cvssVector => {
return undefined;
}
};

/**
* This function parses a CVSS vector string and returns an object with the
* metrics as labels.
* @param {string} cvssVector - The CVSS vector to parse the metrics from.
* @returns {Record<string,string>} - An object with key-value pairs of the metrics.
*/
export const parseCvssV4MetricsFromVector = cvssVector => {
if (!isDefined(cvssVector) || cvssVector.trim().length === 0) {
return {};
}
let ret = {};
const metrics = processVector(cvssVector);

for (const metric in metrics) {
const value = metrics[metric];
if (
isDefined(cvss4MetricValueToLabels[metric]) &&
isDefined(cvss4MetricValueToLabels[metric][value])
)
ret[metric] = cvss4MetricValueToLabels[metric][value];
}
return ret;
};
9 changes: 8 additions & 1 deletion src/web/pages/cves/details.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,19 @@ const CVSS_PROPS = {
cvssAuthentication: _l('Authentication'),
cvssAttackVector: _l('Attack Vector'),
cvssAttackComplexity: _l('Attack Complexity'),
cvssAttackRequirements: _l('Attack Requirements'),
cvssPrivilegesRequired: _l('Privileges Required'),
cvssUserInteraction: _l('User Interaction'),
cvssScope: _l('Scope'),
cvssConfidentialityImpact: _l('Confidentiality Impact'),
cvssIntegrityImpact: _l('Integrity Impact'),
cvssAvailabilityImpact: _l('Availability Impact'),
cvssConfidentialityVS: _l('Vulnerable System Confidentiality Impact'),
cvssIntegrityVS: _l('Vulnerable System Integrity Impact'),
cvssAvailabilityVS: _l('Vulnerable System Availability Impact'),
cvssConfidentialitySS: _l('Subsequent System Confidentiality Impact'),
cvssIntegritySS: _l('Subsequent System Integrity Impact'),
cvssAvailabilitySS: _l('Subsequent System Availability Impact'),
};

const CveDetails = ({entity}) => {
Expand Down Expand Up @@ -78,7 +85,7 @@ const CveDetails = ({entity}) => {
.map(([name, title]) => (
<TableRow key={name}>
<TableData>{`${title}`}</TableData>
<TableData>{entity[name]}</TableData>
<TableData>{_(entity[name])}</TableData>
</TableRow>
))}
</TableBody>
Expand Down
7 changes: 2 additions & 5 deletions src/web/pages/extras/cvssV4/CvssV4Calculator.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@ import PropTypes from 'prop-types';
import {useState, useEffect, useMemo} from 'react';
import _ from 'gmp/locale';

import {cvssConfigData} from 'web/pages/extras/cvssV4/cvssConfig';
import {
cvssConfigData,
expectedMetricOptionsOrdered,
} from 'web/pages/extras/cvssV4/cvssConfig';

import {
processVector,
calculateScoreSafely,
removeUnusedMetrics,
} from './utils';
} from 'gmp/parser/cvssV4';

import useUserSessionTimeout from 'web/utils/useUserSessionTimeout';
import FormGroup from 'web/components/form/formgroup';
Expand Down
35 changes: 0 additions & 35 deletions src/web/pages/extras/cvssV4/cvssConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -340,38 +340,3 @@ export const cvssConfigData = {
},
},
};

export const expectedMetricOptionsOrdered = [
['AV', 'N', 'A', 'L', 'P'],
['AC', 'L', 'H'],
['AT', 'N', 'P'],
['PR', 'N', 'L', 'H'],
['UI', 'N', 'P', 'A'],
['VC', 'N', 'L', 'H'],
['VI', 'N', 'L', 'H'],
['VA', 'N', 'L', 'H'],
['SC', 'N', 'L', 'H'],
['SI', 'N', 'L', 'H'],
['SA', 'N', 'L', 'H'],
['E', 'X', 'A', 'P', 'U'],
['CR', 'X', 'H', 'M', 'L'],
['IR', 'X', 'H', 'M', 'L'],
['AR', 'X', 'H', 'M', 'L'],
['MAV', 'X', 'N', 'A', 'L', 'P'],
['MAC', 'X', 'L', 'H'],
['MAT', 'X', 'N', 'P'],
['MPR', 'X', 'N', 'L', 'H'],
['MUI', 'X', 'N', 'P', 'A'],
['MVC', 'X', 'H', 'L', 'N'],
['MVI', 'X', 'H', 'L', 'N'],
['MVA', 'X', 'H', 'L', 'N'],
['MSC', 'X', 'H', 'L', 'N'],
['MSI', 'X', 'S', 'H', 'L', 'N'],
['MSA', 'X', 'S', 'H', 'L', 'N'],
['S', 'X', 'N', 'P'],
['AU', 'X', 'N', 'Y'],
['R', 'X', 'A', 'U', 'I'],
['V', 'X', 'D', 'C'],
['RE', 'X', 'L', 'M', 'H'],
['U', 'X', 'Clear', 'Green', 'Amber', 'Red'],
];

0 comments on commit 126712d

Please sign in to comment.