From ec554b77eee236742158443a0e479b5f5924547c Mon Sep 17 00:00:00 2001 From: "Daigneau, Jeremy T" Date: Thu, 10 Aug 2023 11:02:41 -0400 Subject: [PATCH 1/3] #1097 created middleware to check for future datePublics --- .../cve.controller/cve.middleware.js | 22 ++++++++++++++++++- src/controller/cve.controller/index.js | 7 +++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/controller/cve.controller/cve.middleware.js b/src/controller/cve.controller/cve.middleware.js index 66931bbf5..a950281be 100644 --- a/src/controller/cve.controller/cve.middleware.js +++ b/src/controller/cve.controller/cve.middleware.js @@ -147,6 +147,25 @@ function validateCveCnaContainerJsonSchema (req, res, next) { next() } +/** + * Checks that datePublic field is not a future date + * + * @param {String} dateIndex + * @returns true + * @throws Error + */ +function validateDatePublic (dateIndex) { + const currentDate = new Date().toISOString() + + // Check if datePublic is a future date + return body(dateIndex).optional({ nullable: true }).custom((datePublic, { req, path }) => { + if (currentDate > datePublic) { + return true + } + throw new Error('DatePublic cannot be a future date') + }) +} + // Organizations in the ADP pilot are generating JSON programatically, and thus // informing them about the result of the final validation (against the full // CVE Record schema) is currently sufficient. @@ -163,5 +182,6 @@ module.exports = { validateUniqueEnglishEntry, hasSingleEnglishEntry, validateDescription, - validateRejectBody + validateRejectBody, + validateDatePublic } diff --git a/src/controller/cve.controller/index.js b/src/controller/cve.controller/index.js index 0a5c465f8..e421bcae5 100644 --- a/src/controller/cve.controller/index.js +++ b/src/controller/cve.controller/index.js @@ -4,7 +4,7 @@ const mw = require('../../middleware/middleware') const errorMsgs = require('../../middleware/errorMessages') const controller = require('./cve.controller') const { body, param, query } = require('express-validator') -const { parseGetParams, parsePostParams, parseError, validateCveCnaContainerJsonSchema, validateCveAdpContainerJsonSchema, validateRejectBody, validateUniqueEnglishEntry, validateDescription } = require('./cve.middleware') +const { parseGetParams, parsePostParams, parseError, validateCveCnaContainerJsonSchema, validateCveAdpContainerJsonSchema, validateRejectBody, validateUniqueEnglishEntry, validateDescription, validateDatePublic } = require('./cve.middleware') const getConstants = require('../../constants').getConstants const CONSTANTS = getConstants() const CHOICES = [CONSTANTS.CVE_STATES.REJECTED, CONSTANTS.CVE_STATES.PUBLISHED] @@ -336,6 +336,7 @@ router.post('/cve/:id', // the lang key to check depends on the state, so pass both validateUniqueEnglishEntry(['containers.cna.descriptions', 'containers.cna.rejectedReasons']), validateDescription(['containers.cna.rejectedReasons', 'containers.cna.descriptions', 'containers.cna.problemTypes[0].descriptions']), + validateDatePublic(['containers.cna.datePublic']), param(['id']).isString().matches(CONSTANTS.CVE_ID_REGEX), parseError, parsePostParams, @@ -420,6 +421,7 @@ router.put('/cve/:id', // the lang key to check depends on the state, so pass both validateUniqueEnglishEntry(['containers.cna.descriptions', 'containers.cna.rejectedReasons']), validateDescription(['containers.cna.rejectedReasons', 'containers.cna.descriptions', 'containers.cna.problemTypes[0].descriptions']), + validateDatePublic(['containers.cna.datePublic']), param(['id']).isString().matches(CONSTANTS.CVE_ID_REGEX), parseError, parsePostParams, @@ -505,6 +507,7 @@ router.post('/cve/:id/cna', validateCveCnaContainerJsonSchema, validateUniqueEnglishEntry('cnaContainer.descriptions'), validateDescription(['cnaContainer.descriptions', 'cnaContainer.problemTypes[0].descriptions']), + validateDatePublic(['containers.cna.datePublic']), param(['id']).isString().matches(CONSTANTS.CVE_ID_REGEX), parseError, parsePostParams, @@ -591,6 +594,7 @@ router.put('/cve/:id/cna', validateCveCnaContainerJsonSchema, validateUniqueEnglishEntry('cnaContainer.descriptions'), validateDescription(['cnaContainer.descriptions', 'cnaContainer.problemTypes[0].descriptions']), + validateDatePublic(['containers.cna.datePublic']), param(['id']).isString().matches(CONSTANTS.CVE_ID_REGEX), parseError, parsePostParams, @@ -849,6 +853,7 @@ router.put('/cve/:id/adp', mw.validateUser, mw.onlyAdps, validateCveAdpContainerJsonSchema, + validateDatePublic(['containers.adp.datePublic']), param(['id']).isString().matches(CONSTANTS.CVE_ID_REGEX), parseError, parsePostParams, From 87e2da5126e0520fe2b1ef94251e7546082f850f Mon Sep 17 00:00:00 2001 From: "Daigneau, Jeremy T" Date: Fri, 11 Aug 2023 10:27:29 -0400 Subject: [PATCH 2/3] #1097 added tests for datePublic validator --- .../cve.controller/cve.middleware.js | 16 +- src/controller/cve.controller/index.js | 6 +- test/integration-tests/constants.js | 9 +- test/schemas/5.0/CVE-2017-4024_published.json | 49 ++-- ...VE-2017-4024_published_bad_datePublic.json | 237 ++++++++++++++++++ test/unit-tests/cve/validateDatePublicTest.js | 24 ++ 6 files changed, 307 insertions(+), 34 deletions(-) create mode 100644 test/schemas/5.0/CVE-2017-4024_published_bad_datePublic.json create mode 100644 test/unit-tests/cve/validateDatePublicTest.js diff --git a/src/controller/cve.controller/cve.middleware.js b/src/controller/cve.controller/cve.middleware.js index a950281be..7aa6a2954 100644 --- a/src/controller/cve.controller/cve.middleware.js +++ b/src/controller/cve.controller/cve.middleware.js @@ -155,17 +155,20 @@ function validateCveCnaContainerJsonSchema (req, res, next) { * @throws Error */ function validateDatePublic (dateIndex) { - const currentDate = new Date().toISOString() - // Check if datePublic is a future date - return body(dateIndex).optional({ nullable: true }).custom((datePublic, { req, path }) => { - if (currentDate > datePublic) { + return body(dateIndex).optional({ nullable: true }).custom((datePublic) => { + if (datePublicHelper(datePublic)) { return true } - throw new Error('DatePublic cannot be a future date') + throw new Error('datePublic cannot be a future date') }) } +function datePublicHelper (datePublic) { + const currentDate = new Date().toISOString() + return currentDate > datePublic +} + // Organizations in the ADP pilot are generating JSON programatically, and thus // informing them about the result of the final validation (against the full // CVE Record schema) is currently sufficient. @@ -183,5 +186,6 @@ module.exports = { hasSingleEnglishEntry, validateDescription, validateRejectBody, - validateDatePublic + validateDatePublic, + datePublicHelper } diff --git a/src/controller/cve.controller/index.js b/src/controller/cve.controller/index.js index e421bcae5..2e89fe49f 100644 --- a/src/controller/cve.controller/index.js +++ b/src/controller/cve.controller/index.js @@ -507,7 +507,7 @@ router.post('/cve/:id/cna', validateCveCnaContainerJsonSchema, validateUniqueEnglishEntry('cnaContainer.descriptions'), validateDescription(['cnaContainer.descriptions', 'cnaContainer.problemTypes[0].descriptions']), - validateDatePublic(['containers.cna.datePublic']), + validateDatePublic(['cnaContainer.datePublic']), param(['id']).isString().matches(CONSTANTS.CVE_ID_REGEX), parseError, parsePostParams, @@ -594,7 +594,7 @@ router.put('/cve/:id/cna', validateCveCnaContainerJsonSchema, validateUniqueEnglishEntry('cnaContainer.descriptions'), validateDescription(['cnaContainer.descriptions', 'cnaContainer.problemTypes[0].descriptions']), - validateDatePublic(['containers.cna.datePublic']), + validateDatePublic(['cnaContainer.datePublic']), param(['id']).isString().matches(CONSTANTS.CVE_ID_REGEX), parseError, parsePostParams, @@ -853,7 +853,7 @@ router.put('/cve/:id/adp', mw.validateUser, mw.onlyAdps, validateCveAdpContainerJsonSchema, - validateDatePublic(['containers.adp.datePublic']), + validateDatePublic(['adpContainer.datePublic']), param(['id']).isString().matches(CONSTANTS.CVE_ID_REGEX), parseError, parsePostParams, diff --git a/test/integration-tests/constants.js b/test/integration-tests/constants.js index 1b683776f..4b8b954b3 100644 --- a/test/integration-tests/constants.js +++ b/test/integration-tests/constants.js @@ -242,5 +242,12 @@ const testAdp2 = { } module.exports = { - headers, nonSecretariatUserHeaders, badNonSecretariatUserHeaders, nonSecretariatUserHeadersWithAdp2, testCve, testCveEdited, testAdp, testAdp2 + headers, + nonSecretariatUserHeaders, + badNonSecretariatUserHeaders, + nonSecretariatUserHeadersWithAdp2, + testCve, + testCveEdited, + testAdp, + testAdp2 } diff --git a/test/schemas/5.0/CVE-2017-4024_published.json b/test/schemas/5.0/CVE-2017-4024_published.json index a65c5fd24..3dcb75293 100644 --- a/test/schemas/5.0/CVE-2017-4024_published.json +++ b/test/schemas/5.0/CVE-2017-4024_published.json @@ -26,17 +26,17 @@ } ], "affected": [ - { - "vendor": "u", - "product": "yuyi", - "versions": [ - { - "version": "uuy", - "status": "affected" - } - ] - } - ], + { + "vendor": "u", + "product": "yuyi", + "versions": [ + { + "version": "uuy", + "status": "affected" + } + ] + } + ], "providerMetadata": { "orgId": "88c02595-c8f7-4864-a0e7-e09b3e1da691", "shortName": "cisco", @@ -137,17 +137,17 @@ } ], "affected": [ - { - "vendor": "u", - "product": "yuyi", - "versions": [ - { - "version": "uuy", - "status": "affected" - } - ] - } - ], + { + "vendor": "u", + "product": "yuyi", + "versions": [ + { + "version": "uuy", + "status": "affected" + } + ] + } + ], "providerMetadata": { "orgId": "88c02595-c8f7-4864-a0e7-e09b3e1da691", "shortName": "cisco", @@ -230,7 +230,8 @@ ], "source": { "discoverer": "Tom Smith" - } + }, + "datePublic": "2022-02-20T00:00:00" } } - } \ No newline at end of file +} \ No newline at end of file diff --git a/test/schemas/5.0/CVE-2017-4024_published_bad_datePublic.json b/test/schemas/5.0/CVE-2017-4024_published_bad_datePublic.json new file mode 100644 index 000000000..61c4c920e --- /dev/null +++ b/test/schemas/5.0/CVE-2017-4024_published_bad_datePublic.json @@ -0,0 +1,237 @@ +{ + "dataType": "CVE_RECORD", + "dataVersion": "5.0", + "cveMetadata": { + "cveId": "CVE-2017-4024", + "assignerOrgId": "88c02595-c8f7-4864-a0e7-e09b3e1da691", + "assignerShortName": "cisco", + "requesterUserId": "1fcbf829-81ae-4b53-a61d-9fa04711447f", + "state": "PUBLISHED", + "dateUpdated": "2021-11-19T20:07:00.403Z" + }, + "containers": { + "adp": [ + { + "metrics": [ + { + "format": "uyi", + "scenario": "kjk", + "other": { + "type": "oio", + "content": { + "kjgk": "kjgkhg" + } + }, + "uu": 8 + } + ], + "affected": [ + { + "vendor": "u", + "product": "yuyi", + "versions": [ + { + "version": "uuy", + "status": "affected" + } + ] + } + ], + "providerMetadata": { + "orgId": "88c02595-c8f7-4864-a0e7-e09b3e1da691", + "shortName": "cisco", + "dateUpdated": "2018-11-13T20:20:39+00:00" + }, + "descriptions": [ + { + "lang": "en", + "value": "y" + } + ], + "problemTypes": [ + { + "descriptions": [ + { + "lang": "en", + "description": "y", + "cweId": "CWE-91", + "type": "u", + "references": [ + { + "url": "https://cwe.mitre.org/data/definitions/284.html" + } + ] + } + ] + } + ], + "references": [ + { + "url": "https://cwe.mitre.org/data/definitions/284.html", + "name": "12345", + "refsource": "12345", + "tags": [ + "x_Broken Link" + ] + } + ], + "impacts": [ + { + "descriptions": [ + { + "lang": "en", + "value": "y" + } + ], + "capecId": "CAPEC-9999" + } + ], + "configurations": [ + { + "lang": "en", + "value": "y" + } + ], + "workarounds": [ + { + "lang": "en", + "value": "y" + } + ], + "exploits": [ + { + "lang": "en", + "value": "y" + } + ], + "timeline": [ + { + "time": "2018-11-13T20:20:39+00:00", + "lang": "in", + "value": "y" + } + ], + "credits": [ + { + "lang": "en", + "value": "y" + } + ], + "source": { + "discoverer": "Tom Smith" + } + } + ], + "cna": { + "metrics": [ + { + "format": "uyi", + "scenario": "kjk", + "other": { + "type": "oio", + "content": { + "kjgk": "kjgkhg" + } + }, + "uu": 8 + } + ], + "affected": [ + { + "vendor": "u", + "product": "yuyi", + "versions": [ + { + "version": "uuy", + "status": "affected" + } + ] + } + ], + "providerMetadata": { + "orgId": "88c02595-c8f7-4864-a0e7-e09b3e1da691", + "shortName": "cisco", + "dateUpdated": "2018-11-13T20:20:39+00:00" + }, + "descriptions": [ + { + "lang": "en", + "value": "ya" + } + ], + "problemTypes": [ + { + "descriptions": [ + { + "lang": "en", + "description": "y", + "cweId": "CWE-91", + "type": "u", + "references": [ + { + "url": "https://cwe.mitre.org/data/definitions/284.html" + } + ] + } + ] + } + ], + "references": [ + { + "url": "https://cwe.mitre.org/data/definitions/284.html", + "name": "12345", + "refsource": "12345", + "tags": [ + "x_Broken Link" + ] + } + ], + "impacts": [ + { + "descriptions": [ + { + "lang": "en", + "value": "y" + } + ], + "capecId": "CAPEC-9999" + } + ], + "configurations": [ + { + "lang": "en", + "value": "y" + } + ], + "workarounds": [ + { + "lang": "en", + "value": "y" + } + ], + "exploits": [ + { + "lang": "en", + "value": "y" + } + ], + "timeline": [ + { + "time": "2018-11-13T20:20:39+00:00", + "lang": "in", + "value": "y" + } + ], + "credits": [ + { + "lang": "en", + "value": "y" + } + ], + "source": { + "discoverer": "Tom Smith" + }, + "datePublic": "3024-02-20T00:00:00" + } + } +} \ No newline at end of file diff --git a/test/unit-tests/cve/validateDatePublicTest.js b/test/unit-tests/cve/validateDatePublicTest.js new file mode 100644 index 000000000..bccfef9c4 --- /dev/null +++ b/test/unit-tests/cve/validateDatePublicTest.js @@ -0,0 +1,24 @@ +/* eslint-disable no-unused-expressions */ + +const chai = require('chai') +const expect = chai.expect + +const { datePublicHelper } = require('../../../src/controller/cve.controller/cve.middleware') +const validDatePublicRecord = require('../../schemas/5.0/CVE-2017-4024_published.json') +const inValidDatePublicRecord = require('../../schemas/5.0/CVE-2017-4024_published_bad_datePublic.json') + +describe('Testing validateDatePublic middleware', () => { + context('Negative Tests', () => { + it('Should throw an error for datePublic dates in the future ', async () => { + const result = datePublicHelper(inValidDatePublicRecord.containers.cna.datePublic) + expect(result).to.be.false + }) + }) + + context('Positive Tests', () => { + it('Should return true for records with datePublic in the past', async () => { + const result = datePublicHelper(validDatePublicRecord.containers.cna.datePublic) + expect(result).to.be.true + }) + }) +}) From 68da425d87dbfd83dc6992ac679798cc2978c9ed Mon Sep 17 00:00:00 2001 From: "Daigneau, Jeremy T" Date: Fri, 11 Aug 2023 11:16:45 -0400 Subject: [PATCH 3/3] #1097 fixed tests --- test/unit-tests/cve/validateDatePublicTest.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/unit-tests/cve/validateDatePublicTest.js b/test/unit-tests/cve/validateDatePublicTest.js index bccfef9c4..b0c1e0e7f 100644 --- a/test/unit-tests/cve/validateDatePublicTest.js +++ b/test/unit-tests/cve/validateDatePublicTest.js @@ -2,21 +2,23 @@ const chai = require('chai') const expect = chai.expect +const _ = require('lodash') const { datePublicHelper } = require('../../../src/controller/cve.controller/cve.middleware') -const validDatePublicRecord = require('../../schemas/5.0/CVE-2017-4024_published.json') +const tempValidDatePublicRecord = require('../../schemas/5.0/CVE-2017-4024_published.json') +const validDatePublicRecord = _.cloneDeep(tempValidDatePublicRecord) const inValidDatePublicRecord = require('../../schemas/5.0/CVE-2017-4024_published_bad_datePublic.json') describe('Testing validateDatePublic middleware', () => { context('Negative Tests', () => { - it('Should throw an error for datePublic dates in the future ', async () => { + it('Should throw an error for datePublic dates in the future ', () => { const result = datePublicHelper(inValidDatePublicRecord.containers.cna.datePublic) expect(result).to.be.false }) }) context('Positive Tests', () => { - it('Should return true for records with datePublic in the past', async () => { + it('Should return true for records with datePublic in the past', () => { const result = datePublicHelper(validDatePublicRecord.containers.cna.datePublic) expect(result).to.be.true })