diff --git a/.mocharc.yml b/.mocharc.yml new file mode 100644 index 00000000..d1d36750 --- /dev/null +++ b/.mocharc.yml @@ -0,0 +1,2 @@ +node-option: + - "experimental-wasm-modules" diff --git a/README.md b/README.md index 0dbf9c1c..ca251240 100644 --- a/README.md +++ b/README.md @@ -317,7 +317,6 @@ The following tests are not yet implemented and therefore missing: - Mandatory Test 6.1.47 - Mandatory Test 6.1.48 - Mandatory Test 6.1.49 -- Mandatory Test 6.1.50 - Mandatory Test 6.1.53 - Mandatory Test 6.1.54 - Mandatory Test 6.1.55 @@ -424,6 +423,7 @@ export const mandatoryTest_6_1_42: DocumentTest export const mandatoryTest_6_1_43: DocumentTest export const mandatoryTest_6_1_44: DocumentTest export const mandatoryTest_6_1_45: DocumentTest +export const mandatoryTest_6_1_50: DocumentTest export const mandatoryTest_6_1_51: DocumentTest export const mandatoryTest_6_1_52: DocumentTest ``` diff --git a/csaf_2_1/mandatoryTests.js b/csaf_2_1/mandatoryTests.js index c302572e..9d5c0773 100644 --- a/csaf_2_1/mandatoryTests.js +++ b/csaf_2_1/mandatoryTests.js @@ -62,5 +62,6 @@ export { mandatoryTest_6_1_42 } from './mandatoryTests/mandatoryTest_6_1_42.js' export { mandatoryTest_6_1_43 } from './mandatoryTests/mandatoryTest_6_1_43.js' export { mandatoryTest_6_1_44 } from './mandatoryTests/mandatoryTest_6_1_44.js' export { mandatoryTest_6_1_45 } from './mandatoryTests/mandatoryTest_6_1_45.js' +export { mandatoryTest_6_1_50 } from '../lib/mandatoryTests/mandatoryTest_6_1_50.js' export { mandatoryTest_6_1_51 } from './mandatoryTests/mandatoryTest_6_1_51.js' export { mandatoryTest_6_1_52 } from './mandatoryTests/mandatoryTest_6_1_52.js' diff --git a/lib/mandatoryTests/mandatoryTest_6_1_50.js b/lib/mandatoryTests/mandatoryTest_6_1_50.js new file mode 100644 index 00000000..0a3d6462 --- /dev/null +++ b/lib/mandatoryTests/mandatoryTest_6_1_50.js @@ -0,0 +1,115 @@ +import Ajv from 'ajv/dist/jtd.js' +import { parse } from '@csaf-rs/vers-rs' + +const ajv = new Ajv() + +const inputSchema = /** @type {const} */ ({ + additionalProperties: true, + optionalProperties: { + product_tree: { + additionalProperties: true, + optionalProperties: { + branches: { elements: { additionalProperties: true, properties: {} } }, + }, + }, + }, +}) + +const branchSchema = /** @type {const} */ ({ + additionalProperties: true, + optionalProperties: { + category: { type: 'string' }, + name: { type: 'string' }, + branches: { elements: { additionalProperties: true, properties: {} } }, + }, +}) + +const validateInput = ajv.compile(inputSchema) +const validateBranch = ajv.compile(branchSchema) + +/** + * @param {any} doc + */ +export function mandatoryTest_6_1_50(doc) { + /** @type {Array<{ message: string; instancePath: string }>} */ + const errors = [] + + if (!validateInput(doc)) { + return { isValid: true, errors } + } + + let isValid = true + + // Check branches if they exist + if (doc.product_tree?.branches) { + isValid = checkBranches({ + path: '/product_tree/branches', + branches: doc.product_tree.branches, + errors, + }) + } + + return { isValid, errors } +} + +/** + * @param {object} params + * @param {string} params.path + * @param {unknown[]} params.branches + * @param {Array<{ message: string; instancePath: string }>} params.errors + * @returns {boolean} isValid + */ +function checkBranches({ path, branches, errors }) { + let isValid = true + + branches.forEach((branch, branchIndex) => { + if (!validateBranch(branch)) { + return + } + + if ( + branch.category === 'product_version_range' && + typeof branch.name === 'string' + ) { + const version = branch.name + + if (version.startsWith('vers:')) { + const parseResult = parseVers(version) + if (!parseResult.ok) { + isValid = false + errors.push({ + message: `${parseResult.error}`, + instancePath: `${path}/${branchIndex}`, + }) + } + } else { + // TODO: Vers-like Specifier (VLS) – noch nicht implementiert, da kein Repository vorhanden + } + } + + // Recursively check nested branches + if (Array.isArray(branch.branches)) { + const nestedValid = checkBranches({ + path: `${path}/${branchIndex}/branches`, + branches: branch.branches, + errors, + }) + if (!nestedValid) isValid = false + } + }) + + return isValid +} + +/** + * @param {string} version + * @returns {{ ok: true } | { ok: false; error: string }} + */ +function parseVers(version) { + try { + parse(version) + return { ok: true } + } catch (e) { + return { ok: false, error: e instanceof Error ? e.message : String(e) } + } +} diff --git a/package-lock.json b/package-lock.json index c2cf33c4..c7479c54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "2.0.18", "license": "MIT", "dependencies": { + "@csaf-rs/vers-rs": "^0.1.2", "@js-joda/core": "^5.6.1", "@js-joda/timezone": "^2.18.2", "ajv": "^8.11.2", @@ -49,6 +50,12 @@ "node": ">=18" } }, + "node_modules/@csaf-rs/vers-rs": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@csaf-rs/vers-rs/-/vers-rs-0.1.2.tgz", + "integrity": "sha512-MVk+d8Oxt4ndPYfP2ZCLirAjPGZo9JKrrJ1awaRSz4Yx5SfxjhYY+lqw8DE4cnVYH4Ev8cQNL1X502s/Lq+1uA==", + "license": "Apache-2.0" + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1250,9 +1257,9 @@ } }, "node_modules/serialize-javascript": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.2.tgz", - "integrity": "sha512-Y/agDAqbUWRYFWLF5pMT9Rb8wN5ERPMbQH7oq6R+4YgFdMNO4+ELo4PjFCW3H+2CbXirPr/XUgYT+3iXJfTbZQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.4.tgz", + "integrity": "sha512-DuGdB+Po43Q5Jxwpzt1lhyFSYKryqoNjQSA9M92tyw0lyHIOur+XCalOUe0KTJpyqzT8+fQ5A0Jf7vCx/NKmIg==", "dev": true, "license": "BSD-3-Clause", "engines": { diff --git a/package.json b/package.json index ae72b356..6e2f57c6 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "scripts": { "pretest": "tsc -b .", "test": "prettier --check . && node scripts/test.js", + "test-vers": "node --experimental-wasm-modules csaf_2_1/test-vers-integration.js", "pretest-report": "tsc -b .", "test-report": "prettier --check . && node scripts/test.js --reporter json > test-results.json", "test-coverage": "c8 node scripts/test.js", @@ -23,6 +24,7 @@ "access": "public" }, "dependencies": { + "@csaf-rs/vers-rs": "^0.1.2", "@js-joda/core": "^5.6.1", "@js-joda/timezone": "^2.18.2", "ajv": "^8.11.2", diff --git a/tests/csaf_2_1/oasis.js b/tests/csaf_2_1/oasis.js index b0a39ebf..4f4a78b7 100644 --- a/tests/csaf_2_1/oasis.js +++ b/tests/csaf_2_1/oasis.js @@ -23,7 +23,6 @@ const excluded = [ '6.1.47', '6.1.48', '6.1.49', - '6.1.50', '6.1.53', '6.1.54', '6.1.55',