From fdc2c9b4f6ab689fa39f1da4f3882ec061ccc1f9 Mon Sep 17 00:00:00 2001 From: david-rocca <davidtrocca@gmail.com> Date: Wed, 18 Dec 2024 10:26:52 -0500 Subject: [PATCH 1/7] Added tests for erlcheck parameter --- test/integration-tests/constants.js | 109 ++++++++++++++++++ test/integration-tests/cve-id/getCveIdTest.js | 2 +- .../integration-tests/cve/erlCheckPOSTTest.js | 82 +++++++++++++ test/integration-tests/cve/erlCheckPUTTest.js | 83 +++++++++++++ 4 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 test/integration-tests/cve/erlCheckPOSTTest.js create mode 100644 test/integration-tests/cve/erlCheckPUTTest.js diff --git a/test/integration-tests/constants.js b/test/integration-tests/constants.js index 87ef53c09..292602f14 100644 --- a/test/integration-tests/constants.js +++ b/test/integration-tests/constants.js @@ -241,6 +241,114 @@ const testAdp2 = { } } +const enrichedCve = { + + cnaContainer: { + affected: [ + { + vendor: 'n/da', + product: 'n/a', + versions: [ + { + version: 'n/a', + status: 'unknown' + } + ] + } + ], + descriptions: [ + { + lang: 'en', + value: "Cross-site scdfgfdgripting (XSS) vulnerability in Revive Adserver before 4.0.1 allows remote authenticated users to inject arbitrary web script or HTML via the user's email address." + } + ], + problemTypes: [ + { + descriptions: [ + { + description: 'n/a', + lang: 'eng', + type: 'text', + cweId: 'CWE-79' + } + ] + } + ], + providerMetadata: { + orgId: '9cbfeea8-dea2-4923-b772-1ab41730e742' + }, + references: [ + { + name: '[oss-security] 20170202 Re: CVE request: multiples vulnerabilities in Revive Adserver', + url: 'http://www.openwall.com/lists/oss-security/2017/02/02/3' + }, + { + name: 'https://www.revive-adserver.com/security/revive-sa-2017-001/', + url: 'https://www.revive-adserver.com/security/revive-sa-2017-001/' + }, + { + name: '95dsf875', + url: 'http://www.securityfocus.com/bid/95875' + } + ], + metrics: [ + { + format: 'CVSS', + scenarios: [ + { + lang: 'en', + value: 'GENERAL' + } + ], + cvssV4_0: { + baseScore: 7.8, + baseSeverity: 'HIGH', + vectorString: 'CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:L/SA:L', + version: '4.0' + }, + cvssV3_1: { + version: '3.1', + attackVector: 'NETWORK', + attackComplexity: 'LOW', + privilegesRequired: 'NONE', + userInteraction: 'NONE', + scope: 'UNCHANGED', + confidentialityImpact: 'HIGH', + integrityImpact: 'HIGH', + availabilityImpact: 'HIGH', + baseScore: 9.8, + baseSeverity: 'CRITICAL', + vectorString: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H' + } + }, + { + format: 'CVSS', + scenarios: [ + { + lang: 'en', + value: "If the enhanced host protection mode is turned on, this vulnerability can only be exploited to run os commands as user 'nobody'. Privilege escalation is not possible." + } + ], + cvssV3_1: { + version: '3.1', + attackVector: 'NETWORK', + attackComplexity: 'LOW', + privilegesRequired: 'NONE', + userInteraction: 'NONE', + scope: 'UNCHANGED', + confidentialityImpact: 'LOW', + integrityImpact: 'LOW', + availabilityImpact: 'LOW', + baseScore: 7.3, + baseSeverity: 'HIGH', + vectorString: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L' + } + } + ] + } + +} + const testOrg = { short_name: 'test_org', @@ -278,6 +386,7 @@ module.exports = { nonSecretariatUserHeadersWithAdp2, testCve, testCveEdited, + enrichedCve, testAdp, testAdp2, testOrg, diff --git a/test/integration-tests/cve-id/getCveIdTest.js b/test/integration-tests/cve-id/getCveIdTest.js index 3209b822e..41dc671e0 100644 --- a/test/integration-tests/cve-id/getCveIdTest.js +++ b/test/integration-tests/cve-id/getCveIdTest.js @@ -12,7 +12,7 @@ const app = require('../../../src/index.js') describe('Testing Get CVE-ID endpoint', () => { // TODO: Update this test to dynamically calculate reserved count. - const RESESRVED_COUNT = 120 + const RESESRVED_COUNT = 122 const YEAR_COUNT = 10 const PUB_YEAR_COUNT = 4 const TIME_WINDOW_COUNT = 40 diff --git a/test/integration-tests/cve/erlCheckPOSTTest.js b/test/integration-tests/cve/erlCheckPOSTTest.js new file mode 100644 index 000000000..b1df9a9a2 --- /dev/null +++ b/test/integration-tests/cve/erlCheckPOSTTest.js @@ -0,0 +1,82 @@ +/* eslint-disable no-unused-expressions */ + +const chai = require('chai') +chai.use(require('chai-http')) + +const expect = chai.expect + +const constants = require('../constants.js') +const app = require('../../../src/index.js') +const helpers = require('../helpers.js') + +const _ = require('lodash') + +const requestLength = 1 +const shortName = 'win_5' +const cveYear = '2023' +const batchType = 'non-sequential' + +describe('Testing POST ERLCheck field', () => { + let cveId + beforeEach(async () => { + cveId = await helpers.cveIdReserveHelper(requestLength, cveYear, shortName, batchType) + }) + context('ERL POST Check Tests', () => { + it('POST CVE that is ERL Checked with correct details', async () => { + await chai.request(app) + .post(`/api/cve/${cveId}/cna?erlcheck=true`) + .set(constants.nonSecretariatUserHeaders) + .send(constants.enrichedCve) + .then((res, err) => { + // Safety Expect + expect(err).to.be.undefined + expect(res).to.have.status(200) + }) + }) + + it('POST CVE that is ERL checked with the incorrect details', async () => { + await chai.request(app) + .post(`/api/cve/${cveId}/cna?erlcheck=true`) + .set(constants.nonSecretariatUserHeaders) + .send(constants.testCve) + .then((res, err) => { + // Safety Expect + expect(res).to.have.status(403) + }) + }) + + it('POST CVE that is ERL is false with correct details', async () => { + await chai.request(app) + .post(`/api/cve/${cveId}/cna?erlcheck=false`) + .set(constants.nonSecretariatUserHeaders) + .send(constants.enrichedCve) + .then((res, err) => { + // Safety Expect + expect(err).to.be.undefined + expect(res).to.have.status(200) + }) + }) + + it('POST CVE that is ERL is false with the incorrect details', async () => { + await chai.request(app) + .post(`/api/cve/${cveId}/cna?erlcheck=false`) + .set(constants.nonSecretariatUserHeaders) + .send(constants.testCve) + .then((res, err) => { + // Safety Expect + expect(res).to.have.status(200) + }) + }) + + it('POST CVE that is ERL is null with the incorrect details', async () => { + await chai.request(app) + .post(`/api/cve/${cveId}/cna?erlcheck=null`) + .set(constants.nonSecretariatUserHeaders) + .send(constants.testCve) + .then((res, err) => { + // Safety Expect + expect(res).to.have.status(400) + }) + }) + }) +}) diff --git a/test/integration-tests/cve/erlCheckPUTTest.js b/test/integration-tests/cve/erlCheckPUTTest.js new file mode 100644 index 000000000..bec8bd1c6 --- /dev/null +++ b/test/integration-tests/cve/erlCheckPUTTest.js @@ -0,0 +1,83 @@ +/* eslint-disable no-unused-expressions */ + +const chai = require('chai') +chai.use(require('chai-http')) + +const expect = chai.expect + +const constants = require('../constants.js') +const app = require('../../../src/index.js') +const helpers = require('../helpers.js') + +const requestLength = 1 +const shortName = 'win_5' +const cveYear = '2023' +const batchType = 'non-sequential' + +describe('Testing PUT ERLCheck field', () => { + let cveId + before(async () => { + cveId = await helpers.cveIdReserveHelper(requestLength, cveYear, shortName, batchType) + await helpers.cveRequestAsCnaHelper(cveId) + console.log('HEre') + }) + context('ERL PUT Check Tests', () => { + it('PUT CVE that is ERL Checked with correct details', async () => { + await chai.request(app) + .put(`/api/cve/${cveId}/cna?erlcheck=true`) + .set(constants.nonSecretariatUserHeaders) + .send(constants.enrichedCve) + .then((res, err) => { + // Safety Expect + expect(err).to.be.undefined + expect(res).to.have.status(200) + }) + }) + + it('PUT CVE that is ERL checked with the incorrect details', async () => { + await chai.request(app) + .put(`/api/cve/${cveId}/cna?erlcheck=true`) + .set(constants.nonSecretariatUserHeaders) + .send(constants.testCve) + .then((res, err) => { + // Safety Expect + expect(res).to.have.status(403) + }) + }) + + it('PUT CVE that is ERL is false with correct details', async () => { + await chai.request(app) + .put(`/api/cve/${cveId}/cna?erlcheck=false`) + .set(constants.nonSecretariatUserHeaders) + .send(constants.enrichedCve) + .then((res, err) => { + // Safety Expect + expect(err).to.be.undefined + expect(res).to.have.status(200) + }) + }) + + it('PUT CVE that is ERL is false with the incorrect details', async () => { + await chai.request(app) + .put(`/api/cve/${cveId}/cna?erlcheck=false`) + .set(constants.nonSecretariatUserHeaders) + .send(constants.testCve) + .then((res, err) => { + // Safety Expect + expect(res).to.have.status(200) + }) + }) + + it('PUT CVE that is ERL is null with correct details', async () => { + await chai.request(app) + .put(`/api/cve/${cveId}/cna?erlcheck=null`) + .set(constants.nonSecretariatUserHeaders) + .send(constants.enrichedCve) + .then((res, err) => { + // Safety Expect + expect(err).to.be.undefined + expect(res).to.have.status(400) + }) + }) + }) +}) From 571b68f5c5951d643df1d5e2494164229cae3850 Mon Sep 17 00:00:00 2001 From: david-rocca <davidtrocca@gmail.com> Date: Wed, 18 Dec 2024 11:08:17 -0500 Subject: [PATCH 2/7] Fix dead links in swagger, also updated documentation --- api-docs/openapi.json | 27 +++++++++++++++------------ src/swagger.js | 15 ++++++++++++--- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/api-docs/openapi.json b/api-docs/openapi.json index 0169e993e..b7e573aa5 100644 --- a/api-docs/openapi.json +++ b/api-docs/openapi.json @@ -1,12 +1,12 @@ { "openapi": "3.0.2", "info": { - "version": "2.4.0", + "version": "2.5.0", "title": "CVE Services API", - "description": "The CVE Services API supports automation tooling for the CVE Program. Credentials are required for most service endpoints. Representatives of <a href='https://www.cve.org/ProgramOrganization/CNAs'>CVE Numbering Authorities (CNAs)</a> should use one of the methods below to obtain credentials: <ul><li>If your organization already has an Organizational Administrator (OA) account for the CVE Services, ask your admin for credentials</li> <li>Contact your Root (<a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/Google'>Google</a>, <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/INCIBE'>INCIBE</a>, <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/jpcert'>JPCERT/CC</a>, or <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/redhat'>Red Hat</a>) or Top-Level Root (<a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/icscert'>CISA ICS</a> or <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/mitre'>MITRE</a>) to request credentials </ul> <p>CVE data is to be in the JSON 5.1 CVE Record format. Details of the JSON 5.1 schema are located <a href='https://github.com/CVEProject/cve-schema/tree/5.1.1-rc2/schema' target='_blank'>here</a>.</p> <a href='https://cveform.mitre.org/' class='link' target='_blank'>Contact the CVE Services team</a>", + "description": "The CVE Services API supports automation tooling for the CVE Program. Credentials are required for most service endpoints. Representatives of <a href='https://www.cve.org/ProgramOrganization/CNAs'>CVE Numbering Authorities (CNAs)</a> should use one of the methods below to obtain credentials: <ul><li>If your organization already has an Organizational Administrator (OA) account for the CVE Services, ask your admin for credentials</li> <li>Contact your Root (<a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/Google'>Google</a>, <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/INCIBE'>INCIBE</a>, <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/jpcert'>JPCERT/CC</a>, or <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/redhat'>Red Hat</a>) or Top-Level Root (<a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/icscert'>CISA ICS</a> or <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/mitre'>MITRE</a>) to request credentials </ul> <p>CVE data is to be in the JSON 5.1 CVE Record format. Details of the JSON 5.1 schema are located <a href='https://github.com/CVEProject/cve-schema/tree/v5.1.1-rc2/schema' target='_blank'>here</a>.</p> <a href='https://cveform.mitre.org/' class='link' target='_blank'>Contact the CVE Services team</a>", "contact": { "name": "CVE Services Overview", - "url": "https://cveproject.github.io/automation-cve-services#services-overview" + "url": "https://www.cve.org/AllResources/CveServices" } }, "servers": [ @@ -1323,6 +1323,9 @@ }, { "$ref": "#/components/parameters/apiSecretHeader" + }, + { + "$ref": "#/components/parameters/erlCheck" } ], "responses": { @@ -1423,15 +1426,6 @@ "type": "string" }, "description": "The CVE ID for which the record is being updated" - }, - { - "$ref": "#/components/parameters/apiEntityHeader" - }, - { - "$ref": "#/components/parameters/apiUserHeader" - }, - { - "$ref": "#/components/parameters/apiSecretHeader" } ], "responses": { @@ -3036,6 +3030,15 @@ "type": "string" } }, + "erlCheck": { + "in": "query", + "name": "erlcheck", + "description": "Enables stricter validation that ensures submitted record meets enrichment data requirements", + "required": false, + "schema": { + "type": "boolean" + } + }, "batch_type": { "in": "query", "name": "batch_type", diff --git a/src/swagger.js b/src/swagger.js index 29cdbc7f4..630c0a2c8 100644 --- a/src/swagger.js +++ b/src/swagger.js @@ -18,7 +18,7 @@ const fullCnaContainerRequest = require('../schemas/cve/create-cve-record-cna-re /* eslint-disable no-multi-str */ const doc = { info: { - version: '2.4.0', + version: '2.5.0', title: 'CVE Services API', description: "The CVE Services API supports automation tooling for the CVE Program. Credentials are \ required for most service endpoints. Representatives of \ @@ -34,11 +34,11 @@ const doc = { or <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/mitre'>MITRE</a>) to request credentials \ </ul> \ <p>CVE data is to be in the JSON 5.1 CVE Record format. Details of the JSON 5.1 schema are \ - located <a href='https://github.com/CVEProject/cve-schema/tree/5.1.1-rc2/schema' target='_blank'>here</a>.</p>\ + located <a href='https://github.com/CVEProject/cve-schema/tree/v5.1.1-rc2/schema' target='_blank'>here</a>.</p>\ <a href='https://cveform.mitre.org/' class='link' target='_blank'>Contact the CVE Services team</a>", contact: { name: 'CVE Services Overview', - url: 'https://cveproject.github.io/automation-cve-services#services-overview' + url: 'https://www.cve.org/AllResources/CveServices' } }, @@ -168,6 +168,15 @@ const doc = { type: 'string' } }, + erlCheck: { + in: 'query', + name: 'erlcheck', + description: 'Enables stricter validation that ensures submitted record meets enrichment data requirements', + required: false, + schema: { + type: 'boolean' + } + }, batch_type: { in: 'query', name: 'batch_type', From d08825bf6fe1a935b0cbcffc7ae905af7fd7d9ba Mon Sep 17 00:00:00 2001 From: david-rocca <davidtrocca@gmail.com> Date: Wed, 18 Dec 2024 11:11:37 -0500 Subject: [PATCH 3/7] Added erl check functionality --- .../cve.controller/cve.controller.js | 33 +++++++++++++++++-- src/controller/cve.controller/error.js | 7 ++++ src/controller/cve.controller/index.js | 8 ++++- src/middleware/errorMessages.js | 1 + src/utils/utils.js | 11 +++++++ 5 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/controller/cve.controller/cve.controller.js b/src/controller/cve.controller/cve.controller.js index d8e62db7a..0eba8b24f 100644 --- a/src/controller/cve.controller/cve.controller.js +++ b/src/controller/cve.controller/cve.controller.js @@ -4,6 +4,7 @@ const errors = require('./error') const getConstants = require('../../constants').getConstants const error = new errors.CveControllerError() const booleanIsTrue = require('../../utils/utils').booleanIsTrue +const isEnrichedContainer = require('../../utils/utils').isEnrichedContainer const url = process.env.NODE_ENV === 'staging' ? 'https://test.cve.org/' : 'https://cve.org/' // Helper function to create providerMetadata object @@ -458,6 +459,14 @@ async function submitCna (req, res, next) { const orgUuid = await orgRepo.getOrgUUID(req.ctx.org) const userUuid = await userRepo.getUserUUID(req.ctx.user, orgUuid) + // To avoid breaking legacy behavior in the "booleanIsTrue" function, we need to check to make sure that undefined is set to false + let erlCheck + if (typeof req.query.erlcheck === 'undefined') { + erlCheck = false + } else { + erlCheck = booleanIsTrue(req.query.erlcheck) || false + } + // check that cve id exists let result = await cveIdRepo.findOneByCveId(id) if (!result || result.state === CONSTANTS.CVE_STATES.AVAILABLE) { @@ -477,10 +486,15 @@ async function submitCna (req, res, next) { return res.status(403).json(error.cveRecordExists()) } + const cnaContainer = req.ctx.body.cnaContainer + if (erlCheck && !isEnrichedContainer(cnaContainer)) { + // Process the ERL check here + return res.status(403).json(error.erlCheckFailed()) + } + // create full cve record here const owningCna = await orgRepo.findOneByUUID(cveId.owning_cna) const assignerShortName = owningCna.short_name - const cnaContainer = req.ctx.body.cnaContainer const dateUpdated = (new Date()).toISOString() const additionalCveMetadataFields = { assignerShortName: assignerShortName, @@ -541,6 +555,16 @@ async function updateCna (req, res, next) { const orgUuid = await orgRepo.getOrgUUID(req.ctx.org) const userUuid = await userRepo.getUserUUID(req.ctx.user, orgUuid) + // To avoid breaking legacy behavior in the "booleanIsTrue" function, we need to check to make sure that undefined is set to false + let erlCheck + if (typeof req.query.erlcheck === 'undefined') { + erlCheck = false + } else { + erlCheck = booleanIsTrue(req.query.erlcheck) || false + } + + console.log('TEST') + // check that cve id exists let result = await cveIdRepo.findOneByCveId(id) if (!result || result.state === CONSTANTS.CVE_STATES.AVAILABLE) { @@ -560,9 +584,14 @@ async function updateCna (req, res, next) { return res.status(403).json(error.cveRecordDne()) } + const cnaContainer = req.ctx.body.cnaContainer + if (erlCheck && !isEnrichedContainer(cnaContainer)) { + // Process the ERL check here + return res.status(403).json(error.erlCheckFailed()) + } + // update cve record here const cveRecord = result.cve - const cnaContainer = req.ctx.body.cnaContainer const dateUpdated = (new Date()).toISOString() cveRecord.cveMetadata.dateUpdated = dateUpdated diff --git a/src/controller/cve.controller/error.js b/src/controller/cve.controller/error.js index a270f0c75..2ccd9b3df 100644 --- a/src/controller/cve.controller/error.js +++ b/src/controller/cve.controller/error.js @@ -111,6 +111,13 @@ class CveControllerError extends idrErr.IDRError { err.message = 'The ADP data does not begin with adpContainer.' return err } + + erlCheckFailed () { + const err = {} + err.error = 'ERL_CHECK_FAILED' + err.message = 'The ERL check failed. The CNA container is not enriched and the ERLCheck flag is set.' + return err + } } module.exports = { diff --git a/src/controller/cve.controller/index.js b/src/controller/cve.controller/index.js index d7ae0f586..78df94a1b 100644 --- a/src/controller/cve.controller/index.js +++ b/src/controller/cve.controller/index.js @@ -549,7 +549,8 @@ router.post('/cve/:id/cna', #swagger.parameters['$ref'] = [ '#/components/parameters/apiEntityHeader', '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' + '#/components/parameters/apiSecretHeader', + '#/components/parameters/erlCheck' ] #swagger.requestBody = { description: '<h3>Notes:</h3> @@ -620,6 +621,8 @@ router.post('/cve/:id/cna', mw.validateUser, mw.onlyCnas, mw.trimJSONWhitespace, + query().custom((query) => { return mw.validateQueryParameterNames(query, ['erlcheck']) }), + query(['erlcheck']).optional().isBoolean({ loose: true }).withMessage(errorMsgs.ERLCHECK), validateCveCnaContainerJsonSchema, validateUniqueEnglishEntry('cnaContainer.descriptions'), validateDescription(['cnaContainer.descriptions', 'cnaContainer.problemTypes[0].descriptions']), @@ -646,6 +649,7 @@ router.put('/cve/:id/cna', '#/components/parameters/apiEntityHeader', '#/components/parameters/apiUserHeader', '#/components/parameters/apiSecretHeader' + '#/components/parameters/erlCheck', ] #swagger.requestBody = { description: '<h3>Notes:</h3> @@ -717,6 +721,8 @@ router.put('/cve/:id/cna', mw.validateUser, mw.onlyCnas, mw.trimJSONWhitespace, + query().custom((query) => { return mw.validateQueryParameterNames(query, ['erlcheck']) }), + query(['erlcheck']).optional().isBoolean({ loose: true }).withMessage(errorMsgs.ERLCHECK), validateCveCnaContainerJsonSchema, validateUniqueEnglishEntry('cnaContainer.descriptions'), validateDescription(['cnaContainer.descriptions', 'cnaContainer.problemTypes[0].descriptions']), diff --git a/src/middleware/errorMessages.js b/src/middleware/errorMessages.js index 9aa7b9c5c..c7aa7db12 100644 --- a/src/middleware/errorMessages.js +++ b/src/middleware/errorMessages.js @@ -10,6 +10,7 @@ module.exports = { COUNT_ONLY: 'Invalid count_only value. Value should be 1, true, or yes to indicate true, or 0, false, or no to indicate false', TIMESTAMP_FORMAT: "Bad date, or invalid timestamp format: valid format is yyyy-MM-ddTHH:mm:ss or yyyy-MM-ddTHH:mm:ssZZ:ZZ (to use '+' in timezone offset, encode as '%2B). ZZ:ZZ (if used) must be between 00:00 and 23:59.", CNA_MODIFIED: 'Invalid cna_modified value. Value should be 1, true, or yes to indicate true, or 0, false, or no to indicate false', + ERLCHECK: 'Invalid erlcheck value. Value should be 1, true, or yes to indicate true, or 0, false, or no to indicate false', FIRSTNAME_LENGTH: 'Invalid name.first. Name must be between 1 and 100 characters in length.', LASTNAME_LENGTH: 'Invalid name.last. Name must be between 1 and 100 characters in length.', MIDDLENAME_LENGTH: 'Invalid name.middle. Name must be between 1 and 100 characters in length.', diff --git a/src/utils/utils.js b/src/utils/utils.js index f6ec5d2a1..68b31aa14 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -123,6 +123,7 @@ function reqCtxMapping (req, keyType, keys) { } // Return true if boolean is 0, true, or yes, with any mix of casing +// Please note that this function does NOT evaluate "undefined" as false. - A tired developer who lost way too much time to this. function booleanIsTrue (val) { if ((val.toString() === '1') || (val.toString().toLowerCase() === 'true') || @@ -153,12 +154,22 @@ function toDate (val) { return result } +function isEnrichedContainer (container) { + const hasCvss = container?.metrics?.some(item => 'cvssV4_0' in item || 'cvssV3_1' in item || 'cvssV3_0' in item || 'cvssV2_0' in item) + const hasCwe = container?.problemTypes?.some(pItem => pItem?.descriptions?.some(dItem => 'cweId' in dItem)) + if (!(hasCvss && hasCwe)) { + return false + } + return true +} + module.exports = { isSecretariat, isBulkDownload, isAdmin, isAdminUUID, isSecretariatUUID, + isEnrichedContainer, getOrgUUID, getUserUUID, reqCtxMapping, From 4d65984bad962f026fe0a87b5df5cd0442afd3c8 Mon Sep 17 00:00:00 2001 From: david-rocca <davidtrocca@gmail.com> Date: Wed, 18 Dec 2024 12:32:27 -0500 Subject: [PATCH 4/7] Fixed liniting test failure --- test/integration-tests/cve/erlCheckPOSTTest.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/integration-tests/cve/erlCheckPOSTTest.js b/test/integration-tests/cve/erlCheckPOSTTest.js index b1df9a9a2..32da1e905 100644 --- a/test/integration-tests/cve/erlCheckPOSTTest.js +++ b/test/integration-tests/cve/erlCheckPOSTTest.js @@ -9,8 +9,6 @@ const constants = require('../constants.js') const app = require('../../../src/index.js') const helpers = require('../helpers.js') -const _ = require('lodash') - const requestLength = 1 const shortName = 'win_5' const cveYear = '2023' From 1890bf5518e3f21a06e1ecf20f8880ca8553cf24 Mon Sep 17 00:00:00 2001 From: david-rocca <davidtrocca@gmail.com> Date: Wed, 18 Dec 2024 12:37:20 -0500 Subject: [PATCH 5/7] remove testing console.log --- src/controller/cve.controller/cve.controller.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/controller/cve.controller/cve.controller.js b/src/controller/cve.controller/cve.controller.js index 0eba8b24f..e112b233a 100644 --- a/src/controller/cve.controller/cve.controller.js +++ b/src/controller/cve.controller/cve.controller.js @@ -563,8 +563,6 @@ async function updateCna (req, res, next) { erlCheck = booleanIsTrue(req.query.erlcheck) || false } - console.log('TEST') - // check that cve id exists let result = await cveIdRepo.findOneByCveId(id) if (!result || result.state === CONSTANTS.CVE_STATES.AVAILABLE) { From 23033305877768c8dd4a11b01865d4b8651d078a Mon Sep 17 00:00:00 2001 From: david-rocca <davidtrocca@gmail.com> Date: Wed, 18 Dec 2024 12:45:23 -0500 Subject: [PATCH 6/7] update unit tests to respect the new query parameter --- test/unit-tests/cve/updateCnaTest.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/unit-tests/cve/updateCnaTest.js b/test/unit-tests/cve/updateCnaTest.js index 7b7beda39..b72cb009e 100644 --- a/test/unit-tests/cve/updateCnaTest.js +++ b/test/unit-tests/cve/updateCnaTest.js @@ -108,6 +108,9 @@ describe('updateCna function', () => { body: { cnaContainer: cnaContainerCopy } + }, + query: { + erlcheck: 'false' } } }) From f160f27f32f0b0b3482d079625626bed501ba4e9 Mon Sep 17 00:00:00 2001 From: david-rocca <davidtrocca@gmail.com> Date: Fri, 20 Dec 2024 10:40:50 -0500 Subject: [PATCH 7/7] Fixed missing comma, and made docs more detailed --- api-docs/openapi.json | 14 +++++++++++++- src/controller/cve.controller/index.js | 4 ++-- src/swagger.js | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/api-docs/openapi.json b/api-docs/openapi.json index b7e573aa5..de6ecbf6f 100644 --- a/api-docs/openapi.json +++ b/api-docs/openapi.json @@ -1426,6 +1426,18 @@ "type": "string" }, "description": "The CVE ID for which the record is being updated" + }, + { + "$ref": "#/components/parameters/apiEntityHeader" + }, + { + "$ref": "#/components/parameters/apiUserHeader" + }, + { + "$ref": "#/components/parameters/apiSecretHeader" + }, + { + "$ref": "#/components/parameters/erlCheck" } ], "responses": { @@ -3033,7 +3045,7 @@ "erlCheck": { "in": "query", "name": "erlcheck", - "description": "Enables stricter validation that ensures submitted record meets enrichment data requirements", + "description": "Enables stricter validation that ensures submitted record meets enrichment data requirements. For a record to be enriched, a CVSS score and a CWE ID must be provided.", "required": false, "schema": { "type": "boolean" diff --git a/src/controller/cve.controller/index.js b/src/controller/cve.controller/index.js index 78df94a1b..3d6ef4570 100644 --- a/src/controller/cve.controller/index.js +++ b/src/controller/cve.controller/index.js @@ -648,8 +648,8 @@ router.put('/cve/:id/cna', #swagger.parameters['$ref'] = [ '#/components/parameters/apiEntityHeader', '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - '#/components/parameters/erlCheck', + '#/components/parameters/apiSecretHeader', + '#/components/parameters/erlCheck' ] #swagger.requestBody = { description: '<h3>Notes:</h3> diff --git a/src/swagger.js b/src/swagger.js index 630c0a2c8..3e21fe402 100644 --- a/src/swagger.js +++ b/src/swagger.js @@ -171,7 +171,7 @@ const doc = { erlCheck: { in: 'query', name: 'erlcheck', - description: 'Enables stricter validation that ensures submitted record meets enrichment data requirements', + description: 'Enables stricter validation that ensures submitted record meets enrichment data requirements. For a record to be enriched, a CVSS score and a CWE ID must be provided.', required: false, schema: { type: 'boolean'