Skip to content

fix: add meta schemas for csaf 2.1 #338

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 18, 2025
Merged
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
20 changes: 20 additions & 0 deletions lib/shared/csafAjv.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,32 @@ import cvss_v2_0 from './csafAjv/cvss-v2.0.js'
import cvss_v3_0 from './csafAjv/cvss-v3.0.js'
import cvss_v3_1 from './csafAjv/cvss-v3.1.js'
import cvss_v4_0 from './csafAjv/cvss-v4.0.js'
import meta from './csafAjv/meta.js'
import formatAssertion from './csafAjv/format-assertion.js'
import ssvcDecisionPointValueSelection from './csafAjv/Decision_Point_Value_Selection-1-0-1.js'
import ssvcDecisionPoint from './csafAjv/Decision_Point-1-0-1.js'

const csafAjv = new Ajv2020({ strict: false, allErrors: true })
addFormats(csafAjv)
csafAjv.addSchema(cvss_v2_0, 'https://www.first.org/cvss/cvss-v2.0.json')
csafAjv.addSchema(cvss_v3_0, 'https://www.first.org/cvss/cvss-v3.0.json')
csafAjv.addSchema(cvss_v3_1, 'https://www.first.org/cvss/cvss-v3.1.json')
csafAjv.addSchema(cvss_v4_0, 'https://www.first.org/cvss/cvss-v4.0.json')
csafAjv.addSchema(
meta,
'https://docs.oasis-open.org/csaf/csaf/v2.1/schema/meta.json'
)
csafAjv.addSchema(
formatAssertion,
'https://json-schema.org/draft/2020-12/meta/format-assertion'
)
csafAjv.addSchema(
ssvcDecisionPointValueSelection,
'https://certcc.github.io/SSVC/data/schema/v1/Decision_Point_Value_Selection-1-0-1.schema.json'
)
csafAjv.addSchema(
ssvcDecisionPoint,
'https://certcc.github.io/SSVC/data/schema/v1/Decision_Point-1-0-1.schema.json'
)

