Skip to content
163 changes: 143 additions & 20 deletions schema/2.0/modules/cyclonedx-vulnerability-2.0.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -365,25 +365,51 @@
},
"scoreMethod": {
"title": "Method",
"description": "Specifies the severity or risk scoring methodology or standard used.",
"enum": [
"CVSSv2",
"CVSSv3",
"CVSSv31",
"CVSSv4",
"OWASP",
"SSVC",
"other"
],
"meta:enum": {
"CVSSv2": "Common Vulnerability Scoring System v2.0",
"CVSSv3": "Common Vulnerability Scoring System v3.0",
"CVSSv31": "Common Vulnerability Scoring System v3.1",
"CVSSv4": "Common Vulnerability Scoring System v4.0",
"OWASP": "OWASP Risk Rating Methodology",
"SSVC": "Stakeholder Specific Vulnerability Categorization",
"other": "Another severity or risk scoring methodology"
}
"description": "Specifies the severity or risk scoring methodology or standard used. Use the custom option for methodologies not listed.",
"oneOf": [
{
"title": "Predefined Method",
"enum": [
"CVSSv2",
"CVSSv3",
"CVSSv31",
"CVSSv4",
"OWASP",
"SSVC",
"EPSS"
],
"meta:enum": {
"CVSSv2": "Common Vulnerability Scoring System v2.0.",
"CVSSv3": "Common Vulnerability Scoring System v3.0.",
"CVSSv31": "Common Vulnerability Scoring System v3.1.",
"CVSSv4": "Common Vulnerability Scoring System v4.0.",
"OWASP": "OWASP Risk Rating Methodology.",
"SSVC": "Stakeholder Specific Vulnerability Categorization.",
"EPSS": "Exploit Prediction Scoring System."
}
},
{
"title": "Custom Method",

@jkowalleck jkowalleck Oct 15, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would love to see examples for the new "other" in the test suite

"type": "object",
"required": [
"name"
],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"minLength": 1,
"title": "Name",
"description": "The name of the custom scoring methodology."
},
"description": {
"type": "string",
"title": "Description",
"description": "A description of the custom scoring methodology."
}
}
}
]
},
"impactAnalysisState": {
"title": "Impact Analysis State",
Expand Down Expand Up @@ -444,7 +470,16 @@
"score": {
"type": "number",
"title": "Score",
"description": "The numerical score of the rating."
"description": "The numerical score of the rating, on the scale defined by the method. Methods that do not produce a single score on that scale record their values in `measures` instead."
},
"measures": {
"type": "array",
"title": "Measures",
"description": "Named values produced by the scoring method. Methods that emit more than one value, or a value on a scale unlike the primary score, record each value here rather than in `score`. Each measure name shall appear at most once within a rating.",
"uniqueItems": true,
"items": {
Comment thread
stevespringett marked this conversation as resolved.
"$ref": "#/$defs/ratingMeasure"
}
},
"severity": {
"$ref": "#/$defs/severity",
Expand All @@ -453,16 +488,104 @@
"method": {
"$ref": "#/$defs/scoreMethod"
},
"methodVersion": {
"type": "string",
"minLength": 1,
"title": "Method Version",
"description": "The version or revision of the scoring method that produced this rating, for methods that version themselves independently of the value recorded in `method`.",
"examples": [
"v2025.03.14"
]
},
"vector": {
"type": "string",
"title": "Vector",
"description": "Textual representation of the metric values used to score the vulnerability"
},
"timestamp": {
"type": "string",
"format": "date-time",
"title": "Timestamp",
"description": "The date and time the rating was produced or observed. Relevant to methods whose scores are recalculated on a schedule."
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Produced or observed" covers two different times. For EPSS, FIRST publishes a score for a given date, and a scanner may retrieve it days later. Consumers need the first to judge freshness.
The date and time the method produced this rating. For methods published on a schedule, this is the date of the published score, not the time it was retrieved.

"justification": {
"type": "string",
"title": "Justification",
"description": "A reason for rating the vulnerability as it was"
}
},
"allOf": [
{
"$comment": "The Exploit Prediction Scoring System reports probabilities and ranks rather than a score on the severity scale that `score` carries, so its values are recorded as named measures. Constraining the form keeps an EPSS probability from being read or ordered as though it were a severity score.",
"if": {
"required": [ "method" ],
"properties": {
"method": { "const": "EPSS" }
}
},
"then": {
"not": { "required": [ "score" ], "properties": { "score": true } }
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The EPSS conditional forbids score but requires nothing! An EPSS rating can omit both measurements and timing. This stops EPSS from using score, but { "method": "EPSS" } with no measures is still valid, and so is an EPSS rating with no timestamp. Given the concerns in #481, could the then branch also require measures containing a probability, and possibly a timestamp?

"then": {
  "not": { "required": ["score"] },
  "required": ["measures", "timestamp"],
  "properties": {
    "measures": { "contains": { "properties": { "name": { "const": "probability" } }, "required": ["name"] } }
  }
} 

If requiring timestamp is too strict, a "should" in the description would still help.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets refine this after the merge.

}
]
},
"ratingMeasure": {
"type": "object",
"title": "Rating Measure",
"description": "A named value produced by a scoring method. The set of names a method emits is defined by that method.",
"required": [
"name",
"value"
],
"additionalProperties": false,
"properties": {
"name": {
"title": "Name",
"description": "The name of the measure, given as a predefined name or as a custom name defined by the scoring method.",
"oneOf": [
{
"title": "Predefined Measure",
"enum": [
"probability",
"percentile",
"confidence",
"rank"
Comment thread
stevespringett marked this conversation as resolved.
],
"meta:enum": {
"probability": "The likelihood of the scored outcome within the period defined by the method, expressed as a decimal from 0 to 1.",
"percentile": "The proportion of the scored population at or below this subject, expressed as a decimal from 0 to 1.",
"confidence": "The degree of certainty in the score, expressed as a decimal from 0 to 1.",
"rank": "The position of the subject within the scored population."
}
},
{
"title": "Custom Measure",
"type": "object",
"required": [
"name"
],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"minLength": 1,
"title": "Name",
"description": "The name of the custom measure."
},
"description": {
"type": "string",
"title": "Description",
"description": "A description of the custom measure."
}
}
}
]
},
"value": {
"type": "number",
"title": "Value",
"description": "The numeric value of the measure."
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

meta:enum says probability and percentile are decimals from 0 to 1, but value is an unbounded number, so probability: 42 validates. A conditional on ratingMeasure could enforce it:

"allOf": [{
  "if": { "properties": { "name": { "enum": ["probability", "percentile", "confidence"] } }, "required": ["name"] },
  "then": { "properties": { "value": { "minimum": 0, "maximum": 1 } } }
}]

}
},
"vulnerabilitySource": {
Expand Down
Loading