Skip to content

Commit a0d39f4

Browse files
committed
feat: add mandatory test 6.1.52
1 parent c8064b6 commit a0d39f4

File tree

5 files changed

+178
-2
lines changed

5 files changed

+178
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,6 @@ The following tests are not yet implemented and therefore missing:
332332
- Mandatory Test 6.1.49
333333
- Mandatory Test 6.1.50
334334
- Mandatory Test 6.1.51
335-
- Mandatory Test 6.1.52
336335
- Mandatory Test 6.1.53
337336
- Mandatory Test 6.1.54
338337
- Mandatory Test 6.1.55
@@ -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_52: 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_52 } from './mandatoryTests/mandatoryTest_6_1_52.js'
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import Ajv from 'ajv/dist/jtd.js'
2+
import {compareZonedDateTimes} from "../../lib/shared/dateHelper.js";
3+
4+
const ajv = new Ajv()
5+
6+
/*
7+
This is the jtd schema that needs to match the input document so that the
8+
test is activated. If this schema doesn't match it normally means that the input
9+
document does not validate against the csaf json schema or optional fields that
10+
the test checks are not present.
11+
*/
12+
const inputSchema = /** @type {const} */ ({
13+
additionalProperties: true,
14+
properties: {
15+
document: {
16+
additionalProperties: true,
17+
properties: {
18+
tracking: {
19+
additionalProperties: true,
20+
properties: {
21+
revision_history: {
22+
elements: {
23+
additionalProperties: true,
24+
optionalProperties: {
25+
date: {type: 'string'},
26+
},
27+
},
28+
},
29+
status: {type: 'string'},
30+
},
31+
},
32+
},
33+
},
34+
vulnerabilities: {
35+
elements: {
36+
additionalProperties: true,
37+
optionalProperties: {
38+
first_known_exploitation_dates: {
39+
elements: {
40+
additionalProperties: true,
41+
optionalProperties: {
42+
date: {type: 'string'},
43+
exploitation_date: {type: 'string'},
44+
},
45+
},
46+
},
47+
},
48+
},
49+
},
50+
},
51+
})
52+
53+
const validate = ajv.compile(inputSchema)
54+
55+
/**
56+
* This implements the mandatory test 6.1.52 of the CSAF 2.1 standard.
57+
*
58+
* @param {any} doc
59+
*/
60+
export function mandatoryTest_6_1_52(doc) {
61+
/*
62+
The `ctx` variable holds the state that is accumulated during the test ran and is
63+
finally returned by the function.
64+
*/
65+
const ctx = {
66+
errors:
67+
/** @type {Array<{ instancePath: string; message: string }>} */ ([]),
68+
isValid: true,
69+
}
70+
71+
if (!validate(doc)) {
72+
return ctx
73+
}
74+
const status = doc.document.tracking.status
75+
if (status !== 'final' && status !== 'interim') {
76+
return ctx
77+
}
78+
79+
const newestRevisionHistoryItem = doc.document.tracking.revision_history
80+
.filter(item => item.date != null)
81+
.slice()
82+
.sort((a, z) =>
83+
compareZonedDateTimes(
84+
/** @type {string} */ (z.date),
85+
/** @type {string} */ (a.date)
86+
)
87+
)[0]
88+
89+
doc.vulnerabilities?.forEach((vulnerability, vulnerabilityIndex) => {
90+
const exploitDate = vulnerability.first_known_exploitation_dates || []
91+
exploitDate.forEach((exploit, exploitIdx) => {
92+
93+
const date = exploit.date
94+
const exploitationDate = exploit.exploitation_date
95+
if (
96+
newestRevisionHistoryItem &&
97+
compareZonedDateTimes(
98+
/** @type {string} */ (newestRevisionHistoryItem.date),
99+
/** @type {string} */ (date)
100+
) < 0
101+
) {
102+
ctx.isValid = false
103+
ctx.errors.push({
104+
instancePath: `/vulnerabilities/${vulnerabilityIndex}/first_known_exploitation_dates/${exploitIdx}/date`,
105+
message: `the status is ${status}, but the 'date' of the First Known Exploitation Dates are newer than the newest revision history date`,
106+
})
107+
}
108+
if (
109+
newestRevisionHistoryItem &&
110+
compareZonedDateTimes(
111+
/** @type {string} */ (newestRevisionHistoryItem.date),
112+
/** @type {string} */ (exploitationDate)
113+
) < 0
114+
) {
115+
ctx.isValid = false
116+
ctx.errors.push({
117+
instancePath: `/vulnerabilities/${vulnerabilityIndex}/first_known_exploitation_dates/${exploitIdx}/exploitation_date`,
118+
message: `the status is ${status}, but the 'exploitation_date' of the First Known Exploitation Dates are newer than the newest revision history date`,
119+
})
120+
}
121+
})
122+
})
123+
124+
return ctx
125+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import assert from 'node:assert/strict'
2+
import {mandatoryTest_6_1_52} from '../../csaf_2_1/mandatoryTests/mandatoryTest_6_1_52.js'
3+
4+
describe('mandatoryTest_6_1_52', function () {
5+
it('only runs on relevant documents', function () {
6+
assert.equal(mandatoryTest_6_1_52({document: 'mydoc'}).isValid, true)
7+
})
8+
9+
it('skips status draft', function () {
10+
assert.equal(
11+
mandatoryTest_6_1_52({
12+
document: {
13+
tracking: {
14+
revision_history: [],
15+
status: 'draft',
16+
},
17+
},
18+
vulnerabilities: []
19+
}).isValid,
20+
true
21+
)
22+
})
23+
24+
it('ignores empty objects in revision_history and first_known_exploitation_dates', function () {
25+
assert.equal(
26+
mandatoryTest_6_1_52({
27+
document: {
28+
tracking: {
29+
revision_history: [
30+
{}, // should be ignored
31+
{
32+
"date": "2024-01-24T10:00:00.000Z"
33+
}
34+
],
35+
status: 'final',
36+
},
37+
},
38+
vulnerabilities: [
39+
{
40+
first_known_exploitation_dates: [
41+
{}, // should be ignored
42+
{
43+
"date": "2024-01-24T13:00:00.000Z",
44+
"exploitation_date": "2024-01-24T12:34:56.789Z",
45+
}
46+
],
47+
},
48+
]
49+
}).isValid, false)
50+
})
51+
})

tests/csaf_2_1/oasis.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ const excluded = [
3737
'6.1.49',
3838
'6.1.50',
3939
'6.1.51',
40-
'6.1.52',
4140
'6.1.53',
4241
'6.1.54',
4342
'6.1.55',

0 commit comments

Comments
 (0)