From 87faa4ea07a3cf10a4634df71912f82207e9c852 Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Fri, 10 Jan 2025 12:13:54 +0100 Subject: [PATCH 1/5] change: cve.js for new data structure --- src/gmp/models/cve.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/gmp/models/cve.js b/src/gmp/models/cve.js index 3a7227ee04..f80098313e 100644 --- a/src/gmp/models/cve.js +++ b/src/gmp/models/cve.js @@ -160,6 +160,39 @@ class Cve extends Info { ret.references = []; } + if (isDefined(element.cve?.references?.reference)) { + ret.references = map(element.cve.references.reference, ref => { + let tags = []; + if (isArray(ref.tags.tag)) { + tags = ref.tags.tag; + } else if (isDefined(ref.tags.tag)) { + tags = [ref.tags.tag]; + } + return { + name: ref.url, + tags: tags, + }; + }); + } + + if (isDefined(element.cve?.configuration_nodes?.node)) { + const nodes = isArray(element.cve.configuration_nodes.node) + ? element.cve.configuration_nodes.node + : [element.cve.configuration_nodes.node]; + nodes.forEach(node => { + if (isDefined(node.match_string?.matched_cpes?.cpe)) { + const cpes = isArray(node.match_string.matched_cpes.cpe) + ? node.match_string.matched_cpes.cpe + : [node.match_string.matched_cpes.cpe]; + cpes.forEach(cpe => { + if (isDefined(cpe._id)) { + ret.products.push(cpe._id); + } + }); + } + }); + } + return ret; } } From 5af0ebbfcd64731bb8ee34193e6af88fbeb02af1 Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Thu, 16 Jan 2025 08:18:47 +0100 Subject: [PATCH 2/5] change: adapt cpe and cve to new data structure --- src/gmp/models/cpe.js | 15 ++++++--- src/gmp/models/cve.js | 78 ++++++++++++++++++++++++------------------- 2 files changed, 54 insertions(+), 39 deletions(-) diff --git a/src/gmp/models/cpe.js b/src/gmp/models/cpe.js index 270a519a0b..2f50389b4d 100644 --- a/src/gmp/models/cpe.js +++ b/src/gmp/models/cpe.js @@ -39,12 +39,17 @@ class Cpe extends Info { delete ret.update_time; } - if (isDefined(ret.raw_data) && isDefined(ret.raw_data['cpe-item'])) { - const cpeItem = ret.raw_data['cpe-item']; - if (isDefined(cpeItem._deprecated_by)) { - ret.deprecatedBy = cpeItem._deprecated_by; - } + /* + * This code includes a backup check for deprecated field `raw_data`. + * Once `raw_data` is removed from the API, this backup check can be removed. + */ + + if (ret.deprecate === 1 && isDefined(ret.deprecated_by)) { + ret.deprecatedBy = ret.deprecated_by; + } else if (isDefined(ret.raw_data?.['cpe-item']?._deprecated_by)) { + ret.deprecatedBy = ret.raw_data['cpe-item']._deprecated_by; } + return ret; } } diff --git a/src/gmp/models/cve.js b/src/gmp/models/cve.js index f80098313e..3cecc8c485 100644 --- a/src/gmp/models/cve.js +++ b/src/gmp/models/cve.js @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import {parseSeverity, parseDate, setProperties} from 'gmp/parser'; +import {parseDate, parseSeverity, setProperties} from 'gmp/parser'; import { parseCvssV2BaseFromVector, parseCvssV3BaseFromVector, @@ -116,10 +116,16 @@ class Cve extends Info { ret.cvssBaseVector = ret.cvss_vector; ret.products = isEmpty(ret.products) ? [] : ret.products.split(' '); + /* + * This code includes a check for deprecated field `raw_data`. + * Once `raw_data` is removed from the API, this check can be removed. + */ + if (isDefined(ret.raw_data) && isDefined(ret.raw_data.entry)) { const {entry} = ret.raw_data; ret.publishedTime = parseDate(entry['published-datetime']); + ret.lastModifiedTime = parseDate(entry['last-modified-datetime']); ret.references = map(entry.references, ref => ({ @@ -154,43 +160,47 @@ class Cve extends Info { ret.products = []; } } - delete ret.raw_data; } else { - ret.references = []; - } + ret.publishedTime = parseDate(ret['published-datetime']); + ret.lastModifiedTime = parseDate(ret['last-modified-datetime']); - if (isDefined(element.cve?.references?.reference)) { - ret.references = map(element.cve.references.reference, ref => { - let tags = []; - if (isArray(ref.tags.tag)) { - tags = ref.tags.tag; - } else if (isDefined(ref.tags.tag)) { - tags = [ref.tags.tag]; - } - return { - name: ref.url, - tags: tags, - }; - }); - } + ret.references = []; + if (isDefined(element.cve?.references?.reference)) { + ret.references = map(element.cve.references.reference, ref => { + let tags = []; + if (isArray(ref.tags.tag)) { + tags = ref.tags.tag; + } else if (isDefined(ref.tags.tag)) { + tags = [ref.tags.tag]; + } + return { + name: ref.url, + tags: tags, + }; + }); + } - if (isDefined(element.cve?.configuration_nodes?.node)) { - const nodes = isArray(element.cve.configuration_nodes.node) - ? element.cve.configuration_nodes.node - : [element.cve.configuration_nodes.node]; - nodes.forEach(node => { - if (isDefined(node.match_string?.matched_cpes?.cpe)) { - const cpes = isArray(node.match_string.matched_cpes.cpe) - ? node.match_string.matched_cpes.cpe - : [node.match_string.matched_cpes.cpe]; - cpes.forEach(cpe => { - if (isDefined(cpe._id)) { - ret.products.push(cpe._id); - } - }); - } - }); + if ( + ret.products.length === 0 && + isDefined(element.cve?.configuration_nodes?.node) + ) { + const nodes = isArray(element.cve.configuration_nodes.node) + ? element.cve.configuration_nodes.node + : [element.cve.configuration_nodes.node]; + nodes.forEach(node => { + if (isDefined(node.match_string?.matched_cpes?.cpe)) { + const cpes = isArray(node.match_string.matched_cpes.cpe) + ? node.match_string.matched_cpes.cpe + : [node.match_string.matched_cpes.cpe]; + cpes.forEach(cpe => { + if (isDefined(cpe._id)) { + ret.products.push(cpe._id); + } + }); + } + }); + } } return ret; From 0cfcc41a7e18b8533c1018af3cbf7241611f5dca Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Thu, 16 Jan 2025 12:00:40 +0100 Subject: [PATCH 3/5] adjust cpe and cve --- src/gmp/models/cpe.js | 4 +- src/gmp/models/cve.js | 132 +++++++++++++---------------- src/web/pages/cves/detailspage.jsx | 15 +++- 3 files changed, 72 insertions(+), 79 deletions(-) diff --git a/src/gmp/models/cpe.js b/src/gmp/models/cpe.js index 2f50389b4d..6d51357030 100644 --- a/src/gmp/models/cpe.js +++ b/src/gmp/models/cpe.js @@ -44,8 +44,8 @@ class Cpe extends Info { * Once `raw_data` is removed from the API, this backup check can be removed. */ - if (ret.deprecate === 1 && isDefined(ret.deprecated_by)) { - ret.deprecatedBy = ret.deprecated_by; + if (ret.deprecated === 1 && isDefined(ret.deprecated_by)) { + ret.deprecatedBy = ret.deprecated_by._cpe_id; } else if (isDefined(ret.raw_data?.['cpe-item']?._deprecated_by)) { ret.deprecatedBy = ret.raw_data['cpe-item']._deprecated_by; } diff --git a/src/gmp/models/cve.js b/src/gmp/models/cve.js index 3cecc8c485..144c65c3ce 100644 --- a/src/gmp/models/cve.js +++ b/src/gmp/models/cve.js @@ -117,89 +117,73 @@ class Cve extends Info { ret.products = isEmpty(ret.products) ? [] : ret.products.split(' '); /* - * This code includes a check for deprecated field `raw_data`. + * The following code blocks for published-datetime, last-modified-datetime, products, and references + * include a backup check for deprecated field `raw_data`. * Once `raw_data` is removed from the API, this check can be removed. */ - if (isDefined(ret.raw_data) && isDefined(ret.raw_data.entry)) { - const {entry} = ret.raw_data; - - ret.publishedTime = parseDate(entry['published-datetime']); - - ret.lastModifiedTime = parseDate(entry['last-modified-datetime']); - - ret.references = map(entry.references, ref => ({ - name: ref.reference.__text, - href: ref.reference._href, + ret.publishedTime = parseDate( + ret['creationTime'] ?? ret.raw_data?.entry?.['published-datetime'], + ); + ret.lastModifiedTime = parseDate( + ret['modificationTime'] ?? + ret.raw_data?.entry?.['last-modified-datetime'], + ); + + ret.references = []; + if (isDefined(element.cve.references?.reference)) { + ret.references = map(element.cve.references.reference, ref => { + let tags = []; + if (isArray(ref.tags.tag)) { + tags = ref.tags.tag; + } else if (isDefined(ref.tags.tag)) { + tags = [ref.tags.tag]; + } + return { + name: ref.url, + tags: tags, + }; + }); + } else { + const {entry} = ret.raw_data ?? {}; + const referencesList = entry?.references || []; + ret.references = map(referencesList, ref => ({ + name: ref.reference?.__text, + href: ref.reference?._href, source: ref.source, reference_type: ref._reference_type, })); + } - if ( - isDefined(entry.cvss) && - isDefined(entry.cvss.base_metrics) && - isDefined(entry.cvss.base_metrics.source) - ) { - ret.source = entry.cvss.base_metrics.source; - } - - if (isDefined(entry.summary)) { - // really don't know why entry.summary and ret.description can differ - // but xslt did use the summary and and e.g. the description of - // CVE-2017-2988 was empty but summary not - ret.description = entry.summary; - } - - const products = entry['vulnerable-software-list']; - if (isDefined(products)) { - if (isDefined(products.product)) { - ret.products = isArray(products.product) - ? products.product - : [products.product]; - } else { - ret.products = []; + if ( + ret.products.length === 0 && + isDefined(element.cve?.configuration_nodes?.node) + ) { + const nodes = isArray(element.cve.configuration_nodes.node) + ? element.cve.configuration_nodes.node + : [element.cve.configuration_nodes.node]; + nodes.forEach(node => { + if ( + node.match_string?.vulnerable === 1 && + isDefined(node.match_string?.matched_cpes?.cpe) + ) { + const cpes = isArray(node.match_string.matched_cpes.cpe) + ? node.match_string.matched_cpes.cpe + : [node.match_string.matched_cpes.cpe]; + cpes.forEach(cpe => { + if (isDefined(cpe._id)) { + ret.products.push(cpe._id); + } + }); } - } - delete ret.raw_data; + }); } else { - ret.publishedTime = parseDate(ret['published-datetime']); - ret.lastModifiedTime = parseDate(ret['last-modified-datetime']); - - ret.references = []; - if (isDefined(element.cve?.references?.reference)) { - ret.references = map(element.cve.references.reference, ref => { - let tags = []; - if (isArray(ref.tags.tag)) { - tags = ref.tags.tag; - } else if (isDefined(ref.tags.tag)) { - tags = [ref.tags.tag]; - } - return { - name: ref.url, - tags: tags, - }; - }); - } - - if ( - ret.products.length === 0 && - isDefined(element.cve?.configuration_nodes?.node) - ) { - const nodes = isArray(element.cve.configuration_nodes.node) - ? element.cve.configuration_nodes.node - : [element.cve.configuration_nodes.node]; - nodes.forEach(node => { - if (isDefined(node.match_string?.matched_cpes?.cpe)) { - const cpes = isArray(node.match_string.matched_cpes.cpe) - ? node.match_string.matched_cpes.cpe - : [node.match_string.matched_cpes.cpe]; - cpes.forEach(cpe => { - if (isDefined(cpe._id)) { - ret.products.push(cpe._id); - } - }); - } - }); + const productsEntry = + ret.raw_data?.entry?.['vulnerable-software-list']?.product; + if (productsEntry) { + ret.products = isArray(productsEntry) ? productsEntry : [productsEntry]; + } else { + ret.products = []; } } diff --git a/src/web/pages/cves/detailspage.jsx b/src/web/pages/cves/detailspage.jsx index 78e796934b..aefdcf5e4e 100644 --- a/src/web/pages/cves/detailspage.jsx +++ b/src/web/pages/cves/detailspage.jsx @@ -4,6 +4,7 @@ */ import _ from 'gmp/locale'; +import {isDefined} from 'gmp/utils/identity'; import React from 'react'; import DateTime from 'web/components/date/datetime'; import CveIcon from 'web/components/icon/cveicon'; @@ -144,15 +145,23 @@ const EntityInfo = ({entity}) => {
{id}
{_('Published:')}
- + {isDefined(publishedTime) ? ( + + ) : ( + _('N/A') + )}
{_('Modified:')}
- + {isDefined(updateTime) ? : _('N/A')}
{_('Last updated:')}
- + {isDefined(lastModifiedTime) ? ( + + ) : ( + _('N/A') + )}
); From 89ffd81f699effef1e92754db283d8e35a4d0b24 Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Thu, 16 Jan 2025 13:42:53 +0100 Subject: [PATCH 4/5] update products parsing and test --- src/gmp/models/__tests__/cve.js | 49 --------------------------------- src/gmp/models/cve.js | 8 ++++-- 2 files changed, 5 insertions(+), 52 deletions(-) diff --git a/src/gmp/models/__tests__/cve.js b/src/gmp/models/__tests__/cve.js index 9411b27a1a..48d087e66b 100644 --- a/src/gmp/models/__tests__/cve.js +++ b/src/gmp/models/__tests__/cve.js @@ -140,7 +140,6 @@ describe('CVE model tests', () => { }, }; const cve = Cve.fromElement(elem); - expect(cve.products).toEqual(['foo:bar/dolor', 'ipsum:lorem']); }); @@ -194,40 +193,6 @@ describe('CVE model tests', () => { expect(cve.references).toEqual(res); }); - test('should parse cvss source', () => { - const elem = { - raw_data: { - entry: { - 'published-datetime': '2018-10-10T11:41:23.022Z', - 'last-modified-datetime': '2018-10-10T11:41:23.022Z', - cvss: { - base_metrics: { - source: 'prot://url', - }, - }, - }, - }, - }; - const cve = Cve.fromElement(elem); - - expect(cve.source).toEqual('prot://url'); - }); - - test('should parse summary', () => { - const elem = { - raw_data: { - entry: { - 'published-datetime': '2018-10-10T11:41:23.022Z', - 'last-modified-datetime': '2018-10-10T11:41:23.022Z', - summary: 'lorem ipsum', - }, - }, - }; - const cve = Cve.fromElement(elem); - - expect(cve.description).toEqual('lorem ipsum'); - }); - test('should parse vulnerable products from raw data', () => { const elem = { raw_data: { @@ -245,20 +210,6 @@ describe('CVE model tests', () => { expect(cve.products).toEqual(['lorem', 'ipsum']); }); - test('should delete raw data', () => { - const elem = { - raw_data: { - entry: { - 'published-datetime': {__text: '2018-10-10T11:41:23.022Z'}, - 'last-modified-datetime': {__text: '2018-10-10T11:41:23.022Z'}, - }, - }, - }; - const cve = Cve.fromElement(elem); - - expect(cve.raw_data).toBeUndefined(); - }); - test('should return empty array for references if no raw data is given', () => { const cve = Cve.fromElement({}); diff --git a/src/gmp/models/cve.js b/src/gmp/models/cve.js index 144c65c3ce..cc0eb4706e 100644 --- a/src/gmp/models/cve.js +++ b/src/gmp/models/cve.js @@ -114,6 +114,7 @@ class Cve extends Info { } ret.cvssBaseVector = ret.cvss_vector; + ret.products = isEmpty(ret.products) ? [] : ret.products.split(' '); /* @@ -131,7 +132,7 @@ class Cve extends Info { ); ret.references = []; - if (isDefined(element.cve.references?.reference)) { + if (element.cve && isDefined(element.cve.references?.reference)) { ret.references = map(element.cve.references.reference, ref => { let tags = []; if (isArray(ref.tags.tag)) { @@ -156,6 +157,7 @@ class Cve extends Info { } if ( + ret.products && ret.products.length === 0 && isDefined(element.cve?.configuration_nodes?.node) ) { @@ -182,11 +184,11 @@ class Cve extends Info { ret.raw_data?.entry?.['vulnerable-software-list']?.product; if (productsEntry) { ret.products = isArray(productsEntry) ? productsEntry : [productsEntry]; - } else { - ret.products = []; } } + ret.products = ret.products.filter(product => product !== ''); + return ret; } } From 2c595be797e15b893aeb5cb27fb56a827fa6c1ba Mon Sep 17 00:00:00 2001 From: daniele-mng Date: Thu, 16 Jan 2025 13:55:23 +0100 Subject: [PATCH 5/5] adjust reference link url --- src/gmp/models/cve.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gmp/models/cve.js b/src/gmp/models/cve.js index cc0eb4706e..5c90b12d73 100644 --- a/src/gmp/models/cve.js +++ b/src/gmp/models/cve.js @@ -143,6 +143,7 @@ class Cve extends Info { return { name: ref.url, tags: tags, + href: ref.url, }; }); } else {