Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,6 @@ The following tests are not yet implemented and therefore missing:
- Recommended Test 6.2.50.3
- Recommended Test 6.2.51
- Recommended Test 6.2.54.1
- Recommended Test 6.2.54.2
- Recommended Test 6.2.54.4

**Informative Tests**
Expand Down Expand Up @@ -526,6 +525,7 @@ export const recommendedTest_6_2_48: DocumentTest
export const recommendedTest_6_2_49: DocumentTest
export const recommendedTest_6_2_52: DocumentTest
export const recommendedTest_6_2_53: DocumentTest
export const recommendedTest_6_2_54_2: DocumentTest
export const recommendedTest_6_2_54_3: DocumentTest
```

Expand Down
2 changes: 2 additions & 0 deletions csaf_2_1/csafAjv.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import cvss_meta from './csafAjv/cvss_meta.js'
import meta_format_assertion from './csafAjv/meta-format-assertion.js'
import draft_07_schema from './csafAjv/draft-07-schema.js'
import selectionList_2_0_0Schema from './csafAjv/SelectionList_2_0_0.schema.js'
import { registerExtensionSchemas } from './csafAjv/extensionSchemas/index.js'

import { validateTimestamp } from './dateHelper.js'

Expand Down Expand Up @@ -45,6 +46,7 @@ csafAjv.addSchema(
selectionList_2_0_0Schema,
'https://certcc.github.io/SSVC/data/schema/v2/SelectionList_2_0_0.schema.json'
)
registerExtensionSchemas(csafAjv)

csafAjv.addFormat('date-time', {
type: 'string',
Expand Down
54 changes: 54 additions & 0 deletions csaf_2_1/csafAjv/extensionSchemas/documentation-11.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// https://raw.githubusercontent.com/oasis-tcs/csaf/refs/heads/master/csaf_2.1/extension/data/valid/documentation-11/documentation-11-content_1.0.0.json
// Copied from ../../../csaf/csaf_2.1/test/extension/data/valid/documentation-11/documentation-11-content_1.0.0.json
export default {
$schema:
'https://docs.oasis-open.org/csaf/csaf/v2.1/schema/extension-metaschema.json',
$id: 'https://raw.githubusercontent.com/oasis-tcs/csaf/refs/heads/master/csaf_2.1/extension/data/valid/documentation-11/documentation-11-content_1.0.0.json',
title: 'CSAF Extension: Documentation 11',
description:
'Representation of a valid extension used for documentation purposes.',
type: 'object',
required: ['$schema', 'category', 'critical', 'content'],
properties: {
$schema: {
title: 'CSAF Extension Schema',
description:
'Contains the URL of the CSAF Extension JSON schema which the JSON object promises to be valid for.',
type: 'string',
format: 'uri',
const:
'https://raw.githubusercontent.com/oasis-tcs/csaf/refs/heads/master/csaf_2.1/extension/data/valid/documentation-11/documentation-11-content_1.0.0.json',
},
category: {
title: 'Extension Category',
description: 'Holds the category of the extension content.',
type: 'string',
enum: ['essential', 'significant', 'supplementary'],
},
content: {
title: 'Content',
description: 'Contains the additional information in its properties.',
type: 'object',
minProperties: 1,
properties: {
documentation: {
title: 'Documentation content',
description:
'Contains a constant string to clarify that this is for documentation purposes only and not to be used in production.',
type: 'string',
const:
'This extension is for documentation and test purposed only. It is valid. It is not allowed to be used in a production CSAF.',
},
},
unevaluatedProperties: false,
},
critical: {
title: 'Critical',
description:
'Determines whether using the extension would fail a mandatory test.',
type: 'boolean',
const: false,
},
},
additionalProperties: false,
}
68 changes: 68 additions & 0 deletions csaf_2_1/csafAjv/extensionSchemas/documentation-12.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// https://raw.githubusercontent.com/oasis-tcs/csaf/refs/heads/master/csaf_2.1/extension/data/valid/documentation-12/documentation-12-content_1.0.0.json
// Copied from ../../../csaf/csaf_2.1/test/extension/data/valid/documentation-12/documentation-12-content_1.0.0.json
export default {
$schema:
'https://docs.oasis-open.org/csaf/csaf/v2.1/schema/extension-metaschema.json',
$id: 'https://raw.githubusercontent.com/oasis-tcs/csaf/refs/heads/master/csaf_2.1/extension/data/valid/documentation-12/documentation-12-content_1.0.0.json',
title: 'CSAF Extension: Documentation 12',
description:
'Representation of a valid extension used for documentation purposes.',
type: 'object',
required: ['$schema', 'category', 'critical', 'content'],
properties: {
$schema: {
title: 'CSAF Extension Schema',
description:
'Contains the URL of the CSAF Extension JSON schema which the JSON object promises to be valid for.',
type: 'string',
format: 'uri',
const:
'https://raw.githubusercontent.com/oasis-tcs/csaf/refs/heads/master/csaf_2.1/extension/data/valid/documentation-12/documentation-12-content_1.0.0.json',
},
category: {
title: 'Extension Category',
description: 'Holds the category of the extension content.',
type: 'string',
enum: ['essential', 'significant', 'supplementary'],
},
content: {
title: 'Content',
description: 'Contains the additional information in its properties.',
type: 'object',
minProperties: 1,
required: ['documentation'],
properties: {
documentation: {
title: 'Documentation content',
description:
'Contains a constant string to clarify that this is for documentation purposes only and not to be used in production.',
type: 'string',
const:
'This extension is for documentation and test purposed only. It is valid. It is not allowed to be used in a production CSAF.',
},
notes: {
title: 'Notes',
description:
'Contains a list of notes to convey a more complex structure.',
type: 'array',
minItems: 1,
items: {
title: 'Entry',
description: 'Contains a single entry.',
type: 'string',
minLength: 1,
},
},
},
unevaluatedProperties: false,
},
critical: {
title: 'Critical',
description:
'Determines whether using the extension would fail a mandatory test.',
type: 'boolean',
const: false,
},
},
additionalProperties: false,
}
129 changes: 129 additions & 0 deletions csaf_2_1/csafAjv/extensionSchemas/documentation-13.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// https://raw.githubusercontent.com/oasis-tcs/csaf/refs/heads/master/csaf_2.1/extension/data/valid/documentation-13/documentation-13-content_1.0.0.json
// Copied from ../../../csaf/csaf_2.1/test/extension/data/valid/documentation-13/documentation-13-content_1.0.0.json
export default {
$schema:
'https://docs.oasis-open.org/csaf/csaf/v2.1/schema/extension-metaschema.json',
$id: 'https://raw.githubusercontent.com/oasis-tcs/csaf/refs/heads/master/csaf_2.1/extension/data/valid/documentation-13/documentation-13-content_1.0.0.json',
title: 'CSAF Extension: Documentation 13',
description:
'Representation of a valid extension for configurations used for documentation purposes.',
type: 'object',
$defs: {
configuration_id_t: {
title: 'Reference token for configuration instance',
description:
'Token required to identify a configuration so that it can be referred to from other parts in the document. There is no predefined or required format for the configuration_id as long as it uniquely identifies a configuration in the context of the current document.',
type: 'string',
minLength: 1,
examples: ['CSAFCID-0001', 'CSAFCID-0002'],
},
configuration_t: {
title: 'Configuration',
description:
'Contains the description of a configuration and assigns it a configuration id.',
type: 'object',
required: ['configuration_id', 'details'],
properties: {
configuration_id: { $ref: '#/$defs/configuration_id_t' },
details: {
title: 'Details of the Configuration',
description:
'Contains all details as human-readable description on how to identify the configuration.',
type: 'string',
minLength: 1,
},
},
additionalProperties: false,
},
relationship_t: {
title: 'Relationship',
description:
'Establishes a link between an existing full_product_name_t element and a configuration, allowing the document producer to define a combination that form a new full_product_name entry.',
type: 'object',
required: [
'configuration_reference',
'full_product_name',
'product_reference',
],
properties: {
configuration_reference: {
title: 'Configuration Reference',
description:
'Holds a Configuration ID that refers to the Configuration element, which is referenced as the second element of the relationship.',
$ref: '#/$defs/configuration_id_t',
},
full_product_name: {
$ref: 'https://docs.oasis-open.org/csaf/csaf/v2.1/schema/csaf.json#/$defs/full_product_name_t',
},
product_reference: {
title: 'Product reference',
description:
'Holds a Product ID that refers to the Full Product Name element, which is referenced as the first element of the relationship.',
$ref: 'https://docs.oasis-open.org/csaf/csaf/v2.1/schema/csaf.json#/$defs/product_id_t',
},
},
additionalProperties: false,
},
},
required: ['$schema', 'category', 'critical', 'content'],
properties: {
$schema: {
title: 'CSAF Extension Schema',
description:
'Contains the URL of the CSAF Extension JSON schema which the JSON object promises to be valid for.',
type: 'string',
format: 'uri',
const:
'https://raw.githubusercontent.com/oasis-tcs/csaf/refs/heads/master/csaf_2.1/extension/data/valid/documentation-13/documentation-13-content_1.0.0.json',
},
category: {
title: 'Extension Category',
description: 'Holds the category of the extension content.',
type: 'string',
enum: ['essential', 'significant', 'supplementary'],
},
content: {
title: 'Content',
description: 'Contains the additional information in its properties.',
type: 'object',
minProperties: 1,
properties: {
configuration_tree: {
title: 'Configuration Tree',
description:
'Contains configurations and their bindings to products.',
type: 'object',
required: ['configurations', 'relationships'],
properties: {
configurations: {
title: 'List of Configurations',
description: 'Contains a list of configuration elements.',
type: 'array',
minItems: 1,
uniqueItems: true,
items: { $ref: '#/$defs/configuration_t' },
},
relationships: {
title: 'List of Relationships',
description:
'Contains a list of product-configuration combinations.',
type: 'array',
minItems: 1,
uniqueItems: true,
items: { $ref: '#/$defs/relationship_t' },
},
},
},
},
unevaluatedProperties: false,
},
critical: {
title: 'Critical',
description:
'Determines whether using the extension would fail a mandatory test.',
type: 'boolean',
const: true,
},
},
additionalProperties: false,
}
78 changes: 78 additions & 0 deletions csaf_2_1/csafAjv/extensionSchemas/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import documentation11 from './documentation-11.js'
import documentation12 from './documentation-12.js'
import documentation13 from './documentation-13.js'

/**
* @typedef {object} ExtensionSchemaEntry
* @property {import('ajv').AnySchemaObject} schema
* @property {'official' | 'registered' | 'experimental'} class Class per
* spec section 2.4.1 ("Classes").
* @property {boolean} deprecated On the OASIS deprecated list (spec 2.4.2)?
* @property {boolean} denyListed On the OASIS deny list (spec 2.4.2)? MUST
* NOT be used if true.
*/

/**
* Allow list of CSAF Extension Schemas this validator implements locally
* (per spec ch. 8, schemas are never fetched over the network).
*
* The OASIS official/registered/deprecated/deny-listed lists (spec 2.4.2)
* aren't available as data here, so entries are limited to the
* documentation-11/12/13 test fixtures (not for production use).
* `documentation-11`'s class is fixed by the spec's own prose (recommended
* tests 6.2.54.1/.2/.4): it's the failing example for "neither official nor
* registered", i.e. `experimental`. `documentation-12`/`-13` have no such
* fixture tie-in and are arbitrarily assigned `registered`/`official` so all
* three code paths of recommendedTest_6_2_54_1 are exercised.
*
* To add a new schema: add a module next to this file (its `$id` must match
* the `$schema` value used in documents), import it here, and add an entry.
*
* @type {ExtensionSchemaEntry[]}
*/
export const extensionSchemas = [
{
schema: documentation11,
class: 'experimental',
deprecated: false,
denyListed: false,
},
{
schema: documentation12,
class: 'registered',
deprecated: false,
denyListed: false,
},
{
schema: documentation13,
class: 'official',
deprecated: false,
denyListed: false,
},
]

/**
* Registers all allow-listed extension schemas on the given Ajv instance so
* that `ajv.getSchema(schemaUrl)` resolves them without any network access.
*
* @param {import('ajv').default} ajv
*/
export function registerExtensionSchemas(ajv) {
for (const { schema } of extensionSchemas) {
ajv.addSchema(schema, schema.$id)
}
}

/**
* Classifies a CSAF Extension by its declared `$schema` URL. Returns the
* class recorded in `extensionSchemas` if allow-listed, otherwise
* `'unknown'` (the real OASIS lists aren't available as data here, so
* guessing from the URL would be misleading).
*
* @param {string} schemaUrl
* @returns {'official' | 'registered' | 'experimental' | 'unknown'}
*/
export function classifyExtensionSchema(schemaUrl) {
const entry = extensionSchemas.find((e) => e.schema.$id === schemaUrl)
return entry?.class ?? 'unknown'
}
1 change: 1 addition & 0 deletions csaf_2_1/recommendedTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ export { recommendedTest_6_2_48 } from './recommendedTests/recommendedTest_6_2_4
export { recommendedTest_6_2_49 } from './recommendedTests/recommendedTest_6_2_49.js'
export { recommendedTest_6_2_52 } from './recommendedTests/recommendedTest_6_2_52.js'
export { recommendedTest_6_2_53 } from './recommendedTests/recommendedTest_6_2_53.js'
export { recommendedTest_6_2_54_2 } from './recommendedTests/recommendedTest_6_2_54_2.js'
export { recommendedTest_6_2_54_3 } from './recommendedTests/recommendedTest_6_2_54_3.js'
Loading