Skip to content

Commit 11baaec

Browse files
Merge pull request #411 from secvisogram/feat/373-csaf-2.1-mandatory-test-6.1.44
feat(CSAF2.1): #373 add mandatory test 6.1.44
2 parents b084e83 + 67a00ea commit 11baaec

File tree

5 files changed

+207
-2
lines changed

5 files changed

+207
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,6 @@ The following tests are not yet implemented and therefore missing:
313313
314314
- Mandatory Test 6.1.26
315315
- Mandatory Test 6.1.27.13
316-
- Mandatory Test 6.1.44
317316
- Mandatory Test 6.1.46
318317
- Mandatory Test 6.1.47
319318
- Mandatory Test 6.1.48
@@ -426,6 +425,7 @@ export const mandatoryTest_6_1_40: DocumentTest
426425
export const mandatoryTest_6_1_41: DocumentTest
427426
export const mandatoryTest_6_1_42: DocumentTest
428427
export const mandatoryTest_6_1_43: DocumentTest
428+
export const mandatoryTest_6_1_44: DocumentTest
429429
export const mandatoryTest_6_1_45: DocumentTest
430430
export const mandatoryTest_6_1_51: DocumentTest
431431
export const mandatoryTest_6_1_52: DocumentTest

csaf_2_1/mandatoryTests.js

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

0 commit comments

Comments
 (0)