Skip to content
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,6 @@ The following tests are not yet implemented and therefore missing:
**Informative Tests**

- Informative Test 6.3.13
- Informative Test 6.3.14
- Informative Test 6.3.16
- Informative Test 6.3.17
- Informative Test 6.3.19.1
Expand Down Expand Up @@ -557,6 +556,7 @@ export const informativeTest_6_3_9: DocumentTest
export const informativeTest_6_3_10: DocumentTest
export const informativeTest_6_3_11: DocumentTest
export const informativeTest_6_3_12: DocumentTest
export const informativeTest_6_3_14: DocumentTest
export const informativeTest_6_3_15: DocumentTest
export const informativeTest_6_3_18: DocumentTest
export const informativeTest_6_3_20: DocumentTest
Expand Down
1 change: 1 addition & 0 deletions csaf_2_1/informativeTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export { informativeTest_6_3_5 } from './informativeTests/informativeTest_6_3_5.
export { informativeTest_6_3_6 } from './informativeTests/informativeTest_6_3_6.js'
export { informativeTest_6_3_11 } from './informativeTests/informativeTest_6_3_11.js'
export { informativeTest_6_3_12 } from './informativeTests/informativeTest_6_3_12.js'
export { informativeTest_6_3_14 } from './informativeTests/informativeTest_6_3_14.js'
export { informativeTest_6_3_15 } from './informativeTests/informativeTest_6_3_15.js'
export { informativeTest_6_3_18 } from './informativeTests/informativeTest_6_3_18.js'
export { informativeTest_6_3_20 } from './informativeTests/informativeTest_6_3_20.js'
Expand Down
103 changes: 103 additions & 0 deletions csaf_2_1/informativeTests/informativeTest_6_3_14.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Ajv } from 'ajv/dist/jtd.js'
import { isRegisteredSsvcNamespace } from '#csaf_2_1/shared/ssvcNamespaces.js'

const ajv = new Ajv()

const inputSchema = /** @type {const} */ ({
additionalProperties: true,
properties: {
document: {
additionalProperties: true,
optionalProperties: {
distribution: {
additionalProperties: true,
optionalProperties: {
tlp: {
additionalProperties: true,
optionalProperties: {
label: { type: 'string' },
},
},
},
},
},
},
vulnerabilities: {
elements: {
additionalProperties: true,
optionalProperties: {
metrics: {
elements: {
additionalProperties: true,
optionalProperties: {
content: {
additionalProperties: true,
optionalProperties: {
ssvc_v2: {
additionalProperties: true,
optionalProperties: {
selections: {
elements: {
additionalProperties: true,
optionalProperties: {
namespace: { type: 'string' },
},
},
},
},
},
},
},
},
},
},
},
},
},
},
})

const validateInput = ajv.compile(inputSchema)

/**
* This implements the informative test 6.3.14 of the CSAF 2.1 standard.
*
* For each decision point, it SHALL be tested that the namespace is not an
* unregistered one if the document is not labeled TLP:CLEAR.
*
* @param {unknown} doc
* @returns {{ infos: Array<{ message: string; instancePath: string }> }}
*/
export function informativeTest_6_3_14(doc) {
const ctx = {
infos: /** @type {Array<{ message: string; instancePath: string }>} */ ([]),
}

if (!validateInput(doc)) {
return ctx
}

if (doc.document?.distribution?.tlp?.label === 'CLEAR') {
return ctx
}

const vulnerabilities = doc.vulnerabilities

vulnerabilities.forEach((vulnerability, vulnerabilityIndex) => {
vulnerability.metrics?.forEach((metric, metricIndex) => {
const selections = metric.content?.ssvc_v2?.selections
if (!selections) return
selections.forEach((selection, selectionIndex) => {
if (!selection.namespace) return
if (!isRegisteredSsvcNamespace(selection.namespace)) {
ctx.infos.push({
instancePath: `/vulnerabilities/${vulnerabilityIndex}/metrics/${metricIndex}/content/ssvc_v2/selections/${selectionIndex}/namespace`,
message: `SSVC decision point namespace "${selection.namespace}" is unregistered and the document is not labeled TLP:CLEAR`,
})
}
})
})
})

return ctx
}
117 changes: 117 additions & 0 deletions tests/csaf_2_1/informativeTest_6_3_14.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { informativeTest_6_3_14 } from '../../csaf_2_1/informativeTests.js'

describe('informativeTest_6_3_14', function () {
it('only runs on relevant documents', function () {
expect(informativeTest_6_3_14({ document: 'mydoc' }).infos.length).toBe(0)
})

it('skips metrics without selections', function () {
expect(
informativeTest_6_3_14({
document: {
distribution: {
tlp: {
label: 'GREEN',
},
},
},
vulnerabilities: [
{},
{
cve: 'CVE-1900-0001',
metrics: [
{
content: {},
products: ['CSAFPID-9080700'],
},
],
},
],
}).infos.length
).toBe(0)
})

it('does not report unregistered namespaces when the document is labeled TLP:CLEAR', function () {
expect(
informativeTest_6_3_14({
document: {
distribution: {
tlp: {
label: 'CLEAR',
},
},
},
vulnerabilities: [
{
cve: 'CVE-1900-0001',
metrics: [
{
content: {
ssvc_v2: {
id: 'CVE-1900-0001',
schemaVersion: '1-0-1',
selections: [
{
name: 'Technical Impact',
namespace: 'acme',
values: ['Total'],
version: '1.0.0',
},
],
timestamp: '2024-01-24T10:00:00.000Z',
},
},
products: ['CSAFPID-9080700'],
},
],
},
],
}).infos.length
).toBe(0)
})

it('skips selections without namespace and reports the unregistered ones', function () {
expect(
informativeTest_6_3_14({
document: {
distribution: {
tlp: {
label: 'GREEN',
},
},
},
vulnerabilities: [
{},
{
cve: 'CVE-1900-0001',
metrics: [
{
content: {
ssvc_v2: {
id: 'CVE-1900-0001',
schemaVersion: '1-0-1',
selections: [
{
name: 'Technical Impact',
namespace: 'acme',
values: ['Total'],
version: '1.0.0',
},
{
name: 'Technical Impact',
values: ['Total'],
version: '1.0.0',
},
],
timestamp: '2024-01-24T10:00:00.000Z',
},
},
products: ['CSAFPID-9080700'],
},
],
},
],
}).infos.length
).toBe(1)
})
})
1 change: 0 additions & 1 deletion tests/csaf_2_1/oasis.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ const excluded = [
'6.2.54.4',
'6.3.12',
'6.3.13',
'6.3.14',
'6.3.16',
'6.3.17',
'6.3.19.1',
Expand Down
Loading