From 6bc2640a2c9ad61b96ac2c0f7667653d9dcd6bd6 Mon Sep 17 00:00:00 2001 From: rschneider <97682836+rainer-exxcellent@users.noreply.github.com> Date: Thu, 3 Jul 2025 11:27:56 +0200 Subject: [PATCH 1/4] feat(CSAF2.1): #287 add mandatory test 6.1.26 --- README.md | 2 +- csaf_2_1/mandatoryTests.js | 2 +- .../mandatoryTests/mandatoryTest_6_1_26.js | 94 +++++++++++++++++++ tests/csaf_2_1/mandatoryTest_6_1_26.js | 18 ++++ tests/csaf_2_1/oasis.js | 1 - 5 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js create mode 100644 tests/csaf_2_1/mandatoryTest_6_1_26.js diff --git a/README.md b/README.md index 5753a4a1..290a721b 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.46 - Mandatory Test 6.1.47 @@ -391,6 +390,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 c302572e..d2550695 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..df88b2fd --- /dev/null +++ b/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js @@ -0,0 +1,94 @@ +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 otherProfileValues = [ + '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 ( + otherProfileValues.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 6caa005c..d082cc32 100644 --- a/tests/csaf_2_1/oasis.js +++ b/tests/csaf_2_1/oasis.js @@ -12,7 +12,6 @@ import * as mandatory from '../../csaf_2_1/mandatoryTests.js' const excluded = [ '6.1.8', '6.1.9', - '6.1.26', '6.1.27.3', '6.1.27.4', '6.1.27.6', From 42964f9982695d80e7e4f7adc13dc1ff35ef3bc5 Mon Sep 17 00:00:00 2001 From: rschneider <97682836+rainer-exxcellent@users.noreply.github.com> Date: Mon, 14 Jul 2025 11:42:19 +0200 Subject: [PATCH 2/4] feat(CSAF2.1): #287 add mandatory test 6.1.42 - change variable name --- csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js b/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js index df88b2fd..2c927a63 100644 --- a/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js +++ b/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js @@ -34,7 +34,7 @@ const profileValues = [ 'csaf_withdrawn', 'csaf_superseded', ] -const otherProfileValues = [ +const prohibitedDocumentCategoryNames = [ 'securityincidentresponse', 'informationaladvisory', 'securityadvisory', @@ -82,7 +82,9 @@ export function mandatoryTest_6_1_26(doc) { // Fail on name similarity if ( - otherProfileValues.includes(category.replace(/[_-\s]+/g, '').toLowerCase()) + prohibitedDocumentCategoryNames.includes( + category.replace(/[_-\s]+/g, '').toLowerCase() + ) ) { ctx.isValid = false ctx.errors.push({ From d96d4e41eeaed714487e5618f71784c1a327d2bd Mon Sep 17 00:00:00 2001 From: rschneider <97682836+rainer-exxcellent@users.noreply.github.com> Date: Mon, 14 Jul 2025 11:49:42 +0200 Subject: [PATCH 3/4] feat(CSAF2.1): #287 add mandatory test 6.1.42 - fix quotes --- csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js b/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js index 2c927a63..b1e7faa1 100644 --- a/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js +++ b/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js @@ -73,8 +73,8 @@ export function mandatoryTest_6_1_26(doc) { if (category.toLowerCase().startsWith('csaf_')) { ctx.isValid = false ctx.errors.push({ - instancePath: `/document/category`, - message: `reserved prefix used`, + instancePath: '/document/category', + message: 'reserved prefix used', }) return ctx @@ -88,8 +88,8 @@ export function mandatoryTest_6_1_26(doc) { ) { ctx.isValid = false ctx.errors.push({ - instancePath: `/document/category`, - message: `value prohibited`, + instancePath: '/document/category', + message: 'value prohibited', }) } return ctx From 4432d09be43bcc72810aae15bb3ea7069bdf053f Mon Sep 17 00:00:00 2001 From: rschneider <97682836+rainer-exxcellent@users.noreply.github.com> Date: Mon, 3 Nov 2025 06:48:37 +0100 Subject: [PATCH 4/4] feat(CSAF2.1): #287 add mandatory test 6.1.26 - changed some comments and field name --- .../mandatoryTests/mandatoryTest_6_1_26.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js b/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js index b1e7faa1..2aa94f10 100644 --- a/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js +++ b/csaf_2_1/mandatoryTests/mandatoryTest_6_1_26.js @@ -24,7 +24,7 @@ const inputSchema = /** @type {const} */ ({ const validateInput = jtdAjv.compile(inputSchema) -const profileValues = [ +const allowedCategoryNames = [ 'csaf_base', 'csaf_security_incident_response', 'csaf_informational_advisory', @@ -34,7 +34,10 @@ const profileValues = [ 'csaf_withdrawn', 'csaf_superseded', ] -const prohibitedDocumentCategoryNames = [ + +// lowercase category names with without the prefix csaf_ and removed dash, whitespace, and underscore characters +// without csaf_base +const prohibitedCategoriesWithoutPrefix = [ 'securityincidentresponse', 'informationaladvisory', 'securityadvisory', @@ -64,8 +67,8 @@ export function mandatoryTest_6_1_26(doc) { /** @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)) { + // Skip test for the allowedCategoryNames in /document/category: + if (allowedCategoryNames.includes(category)) { return ctx } @@ -74,15 +77,16 @@ export function mandatoryTest_6_1_26(doc) { ctx.isValid = false ctx.errors.push({ instancePath: '/document/category', - message: 'reserved prefix used', + message: 'reserved prefix "csaf_" used', }) return ctx } - // Fail on name similarity + // Fail on name case-insensitive similarity if ( - prohibitedDocumentCategoryNames.includes( + prohibitedCategoriesWithoutPrefix.includes( + //remove occurrences of dash, whitespace, and underscore characters category.replace(/[_-\s]+/g, '').toLowerCase() ) ) {