export default csafAjv
106 changes: 106 additions & 0 deletions lib/shared/csafAjv/Decision_Point-1-0-1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
export default {
$schema: 'https://json-schema.org/draft/2020-12/schema',
title: 'Decision Point schema definition',
$id: 'https://certcc.github.io/SSVC/data/schema/v1/Decision_Point-1-0-1.schema.json',
description:
'Decision points are the basic building blocks of SSVC decision functions. Individual decision points describe a single aspect of the input to a decision function.',
$defs: {
schemaVersion: {
description: 'Schema version used to represent this Decision Point.',
type: 'string',
enum: ['1-0-1'],
},
decision_point_value: {
type: 'object',
additionalProperties: false,
properties: {
key: {
type: 'string',
description:
'A short, unique string (or key) used as a shorthand identifier for a Decision Point Value.',
minLength: 1,
examples: ['P', 'Y'],
},
name: {
type: 'string',
description: 'A short label that identifies a Decision Point Value',
minLength: 1,
examples: ['Public PoC', 'Yes'],
},
description: {
type: 'string',
description: 'A full description of the Decision Point Value.',
minLength: 1,
examples: [
'One of the following is true: (1) Typical public PoC exists in sources such as Metasploit or websites like ExploitDB; or (2) the vulnerability has a well-known method of exploitation.',
'Attackers can reliably automate steps 1-4 of the kill chain.',
],
},
},
required: ['key', 'name', 'description'],
},
decision_point: {
type: 'object',
additionalProperties: false,
properties: {
schemaVersion: {
$ref: '#/$defs/schemaVersion',
},
namespace: {
type: 'string',
description:
'Namespace (a short, unique string): The value must be one of the official namespaces, currenlty "ssvc", "cvss" OR can start with \'x_\' for private namespaces. See SSVC Documentation for details.',
pattern: '^(?=.{3,100}$)(x_)?[a-z0-9]{3}([/.-]?[a-z0-9]+){0,97}$',
examples: ['ssvc', 'cvss', 'x_custom', 'x_custom/extension'],
},
version: {
type: 'string',
description:
'Version (a semantic version string) that identifies the version of a Decision Point.',
pattern:
'^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$',
examples: ['1.0.1', '1.0.1-alpha'],
},
key: {
type: 'string',
description:
'A short, unique string (or key) used as a shorthand identifier for a Decision Point.',

minLength: 1,
examples: ['E', 'A'],
},
name: {
type: 'string',
description: 'A short label that identifies a Decision Point.',
minLength: 1,
examples: ['Exploitation', 'Automatable'],
},
description: {
type: 'string',
description:
'A full description of the Decision Point, explaining what it represents and how it is used in SSVC.',
minLength: 1,
},
values: {
description: 'A set of possible answers for a given Decision Point',
uniqueItems: true,
type: 'array',
minItems: 1,
items: {
$ref: '#/$defs/decision_point_value',
},
},
},
required: [
'namespace',
'version',
'key',
'name',
'description',
'values',
'schemaVersion',
],
},
},
$ref: '#/$defs/decision_point',
}
83 changes: 83 additions & 0 deletions lib/shared/csafAjv/Decision_Point_Value_Selection-1-0-1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
export default {
$schema: 'https://json-schema.org/draft/2020-12/schema',
$id: 'https://certcc.github.io/SSVC/data/schema/v1/Decision_Point_Value_Selection-1-0-1.schema.json',
description:
'This schema defines the structure for selecting SSVC Decision Points and their evaluated values for a given vulnerability. Each vulnerability can have multiple Decision Points, and each Decision Point can have multiple selected values when full certainty is not available.',
$defs: {
id: {
type: 'string',
description:
'Identifier for the vulnerability that was evaluation, such as CVE, CERT/CC VU#, OSV id, Bugtraq, GHSA etc.',
examples: ['CVE-1900-1234', 'VU#11111', 'GHSA-11a1-22b2-33c3'],
minLength: 1,
},
role: {
type: 'string',
description:
'The role of the stakeholder performing the evaluation (e.g., Supplier, Deployer, Coordinator). See SSVC documentation for a currently identified list: https://certcc.github.io/SSVC/topics/enumerating_stakeholders/',
examples: ['Supplier', 'Deployer', 'Coordinator'],
minLength: 1,
},
timestamp: {
description:
'Date and time when the evaluation of the Vulnerability was performed according to RFC 3339, section 5.6.',
type: 'string',
format: 'date-time',
},
SsvcdecisionpointselectionSchema: {
description:
'A down-selection of SSVC Decision Points that represent an evaluation at a specific time of a Vulnerability evaluation.',
properties: {
name: {
$ref: 'https://certcc.github.io/SSVC/data/schema/v1/Decision_Point-1-0-1.schema.json#/$defs/decision_point/properties/name',
},
namespace: {
$ref: 'https://certcc.github.io/SSVC/data/schema/v1/Decision_Point-1-0-1.schema.json#/$defs/decision_point/properties/namespace',
},
values: {
description:
'One or more Decision Point Values that were selected for this Decision Point. If the evaluation is uncertain, multiple values may be listed to reflect the potential range of possibilities.',
title: 'values',
type: 'array',
minItems: 1,
items: {
$ref: 'https://certcc.github.io/SSVC/data/schema/v1/Decision_Point-1-0-1.schema.json#/$defs/decision_point_value/properties/name',
},
},
version: {
$ref: 'https://certcc.github.io/SSVC/data/schema/v1/Decision_Point-1-0-1.schema.json#/$defs/decision_point/properties/version',
},
},
type: 'object',
required: ['name', 'namespace', 'values', 'version'],
additionalProperties: false,
},
},
properties: {
id: {
$ref: '#/$defs/id',
},
role: {
$ref: '#/$defs/role',
},
schemaVersion: {
$ref: 'https://certcc.github.io/SSVC/data/schema/v1/Decision_Point-1-0-1.schema.json#/$defs/schemaVersion',
},
timestamp: {
$ref: '#/$defs/timestamp',
},
selections: {
description:
'An array of Decision Points and their selected values for the identified Vulnerability. If a clear evaluation is uncertain, multiple values may be listed for a Decision Point instead of waiting for perfect clarity.',
title: 'selections',
type: 'array',
minItems: 1,
items: {
$ref: '#/$defs/SsvcdecisionpointselectionSchema',
},
},
},
type: 'object',
required: ['selections', 'id', 'timestamp', 'schemaVersion'],
additionalProperties: false,
}
11 changes: 11 additions & 0 deletions lib/shared/csafAjv/format-assertion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default {
$schema: 'https://json-schema.org/draft/2020-12/schema',
$id: 'https://json-schema.org/draft/2020-12/meta/format-assertion',
$dynamicAnchor: 'meta',

title: 'Format vocabulary meta-schema for assertion results',
type: ['object', 'boolean'],
properties: {
format: { type: 'string' },
},
}
13 changes: 13 additions & 0 deletions lib/shared/csafAjv/meta.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default {
$schema: 'https://json-schema.org/draft/2020-12/schema',
$id: 'https://docs.oasis-open.org/csaf/csaf/v2.1/schema/meta.json',
$dynamicAnchor: 'meta',
$vocabulary: {
'https://json-schema.org/draft/2020-12/vocab/core': true,
'https://json-schema.org/draft/2020-12/vocab/format-assertion': true,
},
allOf: [
{ $ref: 'https://json-schema.org/draft/2020-12/meta/core' },
{ $ref: 'https://json-schema.org/draft/2020-12/meta/format-assertion' },
],
}
45 changes: 45 additions & 0 deletions tests/csaf_2_1/schemaTests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import assert from 'node:assert/strict'
import { csaf_2_1, csaf_2_1_strict } from '../../csaf_2_1/schemaTests.js'

const minimalValidDocument = {
$schema: 'https://docs.oasis-open.org/csaf/csaf/v2.1/schema/csaf.json',
document: {
title: 'Basic CSAF document',
csaf_version: '2.1',
category: 'csaf_base',
distribution: {
tlp: { label: 'AMBER' },
},
publisher: {
name: 'Some publisher',
namespace: 'https://example.com',
category: 'coordinator',
},
tracking: {
id: 'some-id',
initial_release_date: '2025-02-18T14:37:32.671Z',
current_release_date: '2025-06-18T14:37:32.671Z',
revision_history: [
{
date: '2025-02-18T14:37:32.671Z',
number: '1',
summary: 'Initial release',
},
],
version: '1',
status: 'draft',
},
},
}

describe('csaf_2_1_strict', function () {
it('validates a basic document', function () {
assert.ok(csaf_2_1_strict(minimalValidDocument).isValid)
})
})

describe('csaf_2_1', function () {
it('validates a basic document', function () {
assert.ok(csaf_2_1(minimalValidDocument).isValid)
})
})