diff --git a/README.md b/README.md index ed6bd63f..6fca84f7 100644 --- a/README.md +++ b/README.md @@ -311,7 +311,6 @@ The following tests are not yet implemented and therefore missing: **Mandatory Tests** -- Mandatory Test 6.1.26 - Mandatory Test 6.1.27.13 - Mandatory Test 6.1.27.18 - Mandatory Test 6.1.42 @@ -402,6 +401,7 @@ export const mandatoryTest_6_1_22: DocumentTest export const mandatoryTest_6_1_23: DocumentTest export const mandatoryTest_6_1_24: DocumentTest export const mandatoryTest_6_1_25: DocumentTest +export const mandatoryTest_6_1_26: DocumentTest export const mandatoryTest_6_1_27_1: DocumentTest export const mandatoryTest_6_1_27_2: DocumentTest export const mandatoryTest_6_1_27_3: DocumentTest diff --git a/csaf_2_1/mandatoryTests.js b/csaf_2_1/mandatoryTests.js index 533923a0..5f4f203f 100644 --- a/csaf_2_1/mandatoryTests.js +++ b/csaf_2_1/mandatoryTests.js @@ -16,7 +16,6 @@ export { mandatoryTest_6_1_23, mandatoryTest_6_1_24, mandatoryTest_6_1_25, - mandatoryTest_6_1_26, mandatoryTest_6_1_27_1, mandatoryTest_6_1_27_2, mandatoryTest_6_1_27_3, @@ -43,6 +42,7 @@ export { mandatoryTest_6_1_9 } from './mandatoryTests/mandatoryTest_6_1_9.js' export { mandatoryTest_6_1_10 } from './mandatoryTests/mandatoryTest_6_1_10.js' export { mandatoryTest_6_1_11 } from './mandatoryTests/mandatoryTest_6_1_11.js' export { mandatoryTest_6_1_13 } from './mandatoryTests/mandatoryTest_6_1_13.js' +export { mandatoryTest_6_1_26 } from './mandatoryTests/mandatoryTest_6_1_26.js' export { mandatoryTest_6_1_27_12 } from './mandatoryTests/mandatoryTest_6_1_27_12.js' export { mandatoryTest_6_1_27_14 } from './mandatoryTests/mandatoryTest_6_1_27_14.js' export { mandatoryTest_6_1_27_15 } from './mandatoryTests/mandatoryTest_6_1_27_15.js' diff --git a/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js b/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js new file mode 100644 index 00000000..b1e7faa1 --- /dev/null +++ b/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js @@ -0,0 +1,96 @@ +import Ajv from 'ajv/dist/jtd.js' + +const jtdAjv = new Ajv() + +/* + This is the jtd schema that needs to match the input document so that the + test is activated. If this schema doesn't match it normally means that the input + document does not validate against the csaf json schema or optional fields that + the test checks are not present. + */ +const inputSchema = /** @type {const} */ ({ + additionalProperties: true, + properties: { + document: { + additionalProperties: true, + properties: { + category: { + type: 'string', + }, + }, + }, + }, +}) + +const validateInput = jtdAjv.compile(inputSchema) + +const profileValues = [ + 'csaf_base', + 'csaf_security_incident_response', + 'csaf_informational_advisory', + 'csaf_security_advisory', + 'csaf_vex', + 'csaf_deprecated_security_advisory', + 'csaf_withdrawn', + 'csaf_superseded', +] +const prohibitedDocumentCategoryNames = [ + 'securityincidentresponse', + 'informationaladvisory', + 'securityadvisory', + 'vex', + 'deprecatedsecurityadvisory', + 'withdrawn', + 'superseded', +] + +/** + * It MUST be tested that the document category is not equal to the (case-insensitive) name (without the prefix csaf_) + * or value of any other profile than "CSAF Base". Any occurrences of dash, whitespace, and underscore characters are + * removed from the values on both sides before the match. + * Also, the value MUST NOT start with the reserved prefix csaf_ except if the value is csaf_base. + * @param {unknown} doc + */ +export function mandatoryTest_6_1_26(doc) { + const ctx = { + errors: + /** @type {Array<{ instancePath: string; message: string }>} */ ([]), + isValid: true, + } + if (!validateInput(doc)) { + return ctx + } + + /** @type {string} */ + const category = doc.document.category + + // Skip test if profile is not "CSAF Base" but one of the other profiles or matches exactly "csaf_base" + if (profileValues.includes(category)) { + return ctx + } + + // Fail on reserved prefix + if (category.toLowerCase().startsWith('csaf_')) { + ctx.isValid = false + ctx.errors.push({ + instancePath: '/document/category', + message: 'reserved prefix used', + }) + + return ctx + } + + // Fail on name similarity + if ( + prohibitedDocumentCategoryNames.includes( + category.replace(/[_-\s]+/g, '').toLowerCase() + ) + ) { + ctx.isValid = false + ctx.errors.push({ + instancePath: '/document/category', + message: 'value prohibited', + }) + } + return ctx +} diff --git a/tests/csaf_2_1/mandatoryTest_6_1_26.js b/tests/csaf_2_1/mandatoryTest_6_1_26.js new file mode 100644 index 00000000..d264959e --- /dev/null +++ b/tests/csaf_2_1/mandatoryTest_6_1_26.js @@ -0,0 +1,18 @@ +import assert from 'node:assert/strict' +import { mandatoryTest_6_1_26 } from '../../csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js' + +describe('mandatoryTest_6_1_26', function () { + it('only runs on relevant documents', function () { + assert.equal(mandatoryTest_6_1_26({ document: 'mydoc' }).isValid, true) + }) + it('check use of reserved prefix csaf_ except if the value is csaf_base', function () { + assert.equal( + mandatoryTest_6_1_26({ + document: { + category: 'csaf_invalid', + }, + }).isValid, + false + ) + }) +}) diff --git a/tests/csaf_2_1/oasis.js b/tests/csaf_2_1/oasis.js index 32fdcaf9..5d43ef0a 100644 --- a/tests/csaf_2_1/oasis.js +++ b/tests/csaf_2_1/oasis.js @@ -10,7 +10,6 @@ import * as mandatory from '../../csaf_2_1/mandatoryTests.js' Once all tests are implemented for CSAF 2.1 this should be deleted. */ const excluded = [ - '6.1.26', '6.1.27.3', '6.1.27.4', '6.1.27.6',