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'