From b7d0d93b88e12ad7718c19f67c4caed359d8ff23 Mon Sep 17 00:00:00 2001 From: bendo-eXX Date: Tue, 31 Mar 2026 16:39:44 +0200 Subject: [PATCH 1/2] fix(CSAF2.1): fix mandatoryTest_6_1_27_5.js --- csaf_2_1/mandatoryTests.js | 2 +- .../mandatoryTests/mandatory_test_6_1_27_5.js | 73 +++++++++++++++++++ tests/csaf_2_1/mandatoryTest_6_1_27_5.js | 36 +++++++++ tests/csaf_2_1/oasis.js | 1 - 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 csaf_2_1/mandatoryTests/mandatory_test_6_1_27_5.js create mode 100644 tests/csaf_2_1/mandatoryTest_6_1_27_5.js diff --git a/csaf_2_1/mandatoryTests.js b/csaf_2_1/mandatoryTests.js index c302572e..10b96f9d 100644 --- a/csaf_2_1/mandatoryTests.js +++ b/csaf_2_1/mandatoryTests.js @@ -21,7 +21,6 @@ export { mandatoryTest_6_1_27_2, mandatoryTest_6_1_27_3, mandatoryTest_6_1_27_4, - mandatoryTest_6_1_27_5, mandatoryTest_6_1_27_6, mandatoryTest_6_1_27_7, mandatoryTest_6_1_27_8, @@ -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_27_5 } from './mandatoryTests/./mandatory_test_6_1_27_5.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/mandatory_test_6_1_27_5.js b/csaf_2_1/mandatoryTests/mandatory_test_6_1_27_5.js new file mode 100644 index 00000000..dddf03b3 --- /dev/null +++ b/csaf_2_1/mandatoryTests/mandatory_test_6_1_27_5.js @@ -0,0 +1,73 @@ +import Ajv from 'ajv/dist/jtd.js' + +const ajv = 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', + }, + }, + }, + vulnerabilities: { + elements: { + additionalProperties: true, + optionalProperties: { + notes: { + elements: { + additionalProperties: true, + properties: {}, + }, + }, + }, + }, + }, + }, +}) + +const validate = ajv.compile(inputSchema) +/** + * @param {any} doc + */ +export function mandatoryTest_6_1_27_5(doc) { + /** @type {Array<{ message: string; instancePath: string }>} */ + const errors = [] + let isValid = true + + if (!validate(doc)) return { errors, isValid } + + const checkedDocumentCategories = new Set([ + 'csaf_security_advisory', + 'csaf_vex', + 'csaf_deprecated_security_advisory', + ]) + + if (!checkedDocumentCategories.has(doc.document?.category)) { + return { errors, isValid } + } + + const vulnerabilities = doc.vulnerabilities + if (Array.isArray(vulnerabilities)) { + vulnerabilities.forEach((vulnerability, vulnerabilityIndex) => { + if (!vulnerability.notes || vulnerability.notes.length === 0) { + isValid = false + errors.push({ + instancePath: `/vulnerabilities/${vulnerabilityIndex}`, + message: 'needs a `notes` attribute', + }) + } + }) + } + + return { errors, isValid } +} diff --git a/tests/csaf_2_1/mandatoryTest_6_1_27_5.js b/tests/csaf_2_1/mandatoryTest_6_1_27_5.js new file mode 100644 index 00000000..785791e1 --- /dev/null +++ b/tests/csaf_2_1/mandatoryTest_6_1_27_5.js @@ -0,0 +1,36 @@ +import assert from 'node:assert/strict' +import { mandatoryTest_6_1_27_5 } from '../../csaf_2_1/mandatoryTests/mandatory_test_6_1_27_5.js' + +describe('mandatoryTest_6_1_27_5', function () { + it('only runs on relevant documents', function () { + assert.equal(mandatoryTest_6_1_27_5({ document: 'mydoc' }).isValid, true) + }) + + it('returns valid for documents with irrelevant category', function () { + assert.equal( + mandatoryTest_6_1_27_5({ + document: { category: 'csaf_base' }, + vulnerabilities: [{}], + }).isValid, + true + ) + }) + + it('returns invalid when vulnerability has no notes', function () { + const result = mandatoryTest_6_1_27_5({ + document: { category: 'csaf_security_advisory' }, + vulnerabilities: [{}], + }) + assert.equal(result.isValid, false) + assert.equal(result.errors.length, 1) + }) + + it('returns invalid when vulnerability has empty notes array', function () { + const result = mandatoryTest_6_1_27_5({ + document: { category: 'csaf_security_advisory' }, + vulnerabilities: [{ notes: [] }], + }) + assert.equal(result.isValid, false) + assert.equal(result.errors.length, 1) + }) +}) diff --git a/tests/csaf_2_1/oasis.js b/tests/csaf_2_1/oasis.js index 69ed16f3..e24d79cc 100644 --- a/tests/csaf_2_1/oasis.js +++ b/tests/csaf_2_1/oasis.js @@ -98,7 +98,6 @@ const excluded = [ const skippedTests = new Set([ 'mandatory/oasis_csaf_tc-csaf_2_1-2024-6-1-01-12.json', 'mandatory/oasis_csaf_tc-csaf_2_1-2024-6-1-03-01.json', - 'mandatory/oasis_csaf_tc-csaf_2_1-2024-6-1-27-05-03.json', ]) /** @typedef {import('../../lib/shared/types.js').DocumentTest} DocumentTest */ From 663c89b170fcdb5dfb20a929ccbf7a222cd2795d Mon Sep 17 00:00:00 2001 From: bendo-eXX Date: Tue, 7 Apr 2026 10:11:32 +0200 Subject: [PATCH 2/2] fix(CSAF2.1): rename mandatoryTest_6_1_27_5.js --- csaf_2_1/mandatoryTests.js | 2 +- .../{mandatory_test_6_1_27_5.js => mandatoryTest_6_1_27_5.js} | 0 tests/csaf_2_1/mandatoryTest_6_1_27_5.js | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename csaf_2_1/mandatoryTests/{mandatory_test_6_1_27_5.js => mandatoryTest_6_1_27_5.js} (100%) diff --git a/csaf_2_1/mandatoryTests.js b/csaf_2_1/mandatoryTests.js index 10b96f9d..8ae82bc4 100644 --- a/csaf_2_1/mandatoryTests.js +++ b/csaf_2_1/mandatoryTests.js @@ -42,7 +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_27_5 } from './mandatoryTests/./mandatory_test_6_1_27_5.js' +export { mandatoryTest_6_1_27_5 } from './mandatoryTests/mandatoryTest_6_1_27_5.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/mandatory_test_6_1_27_5.js b/csaf_2_1/mandatoryTests/mandatoryTest_6_1_27_5.js similarity index 100% rename from csaf_2_1/mandatoryTests/mandatory_test_6_1_27_5.js rename to csaf_2_1/mandatoryTests/mandatoryTest_6_1_27_5.js diff --git a/tests/csaf_2_1/mandatoryTest_6_1_27_5.js b/tests/csaf_2_1/mandatoryTest_6_1_27_5.js index 785791e1..3dae2b00 100644 --- a/tests/csaf_2_1/mandatoryTest_6_1_27_5.js +++ b/tests/csaf_2_1/mandatoryTest_6_1_27_5.js @@ -1,5 +1,5 @@ import assert from 'node:assert/strict' -import { mandatoryTest_6_1_27_5 } from '../../csaf_2_1/mandatoryTests/mandatory_test_6_1_27_5.js' +import { mandatoryTest_6_1_27_5 } from '../../csaf_2_1/mandatoryTests/mandatoryTest_6_1_27_5.js' describe('mandatoryTest_6_1_27_5', function () { it('only runs on relevant documents', function () {