Skip to content

Commit 095df66

Browse files
committed
feat: add mandatory test 6.1.53
1 parent c8064b6 commit 095df66

File tree

5 files changed

+153
-4
lines changed

5 files changed

+153
-4
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,6 @@ The following tests are not yet implemented and therefore missing:
333333
- Mandatory Test 6.1.50
334334
- Mandatory Test 6.1.51
335335
- Mandatory Test 6.1.52
336-
- Mandatory Test 6.1.53
337336
- Mandatory Test 6.1.54
338337
- Mandatory Test 6.1.55
339338
@@ -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_53: 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_53 } from './mandatoryTests/mandatoryTest_6_1_53.js'
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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.53 of the CSAF 2.1 standard.
57+
*
58+
* @param {any} doc
59+
*/
60+
export function mandatoryTest_6_1_53(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+
doc.vulnerabilities?.forEach((vulnerability, vulnerabilityIndex) => {
80+
const exploitDate = vulnerability.first_known_exploitation_dates || []
81+
exploitDate.forEach((exploit, exploitIdx) => {
82+
const date = exploit.date
83+
const exploitationDate = exploit.exploitation_date
84+
85+
if (
86+
compareZonedDateTimes(
87+
/** @type {string} */ (date),
88+
/** @type {string} */ (exploitationDate)
89+
) < 0
90+
) {
91+
ctx.isValid = false
92+
ctx.errors.push({
93+
instancePath: `/vulnerabilities/${vulnerabilityIndex}/first_known_exploitation_dates/${exploitIdx}`,
94+
message: `the status is ${status}, but the 'exploitation_date' are newer than the 'date'`,
95+
})
96+
}
97+
})
98+
})
99+
100+
return ctx
101+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import assert from 'node:assert/strict'
2+
import {mandatoryTest_6_1_53} from '../../csaf_2_1/mandatoryTests/mandatoryTest_6_1_53.js'
3+
4+
describe('mandatoryTest_6_1_53', function () {
5+
it('only runs on relevant documents', function () {
6+
assert.equal(mandatoryTest_6_1_53({document: 'mydoc'}).isValid, true)
7+
})
8+
9+
10+
it('skips status draft', function () {
11+
assert.equal(
12+
mandatoryTest_6_1_53({
13+
document: {
14+
tracking: {
15+
revision_history: [],
16+
status: 'draft',
17+
},
18+
},
19+
vulnerabilities: []
20+
}).isValid,
21+
true
22+
)
23+
})
24+
25+
it('skips empty vulnerability', function () {
26+
assert.equal(
27+
mandatoryTest_6_1_53({
28+
document: {
29+
tracking: {
30+
revision_history: [],
31+
status: 'final',
32+
},
33+
},
34+
vulnerabilities: [
35+
{},
36+
{ first_known_exploitation_dates: [
37+
{}, // should be ignored
38+
{
39+
"date": "2024-01-24T13:00:00.000Z",
40+
"exploitation_date": "2024-01-24T12:34:56.789Z",
41+
}
42+
]
43+
}]
44+
}).isValid,
45+
true
46+
)
47+
})
48+
})

tests/csaf_2_1/oasis.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { readFile } from 'node:fs/promises'
2-
import { readFileSync } from 'node:fs'
1+
import {readFile} from 'node:fs/promises'
2+
import {readFileSync} from 'node:fs'
33
import assert from 'node:assert/strict'
44
import * as informative from '../../csaf_2_1/informativeTests.js'
55
import * as recommended from '../../csaf_2_1/recommendedTests.js'
@@ -38,7 +38,6 @@ const excluded = [
3838
'6.1.50',
3939
'6.1.51',
4040
'6.1.52',
41-
'6.1.53',
4241
'6.1.54',
4342
'6.1.55',
4443
'6.2.11',

0 commit comments

Comments
 (0)