diff --git a/README.md b/README.md index ebb45f8d..7e32e7d8 100644 --- a/README.md +++ b/README.md @@ -355,7 +355,6 @@ The following tests are not yet implemented and therefore missing: - Recommended Test 6.2.32 - Recommended Test 6.2.33 - Recommended Test 6.2.34 -- Recommended Test 6.2.35 - Recommended Test 6.2.36 - Recommended Test 6.2.37 - Recommended Test 6.2.38 @@ -462,6 +461,7 @@ export const recommendedTest_6_2_16: DocumentTest export const recommendedTest_6_2_17: DocumentTest export const recommendedTest_6_2_18: DocumentTest export const recommendedTest_6_2_22: DocumentTest +export const recommendedTest_6_2_35: DocumentTest ``` [(back to top)](#bsi-csaf-validator-lib) diff --git a/csaf_2_1/recommendedTests.js b/csaf_2_1/recommendedTests.js index aeb25c42..2ddb3f5c 100644 --- a/csaf_2_1/recommendedTests.js +++ b/csaf_2_1/recommendedTests.js @@ -30,4 +30,5 @@ export { recommendedTest_6_2_22 } from './recommendedTests/recommendedTest_6_2_2 export { recommendedTest_6_2_27 } from './recommendedTests/recommendedTest_6_2_27.js' export { recommendedTest_6_2_28 } from './recommendedTests/recommendedTest_6_2_28.js' export { recommendedTest_6_2_29 } from './recommendedTests/recommendedTest_6_2_29.js' +export { recommendedTest_6_2_35 } from './recommendedTests/recommendedTest_6_2_35.js' export { recommendedTest_6_2_38 } from './recommendedTests/recommendedTest_6_2_38.js' diff --git a/csaf_2_1/recommendedTests/recommendedTest_6_2_35.js b/csaf_2_1/recommendedTests/recommendedTest_6_2_35.js new file mode 100644 index 00000000..f75c90bf --- /dev/null +++ b/csaf_2_1/recommendedTests/recommendedTest_6_2_35.js @@ -0,0 +1,100 @@ +import Ajv from 'ajv/dist/jtd.js' + +const ajv = new Ajv() + +const inputSchema = /** @type {const} */ ({ + additionalProperties: true, + properties: { + document: { + additionalProperties: true, + properties: { + distribution: { + additionalProperties: true, + properties: { + tlp: { + additionalProperties: true, + properties: { + label: { type: 'string' }, + }, + }, + }, + }, + }, + }, + vulnerabilities: { + elements: { + additionalProperties: true, + optionalProperties: { + metrics: { + elements: { + additionalProperties: true, + optionalProperties: { + content: { + additionalProperties: true, + optionalProperties: { + ssvc_v1: { + additionalProperties: true, + optionalProperties: { + selections: { + elements: { + additionalProperties: true, + optionalProperties: { + namespace: { + type: 'string', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +}) + +const validate = ajv.compile(inputSchema) + +/** + * This implements the recommended test 6.2.35 of the CSAF 2.1 standard. + * + * @param {any} doc + */ +export function recommendedTest_6_2_35(doc) { + /** @type {Array<{ message: string; instancePath: string }>} */ + const warnings = [] + const context = { warnings } + + if (!validate(doc)) { + return context + } + + /* + * According to https://certcc.github.io/SSVC/data/schema/v1/Decision_Point-1-0-1.schema.json#/$defs/decision_point/properties/namespace + * a private namespace starts with "x_" + * */ + + if (doc.document.distribution.tlp.label !== 'CLEAR') { + return context + } + doc.vulnerabilities?.forEach((vulnerability, vulnerabilityIndex) => { + vulnerability.metrics?.forEach((metric, metricIndex) => { + const selections = metric.content?.ssvc_v1?.selections || [] + selections.forEach((selection, selectionIndex) => { + if (selection.namespace?.startsWith('x_')) { + context.warnings.push({ + message: `The namespace "${selection.namespace}" is a private namespace`, + instancePath: `/vulnerabilities/${vulnerabilityIndex}/metrics/${metricIndex}/content/ssvc_v1/selections/${selectionIndex}/namespace`, + }) + } + }) + }) + }) + + return context +} diff --git a/tests/csaf_2_1/oasis.js b/tests/csaf_2_1/oasis.js index 31ce644c..569a0c9d 100644 --- a/tests/csaf_2_1/oasis.js +++ b/tests/csaf_2_1/oasis.js @@ -53,7 +53,6 @@ const excluded = [ '6.2.32', '6.2.33', '6.2.34', - '6.2.35', '6.2.36', '6.2.37', '6.2.39.1', diff --git a/tests/csaf_2_1/recommendedTest_6_2_35.js b/tests/csaf_2_1/recommendedTest_6_2_35.js new file mode 100644 index 00000000..c07c4cdd --- /dev/null +++ b/tests/csaf_2_1/recommendedTest_6_2_35.js @@ -0,0 +1,51 @@ +import assert from 'node:assert' +import { recommendedTest_6_2_35 } from '../../csaf_2_1/recommendedTests.js' + +describe('recommendedTest_6_2_35', function () { + it('only runs on relevant documents', function () { + assert.equal( + recommendedTest_6_2_35({ vulnerabilities: 'mydoc' }).warnings.length, + 0 + ) + }) + it('skips empty objects', function () { + assert.equal( + recommendedTest_6_2_35({ + document: { + distribution: { + tlp: { + label: 'CLEAR', + }, + }, + }, + vulnerabilities: [ + { + metrics: [ + { + content: { + ssvc_v1: {}, // should be ignored + }, + }, + ], + }, + { + metrics: [ + { + content: { + ssvc_v1: { + selections: [ + { + namespace: 'x_custom', + }, + ], + }, + }, + }, + ], + }, + ], + }).warnings.length, + 1 + ) + }) +})