Skip to content

Commit 52c4e97

Browse files
feat(CSAF2.1): #373 add mandatory test 6.1.44
1 parent 54d3dd5 commit 52c4e97

File tree

5 files changed

+211
-2
lines changed

5 files changed

+211
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,6 @@ The following tests are not yet implemented and therefore missing:
324324
- Mandatory Test 6.1.36
325325
- Mandatory Test 6.1.42
326326
- Mandatory Test 6.1.43
327-
- Mandatory Test 6.1.44
328327
- Mandatory Test 6.1.45
329328
- Mandatory Test 6.1.46
330329
- Mandatory Test 6.1.47
@@ -435,6 +434,7 @@ export const mandatoryTest_6_1_38: DocumentTest
435434
export const mandatoryTest_6_1_39: DocumentTest
436435
export const mandatoryTest_6_1_40: DocumentTest
437436
export const mandatoryTest_6_1_41: DocumentTest
437+
export const mandatoryTest_6_1_44: DocumentTest
438438
```
439439
440440
[(back to top)](#bsi-csaf-validator-lib)

csaf_2_1/mandatoryTests.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,4 @@ export { mandatoryTest_6_1_38 } from './mandatoryTests/mandatoryTests_6_1_38.js'
4949
export { mandatoryTest_6_1_39 } from './mandatoryTests/mandatoryTest_6_1_39.js'
5050
export { mandatoryTest_6_1_40 } from './mandatoryTests/mandatoryTest_6_1_40.js'
5151
export { mandatoryTest_6_1_41 } from './mandatoryTests/mandatoryTest_6_1_41.js'
52+
export { mandatoryTest_6_1_44 } from './mandatoryTests/mandatoryTest_6_1_44.js'
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
import Ajv from 'ajv/dist/jtd.js'
2+
3+
const ajv = new Ajv()
4+
5+
const branchSchema = /** @type {const} */ ({
6+
additionalProperties: true,
7+
optionalProperties: {
8+
branches: {
9+
elements: {
10+
additionalProperties: true,
11+
properties: {},
12+
},
13+
},
14+
product: {
15+
additionalProperties: true,
16+
optionalProperties: {
17+
product_identification_helper: {
18+
additionalProperties: true,
19+
optionalProperties: {
20+
serial_numbers: { elements: { type: 'string' } },
21+
},
22+
},
23+
},
24+
},
25+
},
26+
})
27+
28+
const validateBranch = ajv.compile(branchSchema)
29+
30+
const fullProductNameSchema = /** @type {const} */ ({
31+
additionalProperties: true,
32+
optionalProperties: {
33+
product_identification_helper: {
34+
additionalProperties: true,
35+
optionalProperties: {
36+
serial_numbers: { elements: { type: 'string' } },
37+
},
38+
},
39+
},
40+
})
41+
42+
/*
43+
This is the jtd schema that needs to match the input document so that the
44+
test is activated. If this schema doesn't match, it normally means that the input
45+
document does not validate against the csaf JSON schema or optional fields that
46+
the test checks are not present.
47+
*/
48+
const inputSchema = /** @type {const} */ ({
49+
additionalProperties: true,
50+
optionalProperties: {
51+
product_tree: {
52+
additionalProperties: true,
53+
optionalProperties: {
54+
branches: {
55+
elements: branchSchema,
56+
},
57+
full_product_names: {
58+
elements: fullProductNameSchema,
59+
},
60+
relationships: {
61+
elements: {
62+
additionalProperties: true,
63+
optionalProperties: {
64+
full_product_name: fullProductNameSchema,
65+
},
66+
},
67+
},
68+
},
69+
},
70+
},
71+
})
72+
73+
const validate = ajv.compile(inputSchema)
74+
75+
/**
76+
* @typedef {import('ajv/dist/core').JTDDataType<typeof branchSchema>} Branch
77+
* @typedef {import('ajv/dist/core').JTDDataType<typeof fullProductNameSchema>} FullProductName
78+
*/
79+
80+
/**
81+
*
82+
* @param {string} stringToCheck
83+
* @return {boolean}
84+
*/
85+
export function containMultipleUnescapedStars(stringToCheck) {
86+
const regex = /\*/g
87+
return (stringToCheck.replace(/\\\*/g, '').match(regex)?.length ?? 0) > 1
88+
}
89+
90+
/**
91+
* Validates all given serial numbers and
92+
* check whether they contain multiple unescaped stars
93+
*
94+
* @param {Array<string> | undefined} serialNumbers serial_numbers to check
95+
* @return {Array<string>} indexes of the serial_numbers that invalid
96+
*/
97+
export function checkSerialNumbers(serialNumbers) {
98+
/** @type {Array<string>}*/
99+
const invalidNumbers = []
100+
if (serialNumbers) {
101+
for (let i = 0; i < serialNumbers.length; i++) {
102+
const modelNumber = serialNumbers[i]
103+
if (containMultipleUnescapedStars(modelNumber)) {
104+
invalidNumbers.push('' + i)
105+
}
106+
}
107+
}
108+
return invalidNumbers
109+
}
110+
111+
/**
112+
* For each serial number, it MUST be tested
113+
* that it does not contain multiple unescaped stars.
114+
*
115+
* @param {unknown} doc
116+
*/
117+
export function mandatoryTest_6_1_44(doc) {
118+
/*
119+
The `ctx` variable holds the state that is accumulated during the test run and is
120+
finally returned by the function.
121+
*/
122+
const ctx = {
123+
errors:
124+
/** @type {Array<{ instancePath: string; message: string }>} */ ([]),
125+
isValid: true,
126+
}
127+
128+
if (!validate(doc)) {
129+
return ctx
130+
}
131+
132+
doc.product_tree?.branches?.forEach((branch, index) => {
133+
checkBranch(`/product_tree/branches/${index}`, branch)
134+
})
135+
136+
doc.product_tree?.full_product_names?.forEach((fullProduceName, index) => {
137+
checkFullProductName(
138+
`/product_tree/full_product_names/${index}`,
139+
fullProduceName
140+
)
141+
})
142+
143+
doc.product_tree?.relationships?.forEach((relationship, index) => {
144+
const fullProductName = relationship.full_product_name
145+
if (fullProductName) {
146+
checkFullProductName(
147+
`/product_tree/relationships/${index}/full_product_name`,
148+
fullProductName
149+
)
150+
}
151+
})
152+
153+
return ctx
154+
155+
/**
156+
* Check whether the serial numbers contain multiple unescaped stars
157+
*
158+
* @param {string} prefix The instance path prefix of the "full product name". It is
159+
* used to generate error messages.
160+
* @param {FullProductName} fullProductName The "full product name" object.
161+
*/
162+
function checkFullProductName(prefix, fullProductName) {
163+
const invalidNumberIndexes = checkSerialNumbers(
164+
fullProductName.product_identification_helper?.serial_numbers
165+
)
166+
invalidNumberIndexes.forEach((invalidNumberIndex) => {
167+
ctx.isValid = false
168+
ctx.errors.push({
169+
instancePath: `${prefix}/product_identification_helper/serial_numbers/${invalidNumberIndex}`,
170+
message: `Serial number contains multiple unescaped stars`,
171+
})
172+
})
173+
}
174+
175+
/**
176+
* Check whether the model numbers contain multiple unescaped stars for the given branch object
177+
* and its branch children.
178+
*
179+
* @param {string} prefix The instance path prefix of the "branch". It is
180+
* used to generate error messages.
181+
* @param {Branch} branch The "branch" object.
182+
*/
183+
function checkBranch(prefix, branch) {
184+
const invalidNumberIndexes = checkSerialNumbers(
185+
branch.product?.product_identification_helper?.serial_numbers
186+
)
187+
invalidNumberIndexes.forEach((invalidNumberIndex) => {
188+
ctx.isValid = false
189+
ctx.errors.push({
190+
instancePath: `${prefix}/product/product_identification_helper/serial_numbers/${invalidNumberIndex}`,
191+
message: `Serial number contains multiple unescaped stars`,
192+
})
193+
})
194+
branch.branches?.forEach((branch, index) => {
195+
if (validateBranch(branch)) {
196+
checkBranch(`${prefix}/branches/${index}`, branch)
197+
}
198+
})
199+
}
200+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import assert from 'node:assert/strict'
2+
3+
import { mandatoryTest_6_1_44 } from '../../csaf_2_1/mandatoryTests/mandatoryTest_6_1_44.js'
4+
5+
describe('mandatoryTest_6_1_44', function () {
6+
it('only runs on relevant documents', function () {
7+
assert.equal(mandatoryTest_6_1_44({ product_tree: 'mydoc' }).isValid, true)
8+
})
9+
})

tests/csaf_2_1/oasis.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ const excluded = [
2929
'6.1.37',
3030
'6.1.42',
3131
'6.1.43',
32-
'6.1.44',
3332
'6.1.45',
3433
'6.1.46',
3534
'6.1.47',

0 commit comments

Comments
 (0)