-- :material-television-shimmer:{ .lg .middle } __Get Started with SSVC__
+- :material-television-shimmer:{ .lg .middle } **Get Started with SSVC**
---
@@ -20,7 +19,7 @@ We have organized the SSVC documentation into four main sections:
[:octicons-arrow-right-24: Learning SSVC](tutorials/index.md)
-- :material-clipboard-check:{ .lg .middle } __SSVC How To__
+- :material-clipboard-check:{ .lg .middle } **SSVC How To**
---
@@ -29,7 +28,7 @@ We have organized the SSVC documentation into four main sections:
[:octicons-arrow-right-24: SSVC How To](howto/index.md)
-- :fontawesome-solid-book:{ .lg .middle } __Learn More about SSVC__
+- :fontawesome-solid-book:{ .lg .middle } **Learn More about SSVC**
---
@@ -38,7 +37,7 @@ We have organized the SSVC documentation into four main sections:
[:octicons-arrow-right-24: Understanding SSVC](topics/index.md)
-- :material-book-open-page-variant:{ .lg .middle } __SSVC Reference__
+- :material-book-open-page-variant:{ .lg .middle } **SSVC Reference**
---
@@ -49,5 +48,4 @@ We have organized the SSVC documentation into four main sections:
-
{% include-markdown "_includes/helping_out.md" heading-offset=1 %}
diff --git a/docs/reference/code/analyze_csv.md b/docs/reference/code/analyze_csv.md
index 1f47e1ab..8bee1a2c 100644
--- a/docs/reference/code/analyze_csv.md
+++ b/docs/reference/code/analyze_csv.md
@@ -1,4 +1,3 @@
# SSVC CSV Analyzer
::: ssvc.csv_analyzer
-
diff --git a/docs/reference/code/doctools.md b/docs/reference/code/doctools.md
index edd3b5e0..a589b962 100644
--- a/docs/reference/code/doctools.md
+++ b/docs/reference/code/doctools.md
@@ -1,4 +1,3 @@
# Doctools
::: ssvc.doctools
-
diff --git a/docs/reference/code/index.md b/docs/reference/code/index.md
index 726664c0..8f2f47ad 100644
--- a/docs/reference/code/index.md
+++ b/docs/reference/code/index.md
@@ -6,4 +6,4 @@ These include:
- [CSV Analyzer](analyze_csv.md)
- [Policy Generator](policy_generator.md)
- [Outcomes](outcomes.md)
-- [Doctools](doctools.md)
\ No newline at end of file
+- [Doctools](doctools.md)
diff --git a/docs/reference/code/policy_generator.md b/docs/reference/code/policy_generator.md
index fa6e8477..4520599f 100644
--- a/docs/reference/code/policy_generator.md
+++ b/docs/reference/code/policy_generator.md
@@ -5,5 +5,4 @@ policy (a decision tree) from a set of input parameters.
It is intended to be used as a library, for example within a Jupyter notebook.
-
-::: ssvc.policy_generator
\ No newline at end of file
+::: ssvc.policy_generator
diff --git a/docs/reference/decision_points/automatable.md b/docs/reference/decision_points/automatable.md
index 9b74a09b..69259cfa 100644
--- a/docs/reference/decision_points/automatable.md
+++ b/docs/reference/decision_points/automatable.md
@@ -1,12 +1,18 @@
-# Automatable
+# Automatable (SSVC)
-{% include-markdown "../../_generated/decision_points/automatable.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.automatable import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
!!! tip "See also"
Automatable combines with [Value Density](./value_density.md) to inform
[Utility](./utility.md)
+{% include-markdown "../../_includes/automatable_cvss_ssvc.md" %}
*Automatable* captures the answer to the question “Can an attacker reliably automate creating exploitation events for this vulnerability?”
@@ -22,13 +28,11 @@
2. weaponization may require human direction for each target
3. delivery may require channels that widely deployed network security configurations block
4. exploitation is not reliable, due to exploit-prevention techniques (e.g., ASLR) enabled by default
-
!!! question "When is Automatable *yes*?"
If the vulnerability allows remote code execution or command injection, the expected response should be yes.
-
Due to vulnerability chaining, there is some nuance as to whether reconnaissance can be automated.
!!! example "Vulnerability Chaining"
@@ -57,11 +61,17 @@ Due to vulnerability chaining, there is some nuance as to whether reconnaissance
## Prior Versions
+```python exec="true" idprefix=""
+from ssvc.decision_points.automatable import VERSIONS
+from ssvc.doc_helpers import prior_version, example_block
-{% include-markdown "../../_generated/decision_points/virulence_1_0_0.md" %}
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
!!! warning "*Virulence* is Superseded by *Automatable*"
*Virulence* is superseded by *Automatable*, which clarified the concept we
we were attempting to capture.
-
\ No newline at end of file
diff --git a/docs/reference/decision_points/compound_decision_points.md b/docs/reference/decision_points/compound_decision_points.md
index cca71dfe..43f0e385 100644
--- a/docs/reference/decision_points/compound_decision_points.md
+++ b/docs/reference/decision_points/compound_decision_points.md
@@ -7,4 +7,3 @@ Examples of compound decision points include:
- [Human Impact](human_impact.md)
- [Public Safety Impact](public_safety_impact.md)
- [Utility](utility.md)
-
diff --git a/docs/reference/decision_points/cvss/attack_complexity.md b/docs/reference/decision_points/cvss/attack_complexity.md
new file mode 100644
index 00000000..909f3a5a
--- /dev/null
+++ b/docs/reference/decision_points/cvss/attack_complexity.md
@@ -0,0 +1,21 @@
+# Attack Complexity
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.attack_complexity import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.attack_complexity import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+```
diff --git a/docs/reference/decision_points/cvss/attack_requirements.md b/docs/reference/decision_points/cvss/attack_requirements.md
new file mode 100644
index 00000000..8474666b
--- /dev/null
+++ b/docs/reference/decision_points/cvss/attack_requirements.md
@@ -0,0 +1,8 @@
+# Attack Requirements
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.attack_requirements import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
diff --git a/docs/reference/decision_points/cvss/attack_vector.md b/docs/reference/decision_points/cvss/attack_vector.md
new file mode 100644
index 00000000..515e39d9
--- /dev/null
+++ b/docs/reference/decision_points/cvss/attack_vector.md
@@ -0,0 +1,22 @@
+# Attack Vector
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.attack_vector import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.attack_vector import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/authentication.md b/docs/reference/decision_points/cvss/authentication.md
new file mode 100644
index 00000000..f7c92e09
--- /dev/null
+++ b/docs/reference/decision_points/cvss/authentication.md
@@ -0,0 +1,22 @@
+# Authentication
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.authentication import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.authentication import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/automatable.md b/docs/reference/decision_points/cvss/automatable.md
new file mode 100644
index 00000000..cc3ccc25
--- /dev/null
+++ b/docs/reference/decision_points/cvss/automatable.md
@@ -0,0 +1,10 @@
+# Automatable (CVSS)
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.supplemental.automatable import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+{% include-markdown "../../../_includes/automatable_cvss_ssvc.md" %}
diff --git a/docs/reference/decision_points/cvss/availability_impact.md b/docs/reference/decision_points/cvss/availability_impact.md
new file mode 100644
index 00000000..5f71b52b
--- /dev/null
+++ b/docs/reference/decision_points/cvss/availability_impact.md
@@ -0,0 +1,22 @@
+# Availability Impact to the Vulnerable System
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.availability_impact import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.availability_impact import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/availability_requirement.md b/docs/reference/decision_points/cvss/availability_requirement.md
new file mode 100644
index 00000000..caf0730e
--- /dev/null
+++ b/docs/reference/decision_points/cvss/availability_requirement.md
@@ -0,0 +1,22 @@
+# Availability Requirement
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.availability_requirement import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.availability_requirement import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/collateral_damage_potential.md b/docs/reference/decision_points/cvss/collateral_damage_potential.md
new file mode 100644
index 00000000..b34cd7c5
--- /dev/null
+++ b/docs/reference/decision_points/cvss/collateral_damage_potential.md
@@ -0,0 +1,22 @@
+# Collateral Damage Potential
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.collateral_damage_potential import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.collateral_damage_potential import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/confidentiality_impact.md b/docs/reference/decision_points/cvss/confidentiality_impact.md
new file mode 100644
index 00000000..a2d85474
--- /dev/null
+++ b/docs/reference/decision_points/cvss/confidentiality_impact.md
@@ -0,0 +1,22 @@
+# Confidentiality Impact to the Vulnerable System
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.confidentiality_impact import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.confidentiality_impact import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/confidentiality_requirement.md b/docs/reference/decision_points/cvss/confidentiality_requirement.md
new file mode 100644
index 00000000..e65e354a
--- /dev/null
+++ b/docs/reference/decision_points/cvss/confidentiality_requirement.md
@@ -0,0 +1,22 @@
+# Confidentiality Requirement
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.confidentiality_requirement import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.confidentiality_requirement import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/exploit_maturity.md b/docs/reference/decision_points/cvss/exploit_maturity.md
new file mode 100644
index 00000000..5d8ddab2
--- /dev/null
+++ b/docs/reference/decision_points/cvss/exploit_maturity.md
@@ -0,0 +1,22 @@
+# Exploit Maturity
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.exploit_maturity import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.exploit_maturity import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/impact_bias.md b/docs/reference/decision_points/cvss/impact_bias.md
new file mode 100644
index 00000000..65ac3599
--- /dev/null
+++ b/docs/reference/decision_points/cvss/impact_bias.md
@@ -0,0 +1,8 @@
+# Impact Bias
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.impact_bias import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
diff --git a/docs/reference/decision_points/cvss/index.md b/docs/reference/decision_points/cvss/index.md
new file mode 100644
index 00000000..f9309c0f
--- /dev/null
+++ b/docs/reference/decision_points/cvss/index.md
@@ -0,0 +1,83 @@
+# CVSS Decision Points
+
+!!! tip inline end "For more information"
+
+ For more information on the CVSS specification, please refer to the
+ [CVSS Specifications](https://www.first.org/cvss/).
+
+For convenience, we have provide a list of decision points that are based
+on the CVSS specification. These decision points can be used to model various
+decisions based on CVSS vector elements.
+
+## Decision Points
+
+The following list of CVSS vector elements have been modeled as SSVC decision
+points for use in vulnerability response and security decision modeling.
+We have organized them into groups according to where they belong in the
+[CVSS v4.0 specification document](https://www.first.org/cvss/v4.0/specification-document).
+
+!!! info "About CVSS Decision Point Versions"
+
+ We have modeled our CVSS-based decision points using the SSVC versioning scheme.
+ Therefore, some decision points may have multiple versions as the concepts have
+ been refined over different versions of the CVSS specification. These versions
+ do _not_ correspond the CVSS specification versions (2.0, 3.0, 3.1, 4.0 etc.).
+
+### Qualitative Severity
+
+
+- [Attack Vector](attack_vector.md)
+- [Attack Complexity](attack_complexity.md)
+- [Attack Requirements](attack_requirements.md)
+- [Privileges Required](privileges_required.md)
+- [User Interaction](user_interaction.md)
+- [Confidentiality Impact](confidentiality_impact.md)
+- [Subsequent Confidentiality Impact](subsequent_confidentiality_impact.md)
+- [Integrity Impact](integrity_impact.md)
+- [Subsequent Integrity Impact](subsequent_integrity_impact.md)
+- [Availability Impact](availability_impact.md)
+- [Subsequent Availability Impact](subsequent_availability_impact.md)
+
+
+### Threat Metrics
+
+
+- [Confidentiality Requirement](confidentiality_requirement.md)
+- [Integrity Requirement](integrity_requirement.md)
+- [Availability Requirement](availability_requirement.md)
+
+
+### Supplemental Metrics
+
+
+- [Safety](safety.md)
+- [Automatable](automatable.md)
+- [Provider Urgency](provider_urgency.md)
+- [Recovery](recovery.md)
+- [Value Density](value_density.md)
+- [Vulnerability Response Effort](vulnerability_response_effort.md)
+
+
+### Older Metrics
+
+
+- [Authentication](authentication.md)
+- [Collateral Damage Potential](collateral_damage_potential.md)
+- [Impact Bias](impact_bias.md)
+- [Remediation Level](remediation_level.md)
+- [Report Confidence](report_confidence.md)
+- [Scope](scope.md)
+- [Target Distribution](target_distribution.md)
+
diff --git a/docs/reference/decision_points/cvss/integrity_impact.md b/docs/reference/decision_points/cvss/integrity_impact.md
new file mode 100644
index 00000000..f31dbdc3
--- /dev/null
+++ b/docs/reference/decision_points/cvss/integrity_impact.md
@@ -0,0 +1,22 @@
+# Integrity Impact to the Vulnerable System
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.integrity_impact import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.integrity_impact import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/integrity_requirement.md b/docs/reference/decision_points/cvss/integrity_requirement.md
new file mode 100644
index 00000000..99e031f1
--- /dev/null
+++ b/docs/reference/decision_points/cvss/integrity_requirement.md
@@ -0,0 +1,22 @@
+# Integrity Requirement
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.integrity_requirement import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.integrity_requirement import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/privileges_required.md b/docs/reference/decision_points/cvss/privileges_required.md
new file mode 100644
index 00000000..a3704e84
--- /dev/null
+++ b/docs/reference/decision_points/cvss/privileges_required.md
@@ -0,0 +1,22 @@
+# Privileges Required
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.privileges_required import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.privileges_required import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/provider_urgency.md b/docs/reference/decision_points/cvss/provider_urgency.md
new file mode 100644
index 00000000..f0fd62fd
--- /dev/null
+++ b/docs/reference/decision_points/cvss/provider_urgency.md
@@ -0,0 +1,8 @@
+# Provider Urgency
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.supplemental.provider_urgency import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
diff --git a/docs/reference/decision_points/cvss/qualitative_severity.md b/docs/reference/decision_points/cvss/qualitative_severity.md
new file mode 100644
index 00000000..81be133d
--- /dev/null
+++ b/docs/reference/decision_points/cvss/qualitative_severity.md
@@ -0,0 +1,12 @@
+# CVSS Qualitative Severity Rating Scale
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.qualitative_severity import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+The [CVSS Qualitative Severity Rating Scale](https://www.first.org/cvss/v4.0/specification-document#Qualitative-Severity-Rating-Scale)
+is a set of labels that describe the severity of a vulnerability based on the
+CVSS Score.
diff --git a/docs/reference/decision_points/cvss/recovery.md b/docs/reference/decision_points/cvss/recovery.md
new file mode 100644
index 00000000..6798a108
--- /dev/null
+++ b/docs/reference/decision_points/cvss/recovery.md
@@ -0,0 +1,8 @@
+# Recovery
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.supplemental.recovery import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
diff --git a/docs/reference/decision_points/cvss/remediation_level.md b/docs/reference/decision_points/cvss/remediation_level.md
new file mode 100644
index 00000000..0f06a9f1
--- /dev/null
+++ b/docs/reference/decision_points/cvss/remediation_level.md
@@ -0,0 +1,22 @@
+# Remediation Level
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.remediation_level import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.remediation_level import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/report_confidence.md b/docs/reference/decision_points/cvss/report_confidence.md
new file mode 100644
index 00000000..a95ac406
--- /dev/null
+++ b/docs/reference/decision_points/cvss/report_confidence.md
@@ -0,0 +1,22 @@
+# Report Confidence
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.report_confidence import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.report_confidence import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/safety.md b/docs/reference/decision_points/cvss/safety.md
new file mode 100644
index 00000000..e205d7b3
--- /dev/null
+++ b/docs/reference/decision_points/cvss/safety.md
@@ -0,0 +1,23 @@
+# Safety
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.supplemental.safety import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+{% include-markdown "../../../_includes/safety_cvss_ssvc.md" %}
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.supplemental.safety import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+```
diff --git a/docs/reference/decision_points/cvss/scope.md b/docs/reference/decision_points/cvss/scope.md
new file mode 100644
index 00000000..e7168659
--- /dev/null
+++ b/docs/reference/decision_points/cvss/scope.md
@@ -0,0 +1,8 @@
+# Scope
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.scope import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
diff --git a/docs/reference/decision_points/cvss/subsequent_availability_impact.md b/docs/reference/decision_points/cvss/subsequent_availability_impact.md
new file mode 100644
index 00000000..3eb241b2
--- /dev/null
+++ b/docs/reference/decision_points/cvss/subsequent_availability_impact.md
@@ -0,0 +1,8 @@
+# Availability Impact to the Subsequent System
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.subsequent_availability_impact import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
diff --git a/docs/reference/decision_points/cvss/subsequent_confidentiality_impact.md b/docs/reference/decision_points/cvss/subsequent_confidentiality_impact.md
new file mode 100644
index 00000000..e0b6e8a8
--- /dev/null
+++ b/docs/reference/decision_points/cvss/subsequent_confidentiality_impact.md
@@ -0,0 +1,8 @@
+# Confidentiality Impact to the Subsequent System
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.subsequent_confidentiality_impact import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
diff --git a/docs/reference/decision_points/cvss/subsequent_integrity_impact.md b/docs/reference/decision_points/cvss/subsequent_integrity_impact.md
new file mode 100644
index 00000000..43083663
--- /dev/null
+++ b/docs/reference/decision_points/cvss/subsequent_integrity_impact.md
@@ -0,0 +1,8 @@
+# Integrity Impact to the Subsequent System
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.subsequent_integrity_impact import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
diff --git a/docs/reference/decision_points/cvss/target_distribution.md b/docs/reference/decision_points/cvss/target_distribution.md
new file mode 100644
index 00000000..4c27649e
--- /dev/null
+++ b/docs/reference/decision_points/cvss/target_distribution.md
@@ -0,0 +1,22 @@
+# Target Distribution
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.target_distribution import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.target_distribution import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/user_interaction.md b/docs/reference/decision_points/cvss/user_interaction.md
new file mode 100644
index 00000000..0f03c589
--- /dev/null
+++ b/docs/reference/decision_points/cvss/user_interaction.md
@@ -0,0 +1,22 @@
+# User Interaction
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.user_interaction import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+## Previous Versions
+
+Following are the previous versions of the decision point:
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.user_interaction import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/cvss/value_density.md b/docs/reference/decision_points/cvss/value_density.md
new file mode 100644
index 00000000..885b3bf0
--- /dev/null
+++ b/docs/reference/decision_points/cvss/value_density.md
@@ -0,0 +1,10 @@
+# Value Density (CVSS)
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.supplemental.value_density import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+{% include-markdown "../../../_includes/value_density_cvss_ssvc.md" %}
diff --git a/docs/reference/decision_points/cvss/vulnerability_response_effort.md b/docs/reference/decision_points/cvss/vulnerability_response_effort.md
new file mode 100644
index 00000000..b9b49f93
--- /dev/null
+++ b/docs/reference/decision_points/cvss/vulnerability_response_effort.md
@@ -0,0 +1,8 @@
+# Vulnerability Response Effort
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.supplemental.vulnerability_response_effort import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
diff --git a/docs/reference/decision_points/exploitation.md b/docs/reference/decision_points/exploitation.md
index 58b398c2..478d4033 100644
--- a/docs/reference/decision_points/exploitation.md
+++ b/docs/reference/decision_points/exploitation.md
@@ -1,6 +1,11 @@
-# Exploitation
+# Exploitation
-{% include-markdown "../../_generated/decision_points/exploitation.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.exploitation import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
The intent of this measure is the present state of exploitation of the vulnerability. The intent is not to predict future exploitation but only to acknowledge the current state of affairs. Predictive systems, such as EPSS, could be used to augment this decision or to notify stakeholders of likely changes [@jacobs2021epss].
@@ -30,10 +35,8 @@ The intent of this measure is the present state of exploitation of the vulnerabi
## CWE-IDs for *PoC*
-
The table below lists CWE-IDs that could be used to mark a vulnerability as *PoC* if the vulnerability is described by the CWE-ID.
-
!!! example "CWE-295"
For example, [CWE-295 Improper Certificate Validation
@@ -46,4 +49,14 @@ The table below lists CWE-IDs that could be used to mark a vulnerability as *PoC
{{ read_csv('cwe/possible-cwe-with-poc-examples.csv') }}
----
\ No newline at end of file
+## Prior Versions
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.exploitation import VERSIONS
+from ssvc.doc_helpers import prior_version, example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/human_impact.md b/docs/reference/decision_points/human_impact.md
index a8a22036..04057d11 100644
--- a/docs/reference/decision_points/human_impact.md
+++ b/docs/reference/decision_points/human_impact.md
@@ -1,6 +1,11 @@
# Human Impact
-{% include-markdown "../../_generated/decision_points/human_impact.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.human_impact import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
!!! tip "See also"
@@ -10,7 +15,7 @@
Note: This is a compound decision point[^1], therefore it is a notational convenience.
*Human Impact* is a combination of how a vulnerability can affect an organization's mission essential functions as well as
-safety considerations, whether for the organization's personnel or the public at large.
+safety considerations, whether for the organization's personnel or the public at large.
We observe that the day-to-day operations of an organization often have already built in a degree of tolerance to small-scale variance in mission impacts.
Thus in our opinion we need only concern ourselves with discriminating well at the upper end of the scale.
Therefore we combine the two lesser mission impacts of degraded and MEF support crippled into a single category, while retaining the distinction between MEF Failure and Mission Failure at the extreme.
@@ -25,10 +30,9 @@ The mapping is shown in the table above.
[^1]: In pilot implementations of SSVC, we received feedback that organizations tend to think of mission and safety impacts as
if they were combined into a single factor: in other words, the priority increases regardless which of the two impact factors was increased.
We therefore combine [Safety Impact](safety_impact.md) and
-[Mission Impact](mission_impact.md) for deployers into a single _Human Impact_ factor
+[Mission Impact](mission_impact.md) for deployers into a single *Human Impact* factor
as a dimension reduction step.
-
## Safety and Mission Impact Decision Points for Industry Sectors
We expect to encounter diversity in both safety and mission impacts across different organizations.
@@ -40,9 +44,14 @@ provide SSVC information tailored as appropriate to their constituency's safety
For considerations on how organizations might communicate SSVC information to their constituents,
see [Guidance on Communicating Results](../../howto/bootstrap/use.md).
-
## Prior Versions
-{% include-markdown "../../_generated/decision_points/human_impact_2_0_0.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.human_impact import VERSIONS
+from ssvc.doc_helpers import prior_version, example_block
-{% include-markdown "../../_generated/decision_points/mission_and_well-being_impact_1_0_0.md" %}
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/index.md b/docs/reference/decision_points/index.md
index ec64f9a7..1f002796 100644
--- a/docs/reference/decision_points/index.md
+++ b/docs/reference/decision_points/index.md
@@ -41,7 +41,7 @@ decision points.
Sometimes this is a "better" or "worse" dimension, but it seems to generalize to
a "more likely to act" or "less likely to act" of dimension.
-!!! question "Where are the _Unknown_ options?"
+!!! question "Where are the *Unknown* options?"
One important omission from the values for each category is an *unknown* option.
Instead, we recommend explicitly identifying an option that is a reasonable assumption based on prior events.
diff --git a/docs/reference/decision_points/mission_impact.md b/docs/reference/decision_points/mission_impact.md
index ca2a05c7..f8b80503 100644
--- a/docs/reference/decision_points/mission_impact.md
+++ b/docs/reference/decision_points/mission_impact.md
@@ -1,43 +1,52 @@
# Mission Impact
-{% include-markdown "../../_generated/decision_points/mission_impact.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.mission_impact import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
!!! tip "See also"
Mission Impact combines with [Safety Impact](./safety_impact.md) to inform
[Human Impact](./human_impact.md)
-A **mission essential function (MEF)** is a function “directly related to accomplishing the organization’s mission as set forth in its statutory or executive charter” [@FCD2_2017, page A-1].
-Identification and prioritization of mission essential functions enables effective continuity planning or crisis planning.
+A **mission essential function (MEF)** is a function “directly related to accomplishing the organization’s mission as set forth in its statutory or executive charter” [@FCD2_2017, page A-1].
+Identification and prioritization of mission essential functions enables effective continuity planning or crisis planning.
Mission Essential Functions are in effect critical activities within an organization that are used to identify key assets, supporting tasks, and resources that an organization requires to remain operational in a crises situation, and so must be included in its planning process.
During an event, key resources may be limited and personnel may be unavailable, so organizations must consider these factors and validate assumptions when identifying, validating, and prioritizing MEFs.
-When reviewing the list of organizational functions, an organization must first identify whether a function is essential or non-essential.
-The distinction between these two categories is whether or not an organization must perform a function during a disruption to normal operations and must continue performance during emergencies [@FCD2_2017, page B-2].
+When reviewing the list of organizational functions, an organization must first identify whether a function is essential or non-essential.
+The distinction between these two categories is whether or not an organization must perform a function during a disruption to normal operations and must continue performance during emergencies [@FCD2_2017, page B-2].
Essential functions are both important and urgent.
Functions that can be deferred until after an emergency are identified as non-essential.
For example, DoD defines MEFs in [DoD Directive 3020.26 DoD Continuity Policy](https://www.esd.whs.mil/Portals/54/Documents/DD/issuances/dodd/302026p.pdf) using similar terminology to [FCD-2](https://www.fema.gov/sites/default/files/2020-07/Federal_Continuity_Directive-2_June132017.pdf) [@dod3026_26_2018].
-As mission essential functions are most clearly defined for government agencies, stakeholders in other sectors may be familiar with different terms of art from continuity planning.
-For example, infrastructure providers in the US may better align with [National Critical Functions](https://www.cisa.gov/national-critical-functions).
-Private sector businesses may better align with [operational and financial impacts](https://www.ready.gov/sites/default/files/2020-03/business-impact-analysis-worksheet.pdf) in a [business continuity plan](https://www.ready.gov/business-continuity-plan).
+As mission essential functions are most clearly defined for government agencies, stakeholders in other sectors may be familiar with different terms of art from continuity planning.
+For example, infrastructure providers in the US may better align with [National Critical Functions](https://www.cisa.gov/national-critical-functions).
+Private sector businesses may better align with [operational and financial impacts](https://www.ready.gov/sites/default/files/2020-03/business-impact-analysis-worksheet.pdf) in a [business continuity plan](https://www.ready.gov/business-continuity-plan).
While the processes, terminology, and audience for these different frameworks differ, they all can provide a sense of the criticality of an asset or assets within the scope of the stakeholder conducting the cyber vulnerability prioritization with SSVC.
-In that sense they all function quite similarly within SSVC. Organizations should use whatever is most appropriate for their stakeholder context, with Mission Essential Function analysis serving as a fully worked example in the SSVC documents.
-
+In that sense they all function quite similarly within SSVC. Organizations should use whatever is most appropriate for their stakeholder context, with Mission Essential Function analysis serving as a fully worked example in the SSVC documents.
## Gathering Information About Mission Impact
-The factors that influence the mission impact level are diverse.
-This paper does not exhaustively discuss how a stakeholder should answer a question; that is a topic for future work.
-At a minimum, understanding mission impact should include gathering information about the critical paths that involve vulnerable components, viability of contingency measures, and resiliency of the systems that support the mission.
-There are various sources of guidance on how to gather this information; see for example the FEMA guidance in Continuity Directive 2 [@FCD2_2017] or OCTAVE FORTE [@tucker2018octave].
+The factors that influence the mission impact level are diverse.
+The material here does not exhaustively discuss how a stakeholder should answer a question; that is a topic for future work.
+At a minimum, understanding mission impact should include gathering information about the critical paths that involve vulnerable components, viability of contingency measures, and resiliency of the systems that support the mission.
+There are various sources of guidance on how to gather this information; see for example the FEMA guidance in Continuity Directive 2 [@FCD2_2017] or OCTAVE FORTE [@tucker2018octave].
This is part of risk management more broadly.
It should require the vulnerability management team to interact with more senior management to understand mission priorities and other aspects of risk mitigation.
-As a heuristic, [Utility](utility.md) might constrain [*Mission Impact*](mission_impact.md) if both are not used in the same decision tree.
-For example, if the [Utility](utility.md) is [*super effective*](utility.md), then [*Mission Impact*](mission_impact.md) is at least [*MEF support crippled*](mission_impact.md).
-
## Prior Versions
-{% include-markdown "../../_generated/decision_points/mission_impact_1_0_0.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.mission_impact import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/public_safety_impact.md b/docs/reference/decision_points/public_safety_impact.md
index 1f26a47d..44b774b0 100644
--- a/docs/reference/decision_points/public_safety_impact.md
+++ b/docs/reference/decision_points/public_safety_impact.md
@@ -1,22 +1,31 @@
# Public Safety Impact
-{% include-markdown "../../_generated/decision_points/public_safety_impact.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.public_safety_impact import LATEST
+from ssvc.doc_helpers import example_block
-!!! tip "See also"
+print(example_block(LATEST))
+```
- - [Safety Impact](./safety_impact.md)
+{% include-markdown "../../_includes/safety_cvss_ssvc.md" %}
This is a compound decision point, therefore it is a notational convenience.
Suppliers necessarily have a rather coarse-grained perspective on the broadly defined [Safety Impact](safety_impact.md) Decision Point.
Therefore we simplify the above into a binary categorization:
-- _Significant_ is when any impact meets the criteria for an impact of Marginal, Critical, or Catastrophic in the
+- *Significant* is when any impact meets the criteria for an impact of Marginal, Critical, or Catastrophic in the
[Safety Impact](safety_impact.md) table.
-- _Minimal_ is when none do.
+- *Minimal* is when none do.
## Prior Versions
-{% include-markdown "../../_generated/decision_points/public_safety_impact_2_0_0.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.public_safety_impact import VERSIONS
+from ssvc.doc_helpers import example_block
-{% include-markdown "../../_generated/decision_points/public_well-being_impact_1_0_0.md" %}
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/public_value_added.md b/docs/reference/decision_points/public_value_added.md
index 03507837..0284c0da 100644
--- a/docs/reference/decision_points/public_value_added.md
+++ b/docs/reference/decision_points/public_value_added.md
@@ -1,12 +1,17 @@
# Public Value Added
-{% include-markdown "../../_generated/decision_points/public_value_added.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.public_value_added import LATEST
+from ssvc.doc_helpers import example_block
-The intent of the definition is that one rarely if ever transitions from _limited_ to _ampliative_ or _ampliative_ to _precedence_.
-A vulnerability could transition from _precedence_ to _ampliative_ and _ampliative_ to _limited_.
+print(example_block(LATEST))
+```
+
+The intent of the definition is that one rarely if ever transitions from *limited* to *ampliative* or *ampliative* to *precedence*.
+A vulnerability could transition from *precedence* to *ampliative* and *ampliative* to *limited*.
That is, *Public Value Added* should only be downgraded through future iterations or re-evaluations.
This directionality is because once other organizations make something public, they cannot effectively un-publish it
(it'll be recorded and people will know about it, even if they take down a webpage).
-The rare case where *Public Value Added* increases would be if an organization published viable information, but
+The rare case where *Public Value Added* increases would be if an organization published viable information, but
then published additional misleading or obscuring information at a later time.
Then one might go from *limited* to *ampliative* in the interest of pointing to the better information.
diff --git a/docs/reference/decision_points/report_credibility.md b/docs/reference/decision_points/report_credibility.md
index f508f7cd..acce744c 100644
--- a/docs/reference/decision_points/report_credibility.md
+++ b/docs/reference/decision_points/report_credibility.md
@@ -1,6 +1,11 @@
# Report Credibility
-{% include-markdown "../../_generated/decision_points/report_credibility.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.report_credibility import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
An analyst should start with a presumption of credibility and proceed toward disqualification.
The reason for this is that, as a coordinator, occasionally doing a bit of extra work on a bad report is preferable to rejecting legitimate reports.
@@ -24,35 +29,35 @@ The indicators for or against are not commensurate, and so they cannot be put on
If neither of these confirmations are available, then the value of the [*Report Credibility*](#report-credibility) decision point depends on a balancing test among the following indicators.
**Indicators *for* Credibility** include:
-
- - The report is specific about what is affected
- - The report provides sufficient detail to reproduce the vulnerability.
- - The report describes an attack scenario.
- - The report suggests mitigations.
- - The report includes proof-of-concept exploit code or steps to reproduce.
- - Screenshots and videos, if provided, support the written text of the report and do not replace it.
- - The report neither exaggerates nor understates the impact.
+
+- The report is specific about what is affected
+- The report provides sufficient detail to reproduce the vulnerability.
+- The report describes an attack scenario.
+- The report suggests mitigations.
+- The report includes proof-of-concept exploit code or steps to reproduce.
+- Screenshots and videos, if provided, support the written text of the report and do not replace it.
+- The report neither exaggerates nor understates the impact.
**Indicators *against* Credibility** include:
- - The report is “spammy” or exploitative (for example, the report is an attempt to upsell the receiver on some product or service).
- - The report is vague or ambiguous about which vendors, products, or versions are affected (for example, the report claims that all “cell phones” or “wifi” or “routers” are affected).
- - The report is vague or ambiguous about the preconditions necessary to exploit the vulnerability.
- - The report is vague or ambiguous about the impact if exploited.
- - The report exaggerates the impact if exploited.
- - The report makes extraordinary claims without correspondingly extraordinary evidence (for example, the report claims that exploitation could result in catastrophic damage to some critical system without a clear causal connection between the facts presented and the impacts claimed).
- - The report is unclear about what the attacker gains by exploiting the vulnerability. What do they get that they didn't already have? For example, an attacker with system privileges can already do lots of bad things, so a report that assumes system privileges as a precondition to exploitation needs to explain what else this gives the attacker.
- - The report depends on preconditions that are extremely rare in practice, and lacks adequate evidence for why those preconditions might be expected to occur (for example, the vulnerability is only exposed in certain non-default configurations—unless there is evidence that a community of practice has established a norm of such a non-default setup).
- - The report claims dire impact for a trivially found vulnerability. It is not impossible for this to occur, but most products and services that have been around for a while have already had their low-hanging fruit major vulnerabilities picked. One notable exception would be if the reporter applied a completely new method for finding vulnerabilities to discover the subject of the report.
- - The report is rambling and is more about a narrative than describing the vulnerability. One description is that the report reads like a food recipe with the obligatory search engine optimization preamble.
- - The reporter is known to have submitted low-quality reports in the past.
- - The report conspicuously misuses technical terminology. This is evidence that the reporter may not understand what they are talking about.
- - The analyst's professional colleagues consider the report to be not credible.
- - The report consists of mostly raw tool output. Fuzz testing outputs are not vulnerability reports.
- - The report lacks sufficient detail for someone to reproduce the vulnerability.
- - The report is just a link to a video or set of images, or lacks written detail while claiming “it's all in the video”. Imagery should support a written description, not replace it.
- - The report describes a bug with no discernible security impact.
- - The report fails to describe an attack scenario, and none is obvious.
+- The report is “spammy” or exploitative (for example, the report is an attempt to upsell the receiver on some product or service).
+- The report is vague or ambiguous about which vendors, products, or versions are affected (for example, the report claims that all “cell phones” or “wifi” or “routers” are affected).
+- The report is vague or ambiguous about the preconditions necessary to exploit the vulnerability.
+- The report is vague or ambiguous about the impact if exploited.
+- The report exaggerates the impact if exploited.
+- The report makes extraordinary claims without correspondingly extraordinary evidence (for example, the report claims that exploitation could result in catastrophic damage to some critical system without a clear causal connection between the facts presented and the impacts claimed).
+- The report is unclear about what the attacker gains by exploiting the vulnerability. What do they get that they didn't already have? For example, an attacker with system privileges can already do lots of bad things, so a report that assumes system privileges as a precondition to exploitation needs to explain what else this gives the attacker.
+- The report depends on preconditions that are extremely rare in practice, and lacks adequate evidence for why those preconditions might be expected to occur (for example, the vulnerability is only exposed in certain non-default configurations—unless there is evidence that a community of practice has established a norm of such a non-default setup).
+- The report claims dire impact for a trivially found vulnerability. It is not impossible for this to occur, but most products and services that have been around for a while have already had their low-hanging fruit major vulnerabilities picked. One notable exception would be if the reporter applied a completely new method for finding vulnerabilities to discover the subject of the report.
+- The report is rambling and is more about a narrative than describing the vulnerability. One description is that the report reads like a food recipe with the obligatory search engine optimization preamble.
+- The reporter is known to have submitted low-quality reports in the past.
+- The report conspicuously misuses technical terminology. This is evidence that the reporter may not understand what they are talking about.
+- The analyst's professional colleagues consider the report to be not credible.
+- The report consists of mostly raw tool output. Fuzz testing outputs are not vulnerability reports.
+- The report lacks sufficient detail for someone to reproduce the vulnerability.
+- The report is just a link to a video or set of images, or lacks written detail while claiming “it's all in the video”. Imagery should support a written description, not replace it.
+- The report describes a bug with no discernible security impact.
+- The report fails to describe an attack scenario, and none is obvious.
We considered adding poor grammar or spelling as an indicator of non-credibility.
On further reflection, we do not recommend that poor grammar or spelling be used as an indicator of low report quality, as many reporters may not be native to the coordinator's language.
@@ -72,7 +77,5 @@ Furthermore, a report may be factual but not identify any security implications;
A coordinator also has a scope defined by their specific constituency and mission.
A report can be entirely credible yet remain out of scope for your coordination practice.
-Decide what to do about out of scope reports separately, before the vulnerability coordination triage decision begins.
+Decide what to do about out of scope reports separately, before the vulnerability coordination triage decision begins.
If a report arrives and would be out of scope even if true, there will be no need to proceed with judging its credibility.
-
-
diff --git a/docs/reference/decision_points/report_public.md b/docs/reference/decision_points/report_public.md
index 5f02a81e..aa795f2e 100644
--- a/docs/reference/decision_points/report_public.md
+++ b/docs/reference/decision_points/report_public.md
@@ -1,3 +1,8 @@
# Report Public
-{% include-markdown "../../_generated/decision_points/report_public.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.report_public import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
diff --git a/docs/reference/decision_points/safety_impact.md b/docs/reference/decision_points/safety_impact.md
index 41cd2eb4..2c9418c4 100644
--- a/docs/reference/decision_points/safety_impact.md
+++ b/docs/reference/decision_points/safety_impact.md
@@ -1,13 +1,18 @@
# Safety Impact
-{% include-markdown "../../_generated/decision_points/safety_impact.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.safety_impact import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+
+{% include-markdown "../../_includes/safety_cvss_ssvc.md" %}
!!! tip "See also"
- Safety Impact combines with [Mission Impact](./mission_impact.md) to
inform [Human Impact](./human_impact.md).
- - [Public Safety Impact](./public_safety_impact.md) provides a simplified
- version of this decision point.
We take an expansive view of safety, in which a safety violation is a violation of what the United States [Centers for Disease Control (CDC)](https://www.cdc.gov/hrqol/wellbeing.htm) calls **well-being**. Physical well-being violations are common safety violations, but we also consider economic, social, emotional, and psychological well-being to be important. Weighing fine differences among these categories is probably not possible, so we will not try. Each decision option lists examples of the effects that qualify for that value/answer in the various types of violations of well-being. These examples should not be considered comprehensive or exhaustive, but rather as suggestive.
-
### Situated Safety Impact
Deployers are anticipated to have a more fine-grained perspective on the safety impacts broadly defined in *Safety Impact*.
We defer this topic for now because we combine it with [*Mission Impact*](mission_impact.md) to simplify implementation for deployers.
-
## Prior Versions
-{% include-markdown "../../_generated/decision_points/safety_impact_1_0_0.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.safety_impact import VERSIONS
+from ssvc.doc_helpers import example_block
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/supplier_cardinality.md b/docs/reference/decision_points/supplier_cardinality.md
index ef55c8bf..ccd088fa 100644
--- a/docs/reference/decision_points/supplier_cardinality.md
+++ b/docs/reference/decision_points/supplier_cardinality.md
@@ -1,3 +1,8 @@
# Supplier Cardinality
-{% include-markdown "../../_generated/decision_points/supplier_cardinality.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.supplier_cardinality import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
diff --git a/docs/reference/decision_points/supplier_contacted.md b/docs/reference/decision_points/supplier_contacted.md
index 7a3d9d38..f75e1615 100644
--- a/docs/reference/decision_points/supplier_contacted.md
+++ b/docs/reference/decision_points/supplier_contacted.md
@@ -1,10 +1,12 @@
# Supplier Contacted
-{% include-markdown "../../_generated/decision_points/supplier_contacted.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.supplier_contacted import LATEST
+from ssvc.doc_helpers import example_block
+print(example_block(LATEST))
+```
!!! tip "Quality Contact Method"
A quality contact method is a publicly posted known good email address, public portal on vendor website, etc.
-
-
diff --git a/docs/reference/decision_points/supplier_engagement.md b/docs/reference/decision_points/supplier_engagement.md
index 42e306af..c8a7426b 100644
--- a/docs/reference/decision_points/supplier_engagement.md
+++ b/docs/reference/decision_points/supplier_engagement.md
@@ -1,3 +1,8 @@
# Supplier Engagement
-{% include-markdown "../../_generated/decision_points/supplier_engagement.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.supplier_engagement import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
diff --git a/docs/reference/decision_points/supplier_involvement.md b/docs/reference/decision_points/supplier_involvement.md
index d28e978e..d4fb9d70 100644
--- a/docs/reference/decision_points/supplier_involvement.md
+++ b/docs/reference/decision_points/supplier_involvement.md
@@ -1,3 +1,8 @@
# Supplier Involvement
-{% include-markdown "../../_generated/decision_points/supplier_involvement.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.supplier_involvement import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
diff --git a/docs/reference/decision_points/system_exposure.md b/docs/reference/decision_points/system_exposure.md
index 9ada4318..9a2f52dd 100644
--- a/docs/reference/decision_points/system_exposure.md
+++ b/docs/reference/decision_points/system_exposure.md
@@ -1,6 +1,11 @@
# System Exposure
-{% include-markdown "../../_generated/decision_points/system_exposure.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.system_exposure import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
Measuring the attack surface precisely is difficult, and we do not propose to perfectly delineate between small and controlled access.
Exposure should be judged against the system in its deployed context, which may differ from how it is commonly expected to be deployed.
@@ -12,7 +17,6 @@ Therefore, a deployer’s response to Exposure may change if such mitigations ar
If a mitigation changes exposure and thereby reduces the priority of a vulnerability, that mitigation can be considered a success.
Whether that mitigation allows the deployer to defer further action varies according to each case.
-
## Gathering Information About System Exposure
*System Exposure* is primarily used by Deployers, so the question is about whether some specific system is in fact exposed, not a hypothetical or aggregate question about systems of that type.
@@ -26,16 +30,25 @@ An analyst should also choose *open* for a phone or PC that connects to the web
Distinguishing between *small* and *controlled* is more nuanced.
If *open* has been ruled out, some suggested heuristics for differentiating the other two are as follows.
Apply these heuristics in order and stop when one of them applies.
- - If the system's networking and communication interfaces have been physically removed or disabled, choose *small*.
- - If [*Automatable*](automatable.md) is [*yes*](automatable.md), then choose *controlled*. The reasoning behind this heuristic is that if reconnaissance through exploitation is automatable, then the usual deployment scenario exposes the system sufficiently that access can be automated, which contradicts the expectations of *small*.
- - If the vulnerable component is on a network where other hosts can browse the web or receive email, choose *controlled*.
- - If the vulnerable component is in a third party library that is unreachable because the feature is unused in the surrounding product, choose *small*.
+
+- If the system's networking and communication interfaces have been physically removed or disabled, choose *small*.
+- If [*Automatable*](automatable.md) is [*yes*](automatable.md), then choose *controlled*. The reasoning behind this heuristic is that if reconnaissance through exploitation is automatable, then the usual deployment scenario exposes the system sufficiently that access can be automated, which contradicts the expectations of *small*.
+- If the vulnerable component is on a network where other hosts can browse the web or receive email, choose *controlled*.
+- If the vulnerable component is in a third party library that is unreachable because the feature is unused in the surrounding product, choose *small*.
The unreachable vulnerable component scenario may be a point of concern for stakeholders like patch suppliers who often find it more cost-effective to simply update the included library to an existing fixed version rather than try to explain to customers why the vulnerable code is unreachable in their own product.
-In those cases, we suggest the stakeholder reviews the decision outcomes of the tree to ensure the appropriate action is taken (paying attention to [_defer_](../../howto/supplier_tree.md) vs [_scheduled_](../../howto/supplier_tree.md), for example).
+In those cases, we suggest the stakeholder reviews the decision outcomes of the tree to ensure the appropriate action is taken (paying attention to [*defer*](../../howto/supplier_tree.md) vs [*scheduled*](../../howto/supplier_tree.md), for example).
If you have suggestions for further heuristics, or potential counterexamples to these, please describe the example and reasoning in an issue on the [SSVC GitHub](https://github.com/CERTCC/SSVC/issues).
## Prior Versions
-{% include-markdown "../../_generated/decision_points/system_exposure_1_0_0.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.system_exposure import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
diff --git a/docs/reference/decision_points/technical_impact.md b/docs/reference/decision_points/technical_impact.md
index f7280b9a..4b1dcaf6 100644
--- a/docs/reference/decision_points/technical_impact.md
+++ b/docs/reference/decision_points/technical_impact.md
@@ -1,6 +1,11 @@
# Technical Impact
-{% include-markdown "../../_generated/decision_points/technical_impact.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.technical_impact import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
When evaluating *Technical Impact*, recall the scope definition in the [Scope Section](../../topics/scope.md).
Total control is relative to the affected component where the vulnerability resides.
@@ -11,7 +16,6 @@ Our definition of **vulnerability** is based on the determination that some secu
We consider a security policy violation to be a technical impact—or at least, a security policy violation must have some technical instantiation.
Therefore, if there is a vulnerability then there must be some technical impact.
-
!!! tip "Gathering Information About Technical Impact"
Assessing *Technical Impact* amounts to assessing the degree of control over the vulnerable component the attacker stands to gain by exploiting the vulnerability.
@@ -28,4 +32,3 @@ Therefore, if there is a vulnerability then there must be some technical impact.
If you find a vulnerability that should have *total* *Technical Impact* but that does not answer yes to any of
these questions, please describe the example and what question we might add to this list in an issue on the
[SSVC GitHub](https://github.com/CERTCC/SSVC/issues).
-
diff --git a/docs/reference/decision_points/utility.md b/docs/reference/decision_points/utility.md
index 93e94124..1c465d41 100644
--- a/docs/reference/decision_points/utility.md
+++ b/docs/reference/decision_points/utility.md
@@ -1,13 +1,17 @@
# Utility
-{% include-markdown "../../_generated/decision_points/utility.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.utility import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
!!! tip "See also"
Utility is a combination of [Automatable](./automatable.md) and
[Value Density](./value_density.md)
-
This is a compound decision point, therefore it is a notational convenience.
*Utility* estimates an adversary's benefit compared to their effort based on the assumption that they can exploit the vulnerability.
@@ -25,7 +29,6 @@ This framing makes it easier to analytically derive these categories from a desc
Roughly, *Utility* is a combination of two things: (1) the value of each exploitation event and (2) the ease and speed with which the adversary can cause exploitation events.
We define *Utility* as laborious, efficient, or super effective, as described in the table above.
-
## Alternative Utility Outputs
Alternative heuristics can plausibly be used as proxies for adversary utility.
@@ -40,13 +43,19 @@ Price does not only track the [*Value Density*](value_density.md) of the system,
Currently, we simplify the analysis and ignore these factors.
However, future work should look for and prevent large mismatches between the outputs of the *Utility* decision point and the exploit markets.
-
-
## Previous Versions
-{% include-markdown "../../_generated/decision_points/utility_1_0_0.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.utility import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\n---\n")
+```
!!! tip "See also"
Utility v1.0.0 was a combination of [Virulence](./automatable.md) and
- [Value Density](./value_density.md)
\ No newline at end of file
+ [Value Density](./value_density.md)
diff --git a/docs/reference/decision_points/value_density.md b/docs/reference/decision_points/value_density.md
index 934231f6..11b02a3b 100644
--- a/docs/reference/decision_points/value_density.md
+++ b/docs/reference/decision_points/value_density.md
@@ -1,12 +1,19 @@
-# Value Density
+# Value Density (SSVC)
-{% include-markdown "../../_generated/decision_points/value_density.md" %}
+```python exec="true" idprefix=""
+from ssvc.decision_points.value_density import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
!!! tip "See also"
Value Density combines with [Automatability](./automatable.md) to inform
[Utility](./utility.md).
+{% include-markdown "../../_includes/value_density_cvss_ssvc.md" %}
+
!!! info "User vs. System Operator"
A “user” is anyone whose professional task is something other than the maintenance of the system or component.
diff --git a/docs/reference/index.md b/docs/reference/index.md
index af7ff33b..26a2efc0 100644
--- a/docs/reference/index.md
+++ b/docs/reference/index.md
@@ -11,19 +11,18 @@
In this section, we provide reference documentation for SSVC.
We have organized the reference documentation into two main sections:
-
-- :material-arrow-decision-outline: [**Decision Points**](decision_points/index.md)
+- :material-arrow-decision-outline: [**Decision Points**](decision_points/index.md)
---
-
+
A list of all the decision points, values, and versions.
-- :material-language-python: [**Code Documentation**](code/index.md)
-
+- :material-language-python: [**Code Documentation**](code/index.md)
+
---
Documentation for the SSVC Python modules.
-
\ No newline at end of file
+
diff --git a/docs/ssvc-calc/README.md b/docs/ssvc-calc/README.md
index db57b28d..f4e9d740 100644
--- a/docs/ssvc-calc/README.md
+++ b/docs/ssvc-calc/README.md
@@ -1,20 +1,19 @@
-# Dryad
+# Dryad
+
Stakeholder-Specific Vulnerability Categorization Calculator
Dryad is a SSVC calculator app that guides you through the simple steps needed in making
a vulnerability priority decision. The result of applying SSVC is a priority decision,
providing you with a recommended action. See the demo in our [SSVC calc website](https://democert.org/ssvc/)
-Some examples of actions are
+Some examples of actions are
defer, scheduled, out-of-cycle, and immediate.
-* The top drop-down allows you to select from multiple decision trees that map to an appropriate Role in SSVC.
-* To explore the decision tree, use the button "Show Full Tree" This will show all the branches, nodes and edges that make up the decision tree. A small zoom control horizontal range slider that can help with very large decision trees.
-* A drop-down allows you to move from Graphic mode to Simple mode.
-* There are also a number of sample CVE in a dropdown that will auto-select a number of steps in the decision tree
-* Use the "Start Decision" to navigate the tree for assesing your prioritization for a vulnerability.
-* You can also import custom decision trees and custom CVE samples for the current decision tree.
-* There is a [data](../data/) folder where there is a number of examples both of schema and examples of exported outputs.
-* You can install this directory as a folder in your public website directory. and expose it. All referenced url's are relative in the scripts and HTML files.
-
-
+- The top drop-down allows you to select from multiple decision trees that map to an appropriate Role in SSVC.
+- To explore the decision tree, use the button "Show Full Tree" This will show all the branches, nodes and edges that make up the decision tree. A small zoom control horizontal range slider that can help with very large decision trees.
+- A drop-down allows you to move from Graphic mode to Simple mode.
+- There are also a number of sample CVE in a dropdown that will auto-select a number of steps in the decision tree
+- Use the "Start Decision" to navigate the tree for assesing your prioritization for a vulnerability.
+- You can also import custom decision trees and custom CVE samples for the current decision tree.
+- There is a [data](../data/) folder where there is a number of examples both of schema and examples of exported outputs.
+- You can install this directory as a folder in your public website directory. and expose it. All referenced url's are relative in the scripts and HTML files.
diff --git a/docs/ssvc-calc/findex.html b/docs/ssvc-calc/findex.html
index afab1d97..63456ee2 100644
--- a/docs/ssvc-calc/findex.html
+++ b/docs/ssvc-calc/findex.html
@@ -1,3 +1,18 @@
+
+
diff --git a/docs/ssvc-calc/index.md b/docs/ssvc-calc/index.md
index 2152c657..7e9df37f 100644
--- a/docs/ssvc-calc/index.md
+++ b/docs/ssvc-calc/index.md
@@ -1,4 +1,5 @@
# SSVC Calculator
+
), which is a good place to go to check on progress or help.
Plans for future work focus on further requirements gathering, analysis of types of risk, and further testing of the reliability of the decision process.
## Requirements Gathering via Sociological Research
@@ -10,9 +10,9 @@ Plans for future work focus on further requirements gathering, analysis of types
The community should know what users of a vulnerability prioritization system want.
To explore their needs, it is important to understand how people actually use CVSS and what they think it tells them.
In general, such empirical, grounded evidence about what practitioners and decision makers want from vulnerability scoring is lacking.
-We have based SSVC’s methodology on multiple decades of professional experience and myriad informal conversations with practitioners.
-Such evidence is not a bad place to start, but it does not lend itself to examination and validation by others.
-The purpose of understanding practitioner expectations is to inform what a vulnerability-prioritization methodology should actually provide by matching it to what people need or expect.
+We have based SSVC’s methodology on multiple decades of professional experience and myriad informal conversations with practitioners.
+Such evidence is not a bad place to start, but it does not lend itself to examination and validation by others.
+The purpose of understanding practitioner expectations is to inform what a vulnerability-prioritization methodology should actually provide by matching it to what people need or expect.
The method this future work should take is long-form, structured interviews.
We do not expect anyone to have access to enough consumers of CVSS to get statistically valid results out of a short survey, nor to pilot a long survey.
@@ -43,7 +43,6 @@ The “credible effects” to consider are those of all vulnerabilities remediat
How exactly to aggregate these different effects is not currently specified except to say that the unit of analysis is the whole work item.
Future work should provide some examples of how this holistic analysis of multiple vulnerabilities remediated in one patch should be conducted.
-
## Further Decision Tree Testing
More testing with diverse analysts is necessary before the decision trees are reliable. In this context, **reliable** means that two analysts, given the same vulnerability description and decision process description, will reach the same decision. Such reliability is important if scores and priorities are going to be useful. If they are not reliable, they will vary widely over time and among analysts. Such variability makes it impossible to tell whether a difference in scores is really due to one vulnerability being higher priority than other.
diff --git a/docs/topics/index.md b/docs/topics/index.md
index eff8e9f3..d1e73a6d 100644
--- a/docs/topics/index.md
+++ b/docs/topics/index.md
@@ -11,7 +11,6 @@
[SSVC How-To](../howto/index.md) provides practical guidance for implementing SSVC in your organization.
For technical reference, see [Reference](../reference/index.md).
-
This documentation defines a testable Stakeholder-Specific Vulnerability Categorization (SSVC) for prioritizing actions during vulnerability management.
The stakeholders in vulnerability management are diverse.
This diversity must be accommodated in the main functionality, rather than squeezed into hard-to-use optional features.
@@ -23,7 +22,6 @@ As such, the modeling framework is important but difficult to pin down.
We approach this problem as a satisficing process.
We do not seek optimal formalisms, but an adequate formalism.
-
## Key Concepts in SSVC Decision Models
SSVC models individual vulnerability management decisions. It is built around the following concepts:
@@ -34,14 +32,13 @@ SSVC models individual vulnerability management decisions. It is built around th
are an ordered set of enumerated values. They are ordered because they are sortable in some dimension, usually
having to do with priority or urgency. They are enumerated because they are finite and discrete.
- **Outcomes** are the dependent variables that are relevant to the decision. Each outcome represents a different
- possible result of the decision.
-- **Outcome Values** are the possible values for an Outcome. Outcomes are similarly defined as an ordered set of
+ possible result of the decision.
+- **Outcome Values** are the possible values for an Outcome. Outcomes are similarly defined as an ordered set of
enumerated values, usually indicating a priority or urgency.
- A **Policy** is a mapping from each combination of decision point values to the set of outcome values.
- A **Decision Function** is a function that accepts a set of decision point values and returns an outcome value based
on a policy.
-
```mermaid
---
title: Decision Points and Values
@@ -69,7 +66,6 @@ flowchart LR
Policy --> Outcomes
```
-
!!! question "Where do the trees come in?"
Our initial concept for SSVC's decision modeling was based on decision trees.
@@ -89,7 +85,7 @@ flowchart LR
convenient way to visualize the decision function, but they are not a requirement of the model.
## Topics Overview
-
+
The remainder of this section is organized as follows:
diff --git a/docs/topics/information_sources.md b/docs/topics/information_sources.md
index 2e080f6f..3ed11424 100644
--- a/docs/topics/information_sources.md
+++ b/docs/topics/information_sources.md
@@ -21,7 +21,6 @@ Although the lists are all different, we expect they are all valid information s
We are not aware of a comparative study of the different lists of active exploits; however, we expect they have similar properties to block lists of network touchpoints [@metcalf2015blocklist] and malware [@kuhrer2014paint].
Namely, each list has a different view and vantage on the problem, which makes them appear to be different, but each list accurately represents its particular vantage at a point in time.
-
## System Exposure
[*System Exposure*](../reference/decision_points/system_exposure.md) could be informed by the various scanning platforms such as Shodan and Shadowserver.
@@ -30,6 +29,7 @@ Such scans do not find all [*open*](../reference/decision_points/system_exposure
Scanning software, such as the open-source tool Nessus, could be used to scan for connectivity inside an organization to catalogue what devices should be scored [*controlled*](../reference/decision_points/system_exposure.md) if, say, the scan finds them on an internal network where devices regularly connect to the Internet.
---
+
## Adapting other Information Sources
Some information sources that were not designed with SSVC in mind can be adapted to work with it.
@@ -54,16 +54,16 @@ The interpretation is different for CVSS version 3 than version 4.
That is, if the vulnerability leads to a high impact on the confidentiality and integrity of the vulnerable system, then that is equivalent to total technical impact on the system.
-The following considerations are accounted for in this recommendation.
+The following considerations are accounted for in this recommendation.
1. A denial of service condition is modeled as a *partial* [*Technical Impact*](../reference/decision_points/technical_impact.md).
Therefore, a high availability impact to the vulnerable system should not be mapped to *total* [*Technical Impact*](../reference/decision_points/technical_impact.md) on its own.
-2. There may be situations in which a high confidentiality impact is sufficient for total technical impact; for example, disclosure of the root or administrative password for the system leads to total technical control of the system.
-So this suggested mapping is a useful heuristic, but there may be exceptions, depending on exactly what the CVSS v4 metric value assignment norms are and become for these situations.
+2. There may be situations in which a high confidentiality impact is sufficient for total technical impact; for example, disclosure of the root or administrative password for the system leads to total technical control of the system.
+So this suggested mapping is a useful heuristic, but there may be exceptions, depending on exactly what the CVSS v4 metric value assignment norms are and become for these situations.
3. While the Subsequent System impact metric group in CVSS v4 is useful, those concepts are not captured by [*Technical Impact*](../reference/decision_points/technical_impact.md).
-Subsequent System impacts are captured, albeit in different framings, by decision points such as [*Situated Safety Impact*](../reference/decision_points/safety_impact.md), [*Mission Impact*](../reference/decision_points/mission_impact.md), and [*Value Density*](../reference/decision_points/value_density.md).
-There is not a direct mapping between the subsequent system impact metric group and these decision points, except in the case of [*Public Safety Impact*](../reference/decision_points/public_safety_impact.md) and the CVSS v4 environmental metrics for Safety Impact in the subsequent system metric group.
-In that case, both definitions map back to the same safety impact standard for definitions (IEC 61508) and so are easily mapped to each other.
+Subsequent System impacts are captured, albeit in different framings, by decision points such as [*Situated Safety Impact*](../reference/decision_points/safety_impact.md), [*Mission Impact*](../reference/decision_points/mission_impact.md), and [*Value Density*](../reference/decision_points/value_density.md).
+There is not a direct mapping between the subsequent system impact metric group and these decision points, except in the case of [*Public Safety Impact*](../reference/decision_points/public_safety_impact.md) and the CVSS v4 environmental metrics for Safety Impact in the subsequent system metric group.
+In that case, both definitions map back to the same safety impact standard for definitions (IEC 61508) and so are easily mapped to each other.
#### CVSS v3 and Technical Impact
@@ -72,10 +72,10 @@ For CVSS v3, the impact metric group cannot be directly mapped to [*Technical Im
If the CVSS version 3 value of “Scope” is “Unchanged,” then the recommendation is the same as that for CVSS v4, above, as the impact metric group is information exclusively about the vulnerable system.
If the CVSS version 3 value of “Scope” is “Changed,” then the impact metrics may be about either the vulnerable system or the subsequent systems, based on whichever makes the final score higher.
Since [*Technical Impact*](../reference/decision_points/technical_impact.md) is based only on the vulnerable system impacts, if "Scope" is "Changed" then the ambiguity between vulnerable and subsequent system impacts is not documented in the vector string.
-This ambiguity makes it impossible to cleanly map the [*Technical Impact*](../reference/decision_points/technical_impact.md) value in this case.
+This ambiguity makes it impossible to cleanly map the [*Technical Impact*](../reference/decision_points/technical_impact.md) value in this case.
!!! tip "Mapping CVSS v3 to Technical Impact"
-
+
Summarizing the discussion above, the mapping between CVSS v3 and [*Technical Impact*](../reference/decision_points/technical_impact.md) is
| CVSS Scope | Confidentiality
(C) | Integrity
(I) | Availability
(A) | [*Technical Impact*](../reference/decision_points/technical_impact.md) |
@@ -85,7 +85,6 @@ This ambiguity makes it impossible to cleanly map the [*Technical Impact*](../re
| Unchanged | Low (L) or None (N) | High (H) | *any* | Partial |
| Changed | *any* | *any* | *any* | (ambiguous) |
-
### CWE and Exploitation
As mentioned in the discussion of [*Exploitation*](../reference/decision_points/exploitation.md), [CWE](https://cwe.mitre.org/) could be used to inform one of the conditions that satisfy [*proof of concept*](../reference/decision_points/exploitation.md).
diff --git a/docs/topics/items_with_same_priority.md b/docs/topics/items_with_same_priority.md
index 87641842..e1b3f661 100644
--- a/docs/topics/items_with_same_priority.md
+++ b/docs/topics/items_with_same_priority.md
@@ -31,5 +31,3 @@ The priority is equivalent.
fine-grained priorities within qualitative categories anyway.
With our system, organizations can be more deliberate about conveniently organizing work that is of equivalent priority.
-
-
diff --git a/docs/topics/limitations.md b/docs/topics/limitations.md
index c6b365fd..2b2d0bf5 100644
--- a/docs/topics/limitations.md
+++ b/docs/topics/limitations.md
@@ -25,16 +25,16 @@ This is not a calculation of any kind, just an assignment of a label which may m
Of course, these labels are dangerous, as they may be misused as numbers.
Therefore, we prefer the use *defer*, *scheduled*, etc., as listed in
[Enumerating Vulnerability Management Actions](../howto/deployer_tree.md).
-
+
## Expanded Context
We incorporated a wider variety of inputs from contexts beyond the affected component.
Some organizations are not prepared or configured to reliably produce such data (e.g., around mission impact or safety impact). There is adequate guidance for how to elicit and curate this type information from various risk management frameworks, including OCTAVE [@caralli2007octave]. Not every organization is going to have sufficiently mature risk management functions to apply SSVC.\
-
+
This second limitation should be approached with two strategies:
1. Organizations should be encouraged and enabled to mature their risk management capabilities
-2. In the meantime, organizations such as NIST could consider developing default advice.
+2. In the meantime, organizations such as NIST could consider developing default advice.
The most practical framing of this approach might be for the NIST NVD to produce scores from the perspective of a
- new stakeholder—something like “national security” or “public well-being” that is explicitly a sort of default
+ new stakeholder—something like “national security” or “public well-being” that is explicitly a sort of default
advice for otherwise uninformed organizations that can then explicitly account for national priorities, such as critical infrastructure.
diff --git a/docs/topics/related_systems.md b/docs/topics/related_systems.md
index 4a4ed6ee..16c71596 100644
--- a/docs/topics/related_systems.md
+++ b/docs/topics/related_systems.md
@@ -107,7 +107,6 @@ CVSS is one-size-fits-all by design.
These customization efforts struggle with adapting CVSS because it was not designed to be adaptable to different stakeholder considerations.
The SSVC section [Tree Construction and Customization Guidance](../howto/tree_customization.md) explains how stakeholders or stakeholder communities can adapt SSVC in a reliable way that still promotes repeatability and communication.
-
## vPrioritizer
vPrioritizer is an open-source project that attempts to integrate asset management and vulnerablity prioritization.
@@ -118,5 +117,3 @@ In that sense, it is compatible with any of methods mentioned above or SSVC.
However, SSVC would be better suited to address vPrioritizer's broad spectrum asset management data.
For example, vPrioritizer aims to collect data points on topics such as asset significance.
Asset significance could be expressed through the SSVC decision points of [*Mission Impact*](../reference/decision_points/mission_impact.md) and situated [*Well-being Impact*](../reference/decision_points/human_impact.md), but it does not have a ready expression in CVSS, EPSS, or VPR.
-
-
diff --git a/docs/topics/representing_information.md b/docs/topics/representing_information.md
index 0d09753f..d5c6f471 100644
--- a/docs/topics/representing_information.md
+++ b/docs/topics/representing_information.md
@@ -1,7 +1,7 @@
# Representing Information for Decisions About Vulnerabilities
We propose that decisions about vulnerabilities—rather than their severity—are a more useful approach.
-Our design goals for the decision-making process are to
+Our design goals for the decision-making process are to
- clearly define whose decisions are involved
- properly use evidentiary categories
@@ -35,23 +35,22 @@ Therefore, under a Gaussian error distribution, 8.9 is really 60\% high and 40\%
SSVC decisions should be distinct and crisp, without such statistical overlaps.
We avoid numerical representations for either inputs or outputs of a vulnerability management decision process.
-Quantified metrics are more useful when
+Quantified metrics are more useful when
-1. data for decision making is available, and
+1. data for decision making is available, and
2. the stakeholders agree on how to measure.
Vulnerability management does not yet meet either criterion.
Furthermore, it is not clear to what extent measurements about a vulnerability can be informative about other vulnerabilities.
Each vulnerability has a potentially unique relationship to the socio-technical system in which it exists, including the Internet.
-
## Be Based on Reliably Available Evidence
Vulnerability management decisions are often contextual: given what is known at the time, the decision is to do X.
But what is known can change over time, which can and should influence the decision.
The context of the vulnerability, and the systems it impacts, are inextricably linked to managing it.
Some information about the context will be relatively static over time, such as the contribution of a system to an organization's mission.
-Other information can change rapidly as events occur, such as the public release of an exploit or observation of attacks.
+Other information can change rapidly as events occur, such as the public release of an exploit or observation of attacks.
Temporal and environmental considerations should be primary, not optional as they are in CVSS.
We discuss the temporal aspects further in [Information Changes over Time](../howto/bootstrap/use.md).
@@ -65,9 +64,9 @@ Transparency should improve trust in the results.
Finally, any result of a decision-making process should be **explainable**
Explainable is defined and used with its common meaning, not as it is used in the research area of explainable artificial intelligence.
An explanation should make the process intelligible to an interested, competent, non-expert person.
-There are at least two reasons common explainability is important:
+There are at least two reasons common explainability is important:
-1. for troubleshooting and error correction and
+1. for troubleshooting and error correction and
2. for justifying proposed decisions.
## Summary
@@ -75,17 +74,16 @@ There are at least two reasons common explainability is important:
To summarize, the following are our design goals for a vulnerability
management process:
- - Outputs are decisions.
+- Outputs are decisions.
- - Pluralistic recommendations are made among a manageable number of
+- Pluralistic recommendations are made among a manageable number of
stakeholder groups.
- - Inputs are qualitative.
+- Inputs are qualitative.
- - Outputs are qualitative, and there are no (unjustified) shifts to
+- Outputs are qualitative, and there are no (unjustified) shifts to
quantitative calculations.
- - Process justification is transparent.
-
- - Results are explainable.
+- Process justification is transparent.
+- Results are explainable.
diff --git a/docs/topics/risk_tolerance_and_priority.md b/docs/topics/risk_tolerance_and_priority.md
index 631be2dc..e738e411 100644
--- a/docs/topics/risk_tolerance_and_priority.md
+++ b/docs/topics/risk_tolerance_and_priority.md
@@ -21,11 +21,10 @@ A successful vulnerability management practice must balance at least two risks:
problems that could arise from making changes to production systems.
2. **Vulnerability risk**: the potential costs of incidents resulting from exploitation of vulnerable systems
-
In developing the decision trees in this document, we had in mind stakeholders with a moderate tolerance for risk. The resulting trees reflect that assumption. Organizations may of course be more or less conservative in their own vulnerability management practices, and we cannot presume to determine how an organization should balance their risk.
We therefore remind our readers that the labels on the trees (defer, immediate, etc.) can and should be customized to
-suit the needs of individual stakeholders wherever necessary and appropriate.
+suit the needs of individual stakeholders wherever necessary and appropriate.
---
@@ -37,5 +36,3 @@ suit the needs of individual stakeholders wherever necessary and appropriate.
the most urgent response.
- On the other hand, an organization with a high aversion to vulnerability risk could elevate the priority of many
branches to ensure fixes are deployed quickly.
-
-
diff --git a/docs/topics/state_of_practice.md b/docs/topics/state_of_practice.md
index e2009777..a9c0997e 100644
--- a/docs/topics/state_of_practice.md
+++ b/docs/topics/state_of_practice.md
@@ -1,5 +1,4 @@
-
# Current state of practice
**Vulnerability management** covers “the discovery, analysis, and handling of new or reported security vulnerabilities in information systems \[and\] the detection of and response to known vulnerabilities in order to prevent them from being exploited” [@csirtservices_v2].
diff --git a/docs/topics/vulnerability_management_decisions.md b/docs/topics/vulnerability_management_decisions.md
index 9744d8fd..6edc2258 100644
--- a/docs/topics/vulnerability_management_decisions.md
+++ b/docs/topics/vulnerability_management_decisions.md
@@ -8,4 +8,3 @@ The “what” is about the scope, both in how the affected system is defined an
While we strive to make our examples realistic, we invite the community to engage and conduct empirical assessments to test them.
The following construction should be treated as an informed hypothesis rather than a conclusion.
-
diff --git a/docs/topics/worked_example.md b/docs/topics/worked_example.md
index 21542a34..ba00739b 100644
--- a/docs/topics/worked_example.md
+++ b/docs/topics/worked_example.md
@@ -43,7 +43,7 @@ use its installation to remotely identify targets.
However, since most of the hospital’s clients have not installed the app, and for nearly all cases, physical proximity
to the device is necessary; therefore, we select [*small*](../reference/decision_points/system_exposure.md) and move on to ask about mission impact.
-According to the fictional pilot scenario,
+According to the fictional pilot scenario,
> Our mission dictates that the first and foremost priority is to contribute to human welfare and to uphold the Hippocratic oath (do no harm).
diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md
index ba3c6f09..802ff565 100644
--- a/docs/tutorials/index.md
+++ b/docs/tutorials/index.md
@@ -44,9 +44,6 @@ SSVC can be used in conjunction with other tools and methodologies to help prior
This information can be used to inform the [Exploitation](../reference/decision_points/exploitation.md) decision point in the
[Supplier](../howto/supplier_tree.md), [Deployer](../howto/deployer_tree.md), and [Coordinator Publication](../howto/publication_decision.md) decision models.
-
-
-
## Videos
Provided below are videos that provide an overview of SSVC and the implementation of decision models.
@@ -71,6 +68,6 @@ We've collected a list of articles and blog posts that provide additional inform
| SEI | [Prioritizing Vulnerability Response with a Stakeholder-Specific Vulnerability Categorization](https://insights.sei.cmu.edu/blog/prioritizing-vulnerability-response-with-a-stakeholder-specific-vulnerability-categorization/) |
| CISA | [Stakeholder-Specific Vulnerability Categorization (SSVC)](https://www.cisa.gov/stakeholder-specific-vulnerability-categorization-ssvc) |
| Qualys | [Effective Vulnerability Management with Stakeholder Specific Vulnerability Categorization (SSVC) and Qualys TruRisk](https://blog.qualys.com/product-tech/2022/11/30/effective-vulnerability-management-with-ssvc-and-qualys-trurisk) |
-| Vulcan Cyber | [The SSVC risk prioritization method: what it is, when to use it, and alternatives](https://vulcan.io/blog/the-ssvc-risk-prioritization-method-what-it-is-when-to-use-it-and-alternatives/) |
+| Vulcan Cyber | [The SSVC risk prioritization method: what it is, when to use it, and alternatives](https://vulcan.io/blog/the-ssvc-risk-prioritization-method-what-it-is-when-to-use-it-and-alternatives/) |
-Have a link to something we missed? Let us know in an [issue](https://github.com/CERTCC/SSVC/issues/new).
\ No newline at end of file
+Have a link to something we missed? Let us know in an [issue](https://github.com/CERTCC/SSVC/issues/new).
diff --git a/mkdocs.yml b/mkdocs.yml
index 3e2ff861..2e47540c 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -71,6 +71,42 @@ nav:
- Human Impact: 'reference/decision_points/human_impact.md'
- Public Safety Impact: 'reference/decision_points/public_safety_impact.md'
- Utility: 'reference/decision_points/utility.md'
+ - CVSS-based decision points:
+ - 'reference/decision_points/cvss/index.md'
+ - Qualitative Severity: 'reference/decision_points/cvss/qualitative_severity.md'
+ - Base Metrics:
+ - Attack Vector: 'reference/decision_points/cvss/attack_vector.md'
+ - Attack Complexity: 'reference/decision_points/cvss/attack_complexity.md'
+ - Attack Requirements: 'reference/decision_points/cvss/attack_requirements.md'
+ - Privileges Required: 'reference/decision_points/cvss/privileges_required.md'
+ - User Interaction: 'reference/decision_points/cvss/user_interaction.md'
+ - Confidentiality Impact: 'reference/decision_points/cvss/confidentiality_impact.md'
+ - Subsequent Confidentiality Impact: 'reference/decision_points/cvss/subsequent_confidentiality_impact.md'
+ - Integrity Impact: 'reference/decision_points/cvss/integrity_impact.md'
+ - Subsequent Integrity Impact: 'reference/decision_points/cvss/subsequent_integrity_impact.md'
+ - Availability Impact: 'reference/decision_points/cvss/availability_impact.md'
+ - Subsequent Availability Impact: 'reference/decision_points/cvss/subsequent_availability_impact.md'
+ - Threat Metrics:
+ - Exploit Maturity: 'reference/decision_points/cvss/exploit_maturity.md'
+ - Environmental Metrics:
+ - Confidentiality Requirement: 'reference/decision_points/cvss/confidentiality_requirement.md'
+ - Integrity Requirement: 'reference/decision_points/cvss/integrity_requirement.md'
+ - Availability Requirement: 'reference/decision_points/cvss/availability_requirement.md'
+ - Supplemental Metrics:
+ - Safety: 'reference/decision_points/cvss/safety.md'
+ - Automatable: 'reference/decision_points/cvss/automatable.md'
+ - Provider Urgency: 'reference/decision_points/cvss/provider_urgency.md'
+ - Recovery: 'reference/decision_points/cvss/recovery.md'
+ - Value Density: 'reference/decision_points/cvss/value_density.md'
+ - Vulnerability Response Effort: 'reference/decision_points/cvss/vulnerability_response_effort.md'
+ - Older Metrics:
+ - Authentication: 'reference/decision_points/cvss/authentication.md'
+ - Collateral Damage Potential: 'reference/decision_points/cvss/collateral_damage_potential.md'
+ - Impact Bias: 'reference/decision_points/cvss/impact_bias.md'
+ - Remediation Level: 'reference/decision_points/cvss/remediation_level.md'
+ - Report Confidence: 'reference/decision_points/cvss/report_confidence.md'
+ - Scope: 'reference/decision_points/cvss/scope.md'
+ - Target Distribution: 'reference/decision_points/cvss/target_distribution.md'
- Code:
- Intro: 'reference/code/index.md'
- CSV Analyzer: 'reference/code/analyze_csv.md'
@@ -117,6 +153,7 @@ plugins:
data_path: 'data/csvs'
- bibtex:
bib_file: 'doc/md_src_files/sources_ssvc.bib'
+ - markdown-exec
- mkdocstrings:
handlers:
python:
diff --git a/requirements.txt b/requirements.txt
index 7a27ea0b..f4294acb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,15 +1,17 @@
mkdocs==1.6.1
-mkdocs-bibtex==2.16.2
-mkdocs-include-markdown-plugin==7.1.2
+mkdocs-bibtex==4.2.1
+mkdocs-include-markdown-plugin==7.1.4
mkdocs-table-reader-plugin==3.1.0
-mkdocs-material==9.5.49
+mkdocs-material==9.6.5
mkdocs-material-extensions==1.3.1
-mkdocstrings==0.27.0
-mkdocstrings-python==1.13.0
+mkdocstrings==0.28.2
+mkdocstrings-python==1.16.2
mkdocs-print-site-plugin==2.6.0
-dataclasses-json==0.6.7
+markdown-exec==1.10.0
thefuzz==0.22.1
pandas==2.2.3
scikit-learn==1.6.1
jsonschema==4.23.0
networkx==3.4.2
+pydantic==2.10.6
+semver==3.0.4
\ No newline at end of file
diff --git a/src/README.md b/src/README.md
index 84b8f226..e90e39d1 100644
--- a/src/README.md
+++ b/src/README.md
@@ -7,13 +7,14 @@ This directory holds helper scripts that can make managing or using SSVC easier.
This python script takes a CSV of the format in the `../data` directory and gets you (most of the way) to a pretty decision tree visualization. It creates a LaTeX file that can create a PDF (and from there, a PNG or whatever you want).
`python SSVC_csv-to-latex.py --help` works and should explain all your options.
-When the script finishes, it will also print a message with instructions for creating the PDF or PNG from the tex. A potential future improvement is to call `latexmk` directly from the python script.
+When the script finishes, it will also print a message with instructions for creating the PDF or PNG from the tex. A potential future improvement is to call `latexmk` directly from the python script.
Example usage:
+
```
python SSVC_csv-to-latex.py --input=../data/ssvc_2_deployer_simplified.csv --output=tmp.tex --delim="," --columns="0,2,1" --label="3" --header-row --priorities="defer, scheduled, out-of-cycle, immediate"
```
Dependencies: LaTeX.
-To install latex, see https://www.latex-project.org/get/
-`latexmk` is a helper script that is not included in all distributions by default; if you need it, see https://ctan.org/pkg/latexmk/?lang=en
+To install latex, see
+`latexmk` is a helper script that is not included in all distributions by default; if you need it, see
diff --git a/src/SSVC_csv-to-latex.py b/src/SSVC_csv-to-latex.py
index 0e2953fe..1112d0fe 100755
--- a/src/SSVC_csv-to-latex.py
+++ b/src/SSVC_csv-to-latex.py
@@ -1,4 +1,18 @@
#!/usr/bin/python
+
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
##########
## put import statements here
import optparse, sys, string, glob, re
@@ -224,7 +238,9 @@ def print_forest_options(
"""
)
location.write(pri5string)
- location.write("}\n") # close the last tikzset; forestset is already closed
+ location.write(
+ "}\n"
+ ) # close the last tikzset; forestset is already closed
def begin_forest(location):
@@ -334,7 +350,9 @@ def main():
if path[i] not in dpoint_values[i]:
dpoint_values[i].append(path[i])
for i in range(depth):
- dpoint_values[i].sort(key=lambda j: sort_order[i].index(j), reverse=True)
+ dpoint_values[i].sort(
+ key=lambda j: sort_order[i].index(j), reverse=True
+ )
# reverse because the latex will flip it again
# loop twice so we don't sort every time we check a new path
@@ -366,7 +384,9 @@ def main():
del tmp_path[-1]
i = i - 1
ofile.write(latex_brace_close) # close each latex brace
- del tmp_path[-1] # every time we close a brace, update the path to reflect
+ del tmp_path[
+ -1
+ ] # every time we close a brace, update the path to reflect
else: # "Normal" case
if counts[i] == len(dpoint_values[i]):
try:
diff --git a/src/enumerate-coord-publish-options.sh b/src/enumerate-coord-publish-options.sh
index 93a83982..d9e4739f 100755
--- a/src/enumerate-coord-publish-options.sh
+++ b/src/enumerate-coord-publish-options.sh
@@ -1,5 +1,20 @@
#!/bin/sh
+#
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+#
+
# row numbers make change discussion a lot easier
i=1
diff --git a/src/enumerate-coord-triage-options.sh b/src/enumerate-coord-triage-options.sh
index ef4b5857..9c283223 100755
--- a/src/enumerate-coord-triage-options.sh
+++ b/src/enumerate-coord-triage-options.sh
@@ -1,5 +1,20 @@
#!/bin/sh
+#
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+#
+
# row numbers make change discussion a lot easier
i=1
diff --git a/src/enumerate-deployer-options.sh b/src/enumerate-deployer-options.sh
index f2990052..bb2d63a8 100755
--- a/src/enumerate-deployer-options.sh
+++ b/src/enumerate-deployer-options.sh
@@ -1,5 +1,20 @@
#!/bin/sh
+#
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+#
+
# row numbers make change discussion a lot easier
i=1
diff --git a/src/enumerate-supplier-options.sh b/src/enumerate-supplier-options.sh
index faba3e93..cbee24c5 100755
--- a/src/enumerate-supplier-options.sh
+++ b/src/enumerate-supplier-options.sh
@@ -1,5 +1,20 @@
#!/bin/sh
+#
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+#
+
# row numbers make change discussion a lot easier
i=1
diff --git a/src/outcomes_to_json.py b/src/outcomes_to_json.py
new file mode 100644
index 00000000..192c8169
--- /dev/null
+++ b/src/outcomes_to_json.py
@@ -0,0 +1,29 @@
+#!/usr/bin/python3
+
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
+from ssvc.outcomes import groups
+from ssvc.outcomes.base import OutcomeGroup
+
+
+def main():
+ for x in dir(groups):
+ outcome = getattr(groups, x)
+ if type(outcome) == OutcomeGroup:
+ with open(f"../data/json/outcomes/{x}.json", "w") as f:
+ f.write(outcome.model_dump_json(indent=2))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/ssvc/__init__.py b/src/ssvc/__init__.py
index 31995ad1..6706583d 100644
--- a/src/ssvc/__init__.py
+++ b/src/ssvc/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
diff --git a/src/ssvc/_mixins.py b/src/ssvc/_mixins.py
index 609c7b73..414c99e1 100644
--- a/src/ssvc/_mixins.py
+++ b/src/ssvc/_mixins.py
@@ -1,10 +1,8 @@
#!/usr/bin/env python
"""
-file: _basics
-author: adh
-created_at: 9/20/23 4:51 PM
+This module provides mixin classes for adding features to SSVC objects.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -17,16 +15,15 @@
# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
# U.S. Patent and Trademark Office by Carnegie Mellon University
-from dataclasses import dataclass, field
from typing import Optional
-from dataclasses_json import config, dataclass_json
+from pydantic import BaseModel, ConfigDict, field_validator
+from semver import Version
from . import _schemaVersion
-@dataclass_json
-@dataclass(kw_only=True)
-class _Versioned:
+
+class _Versioned(BaseModel):
"""
Mixin class for versioned SSVC objects.
"""
@@ -34,9 +31,25 @@ class _Versioned:
version: str = "0.0.0"
schemaVersion: str = _schemaVersion
-@dataclass_json
-@dataclass(kw_only=True)
-class _Namespaced:
+ @field_validator("version")
+ @classmethod
+ def validate_version(cls, value):
+ """
+ Validate the version field.
+ Args:
+ value: a string representing a version number
+
+ Returns:
+ a fully qualified version number
+
+ Raises:
+ ValueError: if the value is not a valid version number
+ """
+ version = Version.parse(value, optional_minor_and_patch=True)
+ return version.__str__()
+
+
+class _Namespaced(BaseModel):
"""
Mixin class for namespaced SSVC objects.
"""
@@ -44,9 +57,7 @@ class _Namespaced:
namespace: str = "ssvc"
-@dataclass_json
-@dataclass(kw_only=True)
-class _Keyed:
+class _Keyed(BaseModel):
"""
Mixin class for keyed SSVC objects.
"""
@@ -58,21 +69,17 @@ def exclude_if_none(value):
return value is None
-@dataclass_json
-@dataclass(kw_only=True)
-class _Commented:
+class _Commented(BaseModel):
"""
Mixin class for commented SSVC objects.
"""
- _comment: Optional[str] = field(
- default=None, metadata=config(exclude=exclude_if_none)
- )
+ _comment: Optional[str] = None
+
+ model_config = ConfigDict(json_encoders={Optional[str]: exclude_if_none})
-@dataclass_json
-@dataclass(kw_only=True)
-class _Base:
+class _Base(BaseModel):
"""
Base class for SSVC objects.
"""
diff --git a/src/ssvc/csv_analyzer.py b/src/ssvc/csv_analyzer.py
index c1c668af..159d43dc 100644
--- a/src/ssvc/csv_analyzer.py
+++ b/src/ssvc/csv_analyzer.py
@@ -40,7 +40,7 @@
Higher values imply more important features.
"""
-# Copyright (c) 2023-2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -95,7 +95,9 @@ def _imp_df(column_names: list, importances: list) -> pd.DataFrame:
a dataframe of feature importances
"""
df = (
- pd.DataFrame({"feature": column_names, "feature_importance": importances})
+ pd.DataFrame(
+ {"feature": column_names, "feature_importance": importances}
+ )
.sort_values("feature_importance", ascending=False)
.reset_index(drop=True)
)
@@ -184,7 +186,9 @@ def _perm_feat_imp(model, x, y):
def _parse_args(args) -> argparse.Namespace:
# parse command line
- parser = argparse.ArgumentParser(description="Analyze an SSVC tree csv file")
+ parser = argparse.ArgumentParser(
+ description="Analyze an SSVC tree csv file"
+ )
parser.add_argument(
"csvfile", metavar="csvfile", type=str, help="the csv file to analyze"
)
@@ -371,8 +375,12 @@ def check_topological_order(df, target):
for u in H.nodes:
H.nodes[u]["outcome"] = G.nodes[u]["outcome"]
- logger.debug(f"Original graph: {len(G.nodes)} nodes with {len(G.edges)} edges")
- logger.debug(f"Reduced graph: {len(H.nodes)} nodes with {len(H.edges)} edges")
+ logger.debug(
+ f"Original graph: {len(G.nodes)} nodes with {len(G.edges)} edges"
+ )
+ logger.debug(
+ f"Reduced graph: {len(H.nodes)} nodes with {len(H.edges)} edges"
+ )
problems = []
# check if the outcome is topologically sorted
diff --git a/src/ssvc/decision_points/__init__.py b/src/ssvc/decision_points/__init__.py
index c4f79b7c..0ffc1a00 100644
--- a/src/ssvc/decision_points/__init__.py
+++ b/src/ssvc/decision_points/__init__.py
@@ -17,4 +17,17 @@
- A description
- A key (a short, unique string) that can be used to identify the value in a shorthand way
"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
from .base import SsvcDecisionPoint, SsvcDecisionPointValue
diff --git a/src/ssvc/decision_points/automatable.py b/src/ssvc/decision_points/automatable.py
index c7051279..843626b5 100644
--- a/src/ssvc/decision_points/automatable.py
+++ b/src/ssvc/decision_points/automatable.py
@@ -1,10 +1,9 @@
#!/usr/bin/env python
+
"""
-file: automatable
-author: adh
-created_at: 9/21/23 10:37 AM
+Provides the Automatable decision point and its values.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -66,11 +65,14 @@
values=(AUT_NO, AUT_YES),
)
+# always append new VERSIONS to this list, do not remove old ones
+VERSIONS = (VIRULENCE_1, AUTOMATABLE_2)
+LATEST = VERSIONS[-1]
+
def main():
- versions = (VIRULENCE_1, AUTOMATABLE_2)
+ print_versions_and_diffs(VERSIONS)
- print_versions_and_diffs(versions)
if __name__ == "__main__":
main()
diff --git a/src/ssvc/decision_points/base.py b/src/ssvc/decision_points/base.py
index 3e1d32ba..869e3263 100644
--- a/src/ssvc/decision_points/base.py
+++ b/src/ssvc/decision_points/base.py
@@ -1,10 +1,9 @@
#!/usr/bin/env python
+
"""
-file: decisionpoints
-author: adh
-created_at: 9/20/23 10:07 AM
+Defines the formatting for SSVC Decision Points.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -18,10 +17,8 @@
# U.S. Patent and Trademark Office by Carnegie Mellon University
import logging
-from dataclasses import dataclass
-from typing import Iterable
-from dataclasses_json import dataclass_json
+from pydantic import BaseModel
from ssvc._mixins import _Base, _Keyed, _Namespaced, _Versioned
@@ -58,27 +55,18 @@ def _reset_registered():
REGISTERED_DECISION_POINTS = []
-@dataclass_json
-@dataclass(kw_only=True)
-class SsvcDecisionPointValue(_Base, _Keyed):
+class SsvcDecisionPointValue(_Base, _Keyed, BaseModel):
"""
Models a single value option for a decision point.
"""
-@dataclass_json
-@dataclass(kw_only=True)
-class SsvcDecisionPoint(
- _Base,
- _Keyed,
- _Versioned,
- _Namespaced,
-):
+class SsvcDecisionPoint(_Base, _Keyed, _Versioned, _Namespaced, BaseModel):
"""
Models a single decision point as a list of values.
"""
- values: Iterable[SsvcDecisionPointValue] = ()
+ values: list[SsvcDecisionPointValue] = []
def __iter__(self):
"""
@@ -86,13 +74,12 @@ def __iter__(self):
"""
return iter(self.values)
- def __post_init__(self):
+ def __init__(self, **data):
+ super().__init__(**data)
register(self)
- if isinstance(self.values[0], dict):
- self.values = tuple(
- SsvcDecisionPointValue.from_dict(v) for v in self.values
- )
+ def __post_init__(self):
+ register(self)
def main():
@@ -116,7 +103,7 @@ def main():
version="1.0.0",
)
- print(dp.to_json(indent=2))
+ print(dp.model_dump_json(indent=2))
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/critical_software.py b/src/ssvc/decision_points/critical_software.py
index c2bdead3..d9dad7d9 100644
--- a/src/ssvc/decision_points/critical_software.py
+++ b/src/ssvc/decision_points/critical_software.py
@@ -1,8 +1,9 @@
#!/usr/bin/env python
+
"""
Provides an SSVC decision point for critical software designation.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -16,6 +17,7 @@
# U.S. Patent and Trademark Office by Carnegie Mellon University
from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue
+from ssvc.decision_points.helpers import print_versions_and_diffs
YES = SsvcDecisionPointValue(
name="Yes",
@@ -40,9 +42,12 @@
),
)
+VERSIONS = (CRITICAL_SOFTWARE_1,)
+LATEST = VERSIONS[-1]
+
def main():
- print(CRITICAL_SOFTWARE_1.to_json(indent=2))
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/__init__.py b/src/ssvc/decision_points/cvss/__init__.py
index a9bc6dd9..0fd60f21 100644
--- a/src/ssvc/decision_points/cvss/__init__.py
+++ b/src/ssvc/decision_points/cvss/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
diff --git a/src/ssvc/decision_points/cvss/_not_defined.py b/src/ssvc/decision_points/cvss/_not_defined.py
index 96ff8f22..8581f2b1 100644
--- a/src/ssvc/decision_points/cvss/_not_defined.py
+++ b/src/ssvc/decision_points/cvss/_not_defined.py
@@ -2,7 +2,7 @@
"""
Provides a generic Not Define decision point value for CVSS decision points.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
diff --git a/src/ssvc/decision_points/cvss/attack_complexity.py b/src/ssvc/decision_points/cvss/attack_complexity.py
index b679d524..5fa68d59 100644
--- a/src/ssvc/decision_points/cvss/attack_complexity.py
+++ b/src/ssvc/decision_points/cvss/attack_complexity.py
@@ -2,7 +2,7 @@
"""
Models the CVSS Attack Complexity (formerly known as Access Complexity) metric as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -133,16 +133,17 @@
"""
-versions = [
+VERSIONS = (
ACCESS_COMPLEXITY_1,
ACCESS_COMPLEXITY_2,
ATTACK_COMPLEXITY_3,
ATTACK_COMPLEXITY_3_0_1,
-]
+)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/attack_requirements.py b/src/ssvc/decision_points/cvss/attack_requirements.py
index fd2348bd..8ae4ba7d 100644
--- a/src/ssvc/decision_points/cvss/attack_requirements.py
+++ b/src/ssvc/decision_points/cvss/attack_requirements.py
@@ -2,7 +2,7 @@
"""
CVSS Attack Requirements
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -47,13 +47,12 @@
),
)
-versions = [
- ATTACK_REQUIREMENTS_1,
-]
+VERSIONS = (ATTACK_REQUIREMENTS_1,)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/attack_vector.py b/src/ssvc/decision_points/cvss/attack_vector.py
index fba9ac29..f20a102d 100644
--- a/src/ssvc/decision_points/cvss/attack_vector.py
+++ b/src/ssvc/decision_points/cvss/attack_vector.py
@@ -2,7 +2,7 @@
"""
Models the CVSS Attack Vector (formerly known as Access Vector) metric as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -194,11 +194,17 @@
),
)
-versions = [ACCESS_VECTOR_1, ACCESS_VECTOR_2, ATTACK_VECTOR_3, ATTACK_VECTOR_3_0_1]
+VERSIONS = (
+ ACCESS_VECTOR_1,
+ ACCESS_VECTOR_2,
+ ATTACK_VECTOR_3,
+ ATTACK_VECTOR_3_0_1,
+)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/authentication.py b/src/ssvc/decision_points/cvss/authentication.py
index d4fa7fd7..516966f1 100644
--- a/src/ssvc/decision_points/cvss/authentication.py
+++ b/src/ssvc/decision_points/cvss/authentication.py
@@ -3,7 +3,7 @@
Models the CVSS Authentication metric as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -79,15 +79,12 @@
Includes MULTIPLE, SINGLE, and AUTH_NONE values for CVSS Authentication.
"""
-
-versions = [
- AUTHENTICATION_1,
- AUTHENTICATION_2,
-]
+VERSIONS = (AUTHENTICATION_1, AUTHENTICATION_2)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/availability_impact.py b/src/ssvc/decision_points/cvss/availability_impact.py
index 3312b80c..bc689915 100644
--- a/src/ssvc/decision_points/cvss/availability_impact.py
+++ b/src/ssvc/decision_points/cvss/availability_impact.py
@@ -3,7 +3,7 @@
Models the CVSS Availability Impact metric as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -114,12 +114,12 @@
)
-AVAILABILITY_IMPACT_2_0_1 = CvssDecisionPoint(
- name="Availability Impact",
+AVAILABILITY_IMPACT_3_0_0 = CvssDecisionPoint(
+ name="Availability Impact to the Vulnerable System",
description="This metric measures the impact to the availability of the impacted system resulting from a "
"successfully exploited vulnerability.",
- key="A",
- version="2.0.1",
+ key="VA",
+ version="3.0.0",
values=(
_NONE_3,
_LOW_2,
@@ -127,11 +127,17 @@
),
)
-versions = [AVAILABILITY_IMPACT_1, AVAILABILITY_IMPACT_2, AVAILABILITY_IMPACT_2_0_1]
+
+VERSIONS = (
+ AVAILABILITY_IMPACT_1,
+ AVAILABILITY_IMPACT_2,
+ AVAILABILITY_IMPACT_3_0_0,
+)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/availability_requirement.py b/src/ssvc/decision_points/cvss/availability_requirement.py
index 0e84929b..09cd2660 100644
--- a/src/ssvc/decision_points/cvss/availability_requirement.py
+++ b/src/ssvc/decision_points/cvss/availability_requirement.py
@@ -3,7 +3,7 @@
Models the CVSS Availability Requirement metric as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -17,7 +17,10 @@
# U.S. Patent and Trademark Office by Carnegie Mellon University
from ssvc.decision_points.base import SsvcDecisionPointValue
-from ssvc.decision_points.cvss._not_defined import NOT_DEFINED_ND, NOT_DEFINED_X
+from ssvc.decision_points.cvss._not_defined import (
+ NOT_DEFINED_ND,
+ NOT_DEFINED_X,
+)
from ssvc.decision_points.cvss.base import CvssDecisionPoint
from ssvc.decision_points.helpers import print_versions_and_diffs
@@ -109,15 +112,16 @@
),
)
-versions = [
+VERSIONS = (
AVAILABILITY_REQUIREMENT_1,
AVAILABILITY_REQUIREMENT_1_1,
AVAILABILITY_REQUIREMENT_1_1_1,
-]
+)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/base.py b/src/ssvc/decision_points/cvss/base.py
index 78aaf15f..9a935991 100644
--- a/src/ssvc/decision_points/cvss/base.py
+++ b/src/ssvc/decision_points/cvss/base.py
@@ -2,9 +2,7 @@
"""
Provides a base class for modeling CVSS vector metrics as SSVC decision points.
"""
-
-
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -17,16 +15,12 @@
# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
# U.S. Patent and Trademark Office by Carnegie Mellon University
-from dataclasses import dataclass
-
-from dataclasses_json import dataclass_json
+from pydantic import BaseModel
from ssvc.decision_points.base import SsvcDecisionPoint
-@dataclass_json
-@dataclass(kw_only=True)
-class CvssDecisionPoint(SsvcDecisionPoint):
+class CvssDecisionPoint(SsvcDecisionPoint, BaseModel):
"""
Models a single CVSS decision point as a list of values.
"""
diff --git a/src/ssvc/decision_points/cvss/collateral_damage_potential.py b/src/ssvc/decision_points/cvss/collateral_damage_potential.py
index 5d309fbe..3c541009 100644
--- a/src/ssvc/decision_points/cvss/collateral_damage_potential.py
+++ b/src/ssvc/decision_points/cvss/collateral_damage_potential.py
@@ -3,7 +3,7 @@
Models the CVSS Collateral Damage Potential metric as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -98,11 +98,12 @@
Updates None description. Adds Low-Medium, Medium-High, and Not Defined value.
"""
-versions = [COLLATERAL_DAMAGE_POTENTIAL_1, COLLATERAL_DAMAGE_POTENTIAL_2]
+VERSIONS = (COLLATERAL_DAMAGE_POTENTIAL_1, COLLATERAL_DAMAGE_POTENTIAL_2)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/confidentiality_impact.py b/src/ssvc/decision_points/cvss/confidentiality_impact.py
index 91e63ac1..c56abb6a 100644
--- a/src/ssvc/decision_points/cvss/confidentiality_impact.py
+++ b/src/ssvc/decision_points/cvss/confidentiality_impact.py
@@ -2,7 +2,7 @@
"""
Models the CVSS Confidentiality Impact metric as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -122,14 +122,14 @@
description="There is no loss of confidentiality within the impacted component.",
)
-CONFIDENTIALITY_IMPACT_2_0_1 = CvssDecisionPoint(
- name="Confidentiality Impact",
+CONFIDENTIALITY_IMPACT_3_0_0 = CvssDecisionPoint(
+ name="Confidentiality Impact to the Vulnerable System",
description="This metric measures the impact to the confidentiality of the information managed by the system due "
"to a successfully exploited vulnerability. Confidentiality refers to limiting information access "
"and disclosure to only authorized users, as well as preventing access by, or disclosure to, "
"unauthorized ones.",
- key="C",
- version="2.0.1",
+ key="VC",
+ version="3.0.0",
values=(
_CI_NONE_3,
_LOW_1,
@@ -138,15 +138,16 @@
)
-versions = [
+VERSIONS = (
CONFIDENTIALITY_IMPACT_1,
CONFIDENTIALITY_IMPACT_2,
- CONFIDENTIALITY_IMPACT_2_0_1,
-]
+ CONFIDENTIALITY_IMPACT_3_0_0,
+)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/confidentiality_requirement.py b/src/ssvc/decision_points/cvss/confidentiality_requirement.py
index 427cf6b3..288c05c8 100644
--- a/src/ssvc/decision_points/cvss/confidentiality_requirement.py
+++ b/src/ssvc/decision_points/cvss/confidentiality_requirement.py
@@ -3,7 +3,7 @@
Models the CVSS Confidentiality Requirement metric as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -17,7 +17,10 @@
# U.S. Patent and Trademark Office by Carnegie Mellon University
from ssvc.decision_points.base import SsvcDecisionPointValue
-from ssvc.decision_points.cvss._not_defined import NOT_DEFINED_ND, NOT_DEFINED_X
+from ssvc.decision_points.cvss._not_defined import (
+ NOT_DEFINED_ND,
+ NOT_DEFINED_X,
+)
from ssvc.decision_points.cvss.base import CvssDecisionPoint
from ssvc.decision_points.helpers import print_versions_and_diffs
@@ -107,15 +110,16 @@
),
)
-versions = [
+VERSIONS = (
CONFIDENTIALITY_REQUIREMENT_1,
CONFIDENTIALITY_REQUIREMENT_1_1,
CONFIDENTIALITY_REQUIREMENT_1_1_1,
-]
+)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/eq_sets.py b/src/ssvc/decision_points/cvss/eq_sets.py
deleted file mode 100644
index aca56dde..00000000
--- a/src/ssvc/decision_points/cvss/eq_sets.py
+++ /dev/null
@@ -1,191 +0,0 @@
-#!/usr/bin/env python
-"""
-CVSS v4 Equivalence Sets
-"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
-# - see Contributors.md for a full list of Contributors
-# - see ContributionInstructions.md for information on how you can Contribute to this project
-# Stakeholder Specific Vulnerability Categorization (SSVC) is
-# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
-# with this Software or contact permission@sei.cmu.edu for full terms.
-# Created, in part, with funding and support from the United States Government
-# (see Acknowledgments file). This program may include and/or can make use of
-# certain third party source code, object code, documentation and other files
-# (“Third Party Software”). See LICENSE.md for more details.
-# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
-# U.S. Patent and Trademark Office by Carnegie Mellon University
-
-from ssvc.decision_points import SsvcDecisionPointValue
-from ssvc.decision_points.cvss.base import CvssDecisionPoint
-
-
-# EQ1 → AV/PR/UI with 3 levels specified in Table 24
-# Levels Constraints Highest Severity Vector(s)
-# 0 AV:N and PR:N and UI:N AV:N/PR:N/UI:N
-# 1 (AV:N or PR:N or UI:N) and not (AV:N and PR:N and UI:N) and not AV:P AV:A/PR:N/UI:N or AV:N/PR:L/UI:N or AV:N/PR:N:/UI:P
-# 2 AV:P or not(AV:N or PR:N or UI:N) AV:P/PR:N/UI:N or AV:A/PR:L/UI:P
-EQ1 = CvssDecisionPoint(
- name="Equivalence Set 1",
- key="EQ1",
- description="AV/PR/UI with 3 levels specified in Table 24",
- version="1.0.0",
- values=[
- SsvcDecisionPointValue(
- name="Low",
- key="L",
- description="2: AV:P or not(AV:N or PR:N or UI:N)",
- ),
- SsvcDecisionPointValue(
- name="Medium",
- key="M",
- description="1: (AV:N or PR:N or UI:N) and not (AV:N and PR:N and UI:N) and not AV:P",
- ),
- SsvcDecisionPointValue(
- name="High",
- key="H",
- description="0: AV:N and PR:N and UI:N",
- ),
- ],
-)
-
-# EQ2 → AC/AT with 2 levels specified in Table 25
-# Levels Constraints Highest Severity Vector(s)
-# 0 AC:L and AT:N AC:L/AT:N
-# 1 not (AC:L and AT:N) AC:L/AT:P or AC:H/AT:N
-EQ2 = CvssDecisionPoint(
- name="Equivalence Set 2",
- key="EQ2",
- description="AC/AT with 2 levels specified in Table 25",
- version="1.0.0",
- values=[
- SsvcDecisionPointValue(
- name="Low",
- key="L",
- description="1: not (AC:L and AT:N)",
- ),
- SsvcDecisionPointValue(
- name="High",
- key="H",
- description="0: AC:L and AT:N",
- ),
- ],
-)
-
-
-# EQ3 → VC/VI/VA with 3 levels specified in Table 26
-# Levels Constraints Highest Severity Vector(s)
-# 0 VC:H and VI:H VC:H/VI:H/VA:H
-# 1 not (VC:H and VI:H) and (VC:H or VI:H or VA:H) VC:L/VI:H/VA:H or VC:H/VI:L/VA:H
-# 2 not (VC:H or VI:H or VA:H) VC:L/VI:L/VA:L
-EQ3 = CvssDecisionPoint(
- name="Equivalence Set 3",
- key="EQ3",
- description="VC/VI/VA with 3 levels specified in Table 26",
- version="1.0.0",
- values=[
- SsvcDecisionPointValue(
- name="Low",
- key="L",
- description="2: not (VC:H or VI:H or VA:H)",
- ),
- SsvcDecisionPointValue(
- name="Medium",
- key="M",
- description="1: not (VC:H and VI:H) and (VC:H or VI:H or VA:H)",
- ),
- SsvcDecisionPointValue(
- name="High",
- key="H",
- description="0: VC:H and VI:H",
- ),
- ],
-)
-
-
-# EQ4 → SC/SI/SA with 3 levels specified in Table 27
-# 0 MSI:S or MSA:S SC:H/SI:S/SA:S
-# 1 not (MSI:S or MSA:S) and (SC:H or SI:H or SA:H) SC:H/SI:H/SA:H
-# 2 not (MSI:S or MSA:S) and not (SC:H or SI:H or SA:H) SC:L/SI:L/SA:L
-EQ4 = CvssDecisionPoint(
- name="Equivalence Set 4",
- key="EQ4",
- description="SC/SI/SA with 3 levels specified in Table 27",
- version="1.0.0",
- values=[
- SsvcDecisionPointValue(
- name="Low",
- key="L",
- description="2: not (MSI:S or MSA:S) and not (SC:H or SI:H or SA:H)",
- ),
- SsvcDecisionPointValue(
- name="Medium",
- key="M",
- description="1: not (MSI:S or MSA:S) and (SC:H or SI:H or SA:H)",
- ),
- SsvcDecisionPointValue(
- name="High",
- key="H",
- description="0: MSI:S or MSA:S",
- ),
- ],
-)
-
-
-# EQ5 → E with 3 levels specified in Table 28
-# 0 E:A E:A
-# 1 E:P E:P
-# 2 E:U E:U
-EQ5 = CvssDecisionPoint(
- name="Equivalence Set 5",
- key="EQ5",
- description="E with 3 levels specified in Table 28",
- version="1.0.0",
- values=[
- SsvcDecisionPointValue(
- name="Low",
- key="L",
- description="2: E:U",
- ),
- SsvcDecisionPointValue(
- name="Medium",
- key="M",
- description="1: E:P",
- ),
- SsvcDecisionPointValue(
- name="High",
- key="H",
- description="0: E:A",
- ),
- ],
-)
-
-# EQ6 → VC/VI/VA+CR/CI/CA with 2 levels specified in Table 29
-# 0 (CR:H and VC:H) or (IR:H and VI:H) or (AR:H and VA:H) VC:H/VI:H/VA:H/CR:H/IR:H/AR:H
-# 1 not (CR:H and VC:H) and not (IR:H and VI:H) and not (AR:H and VA:H) VC:H/VI:H/VA:H/CR:M/IR:M/AR:M or VC:H/VI:H/VA:L/CR:M/IR:M/AR:H or VC:H/VI:L/VA:H/CR:M/IR:H/AR:M or VC:H/VI:L/VA:L/CR:M/IR:H/AR:H or VC:L/VI:H/VA:H/CR:H/IR:M/AR:M or VC:L/VI:H/VA:L/CR:H/IR:M/AR:H or VC:L/VI:L/VA:H/CR:H/IR:H/AR:M or VC:L/VI:L/VA:L/CR:H/IR:H/AR:H
-EQ6 = CvssDecisionPoint(
- name="Equivalence Set 6",
- key="EQ6",
- description="VC/VI/VA+CR/CI/CA with 2 levels specified in Table 29",
- version="1.0.0",
- values=[
- SsvcDecisionPointValue(
- name="Low",
- key="L",
- description="1: not (CR:H and VC:H) and not (IR:H and VI:H) and not (AR:H and VA:H)",
- ),
- SsvcDecisionPointValue(
- name="High",
- key="H",
- description="0: (CR:H and VC:H) or (IR:H and VI:H) or (AR:H and VA:H)",
- ),
- ],
-)
-
-
-def main():
- for dp in [EQ1, EQ2, EQ3, EQ4, EQ5, EQ6]:
- print(dp.to_json(indent=2))
-
-
-if __name__ == "__main__":
- main()
diff --git a/src/ssvc/decision_points/cvss/equivalence_set_1.py b/src/ssvc/decision_points/cvss/equivalence_set_1.py
new file mode 100644
index 00000000..6e832210
--- /dev/null
+++ b/src/ssvc/decision_points/cvss/equivalence_set_1.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+"""
+Provides an object representing the CVSS Equivalence Set 1 as a decision point.
+"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
+from ssvc.decision_points import SsvcDecisionPointValue
+from ssvc.decision_points.cvss.base import CvssDecisionPoint
+from ssvc.decision_points.helpers import print_versions_and_diffs
+
+TWO = SsvcDecisionPointValue(
+ name="Low",
+ key="L",
+ description="2: AV:P or not(AV:N or PR:N or UI:N)",
+)
+
+ONE = SsvcDecisionPointValue(
+ name="Medium",
+ key="M",
+ description="1: (AV:N or PR:N or UI:N) and not (AV:N and PR:N and UI:N) and not AV:P",
+)
+
+ZERO = SsvcDecisionPointValue(
+ name="High",
+ key="H",
+ description="0: AV:N and PR:N and UI:N",
+)
+
+# EQ1 → AV/PR/UI with 3 levels specified in Table 24
+# Levels Constraints Highest Severity Vector(s)
+# 0 AV:N and PR:N and UI:N AV:N/PR:N/UI:N
+# 1 (AV:N or PR:N or UI:N) and not (AV:N and PR:N and UI:N) and not AV:P AV:A/PR:N/UI:N or AV:N/PR:L/UI:N or AV:N/PR:N:/UI:P
+# 2 AV:P or not(AV:N or PR:N or UI:N) AV:P/PR:N/UI:N or AV:A/PR:L/UI:P
+EQ1 = CvssDecisionPoint(
+ name="Equivalence Set 1",
+ key="EQ1",
+ description="AV/PR/UI with 3 levels specified in Table 24",
+ version="1.0.0",
+ values=(
+ TWO,
+ ONE,
+ ZERO,
+ ),
+)
+
+VERSIONS = (EQ1,)
+LATEST = EQ1
+
+
+def main():
+ print_versions_and_diffs(VERSIONS)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/ssvc/decision_points/cvss/equivalence_set_2.py b/src/ssvc/decision_points/cvss/equivalence_set_2.py
new file mode 100644
index 00000000..b20e1cd1
--- /dev/null
+++ b/src/ssvc/decision_points/cvss/equivalence_set_2.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+"""
+This module provides an object representing the CVSS Equivalence Set 2 as a decision point.
+"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
+from ssvc.decision_points import SsvcDecisionPointValue
+from ssvc.decision_points.cvss.base import CvssDecisionPoint
+from ssvc.decision_points.helpers import print_versions_and_diffs
+
+# EQ2 → AC/AT with 2 levels specified in Table 25
+# Levels Constraints Highest Severity Vector(s)
+# 0 AC:L and AT:N AC:L/AT:N
+# 1 not (AC:L and AT:N) AC:L/AT:P or AC:H/AT:N
+ONE = SsvcDecisionPointValue(
+ name="Low",
+ key="L",
+ description="1: not (AC:L and AT:N)",
+)
+ZERO = SsvcDecisionPointValue(
+ name="High",
+ key="H",
+ description="0: AC:L and AT:N",
+)
+
+EQ2 = CvssDecisionPoint(
+ name="Equivalence Set 2",
+ key="EQ2",
+ description="AC/AT with 2 levels specified in Table 25",
+ version="1.0.0",
+ values=(
+ ONE,
+ ZERO,
+ ),
+)
+
+VERSIONS = (EQ2,)
+LATEST = VERSIONS[-1]
+
+
+def main():
+ print_versions_and_diffs(VERSIONS)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/ssvc/decision_points/cvss/equivalence_set_3.py b/src/ssvc/decision_points/cvss/equivalence_set_3.py
new file mode 100644
index 00000000..5d551b39
--- /dev/null
+++ b/src/ssvc/decision_points/cvss/equivalence_set_3.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+"""
+This module provides an object representing the CVSS Equivalence Set 3 as a decision point.
+"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
+from ssvc.decision_points import SsvcDecisionPointValue
+from ssvc.decision_points.cvss.base import CvssDecisionPoint
+from ssvc.decision_points.helpers import print_versions_and_diffs
+
+# EQ3 → VC/VI/VA with 3 levels specified in Table 26
+# Levels Constraints Highest Severity Vector(s)
+# 0 VC:H and VI:H VC:H/VI:H/VA:H
+# 1 not (VC:H and VI:H) and (VC:H or VI:H or VA:H) VC:L/VI:H/VA:H or VC:H/VI:L/VA:H
+# 2 not (VC:H or VI:H or VA:H) VC:L/VI:L/VA:L
+TWO = SsvcDecisionPointValue(
+ name="Low",
+ key="L",
+ description="2: not (VC:H or VI:H or VA:H)",
+)
+ONE = SsvcDecisionPointValue(
+ name="Medium",
+ key="M",
+ description="1: not (VC:H and VI:H) and (VC:H or VI:H or VA:H)",
+)
+ZERO = SsvcDecisionPointValue(
+ name="High",
+ key="H",
+ description="0: VC:H and VI:H",
+)
+
+EQ3 = CvssDecisionPoint(
+ name="Equivalence Set 3",
+ key="EQ3",
+ description="VC/VI/VA with 3 levels specified in Table 26",
+ version="1.0.0",
+ values=(
+ TWO,
+ ONE,
+ ZERO,
+ ),
+)
+
+
+VERSIONS = (EQ3,)
+LATEST = VERSIONS[-1]
+
+
+def main():
+ print_versions_and_diffs(VERSIONS)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/ssvc/decision_points/cvss/equivalence_set_4.py b/src/ssvc/decision_points/cvss/equivalence_set_4.py
new file mode 100644
index 00000000..c7caf550
--- /dev/null
+++ b/src/ssvc/decision_points/cvss/equivalence_set_4.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+"""
+This module provides an object representing the CVSS Equivalence Set 4 as a decision point.
+"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
+from ssvc.decision_points import SsvcDecisionPointValue
+from ssvc.decision_points.cvss.base import CvssDecisionPoint
+from ssvc.decision_points.helpers import print_versions_and_diffs
+
+# EQ4 → SC/SI/SA with 3 levels specified in Table 27
+# 0 MSI:S or MSA:S SC:H/SI:S/SA:S
+# 1 not (MSI:S or MSA:S) and (SC:H or SI:H or SA:H) SC:H/SI:H/SA:H
+# 2 not (MSI:S or MSA:S) and not (SC:H or SI:H or SA:H) SC:L/SI:L/SA:L
+TWO = SsvcDecisionPointValue(
+ name="Low",
+ key="L",
+ description="2: not (MSI:S or MSA:S) and not (SC:H or SI:H or SA:H)",
+)
+ONE = SsvcDecisionPointValue(
+ name="Medium",
+ key="M",
+ description="1: not (MSI:S or MSA:S) and (SC:H or SI:H or SA:H)",
+)
+ZERO = SsvcDecisionPointValue(
+ name="High",
+ key="H",
+ description="0: MSI:S or MSA:S",
+)
+EQ4 = CvssDecisionPoint(
+ name="Equivalence Set 4",
+ key="EQ4",
+ description="SC/SI/SA with 3 levels specified in Table 27",
+ version="1.0.0",
+ values=(
+ TWO,
+ ONE,
+ ZERO,
+ ),
+)
+
+VERSIONS = (EQ4,)
+LATEST = VERSIONS[-1]
+
+
+def main():
+ print_versions_and_diffs(VERSIONS)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/ssvc/decision_points/cvss/equivalence_set_5.py b/src/ssvc/decision_points/cvss/equivalence_set_5.py
new file mode 100644
index 00000000..30e3fdba
--- /dev/null
+++ b/src/ssvc/decision_points/cvss/equivalence_set_5.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+"""
+This module provides an object representing the CVSS Equivalence Set 5 as a decision point.
+"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
+from ssvc.decision_points import SsvcDecisionPointValue
+from ssvc.decision_points.cvss.base import CvssDecisionPoint
+from ssvc.decision_points.helpers import print_versions_and_diffs
+
+# EQ5 → E with 3 levels specified in Table 28
+# 0 E:A E:A
+# 1 E:P E:P
+# 2 E:U E:U
+TWO = SsvcDecisionPointValue(name="Low", key="L", description="2: E:U", )
+ONE = SsvcDecisionPointValue(name="Medium", key="M", description="1: E:P", )
+ZERO = SsvcDecisionPointValue(name="High", key="H", description="0: E:A", )
+EQ5 = CvssDecisionPoint(
+ name="Equivalence Set 5",
+ key="EQ5",
+ description="E with 3 levels specified in Table 28",
+ version="1.0.0",
+ values=(
+ TWO,
+ ONE,
+ ZERO,
+),
+)
+
+
+VERSIONS = (EQ5,)
+LATEST = VERSIONS[-1]
+
+def main():
+ print_versions_and_diffs(VERSIONS)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/src/ssvc/decision_points/cvss/equivalence_set_6.py b/src/ssvc/decision_points/cvss/equivalence_set_6.py
new file mode 100644
index 00000000..4b4887c8
--- /dev/null
+++ b/src/ssvc/decision_points/cvss/equivalence_set_6.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+"""
+This module provides an object representing the CVSS Equivalence Set 6 as a decision point.
+"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
+from ssvc.decision_points import SsvcDecisionPointValue
+from ssvc.decision_points.cvss.base import CvssDecisionPoint
+from ssvc.decision_points.helpers import print_versions_and_diffs
+
+# EQ6 → VC/VI/VA+CR/CI/CA with 2 levels specified in Table 29
+# 0 (CR:H and VC:H) or (IR:H and VI:H) or (AR:H and VA:H) VC:H/VI:H/VA:H/CR:H/IR:H/AR:H
+# 1 not (CR:H and VC:H) and not (IR:H and VI:H) and not (AR:H and VA:H) VC:H/VI:H/VA:H/CR:M/IR:M/AR:M or VC:H/VI:H/VA:L/CR:M/IR:M/AR:H or VC:H/VI:L/VA:H/CR:M/IR:H/AR:M or VC:H/VI:L/VA:L/CR:M/IR:H/AR:H or VC:L/VI:H/VA:H/CR:H/IR:M/AR:M or VC:L/VI:H/VA:L/CR:H/IR:M/AR:H or VC:L/VI:L/VA:H/CR:H/IR:H/AR:M or VC:L/VI:L/VA:L/CR:H/IR:H/AR:H
+ONE = SsvcDecisionPointValue(name="Low", key="L",
+ description="1: not (CR:H and VC:H) and not (IR:H and VI:H) and not (AR:H and VA:H)", )
+ZERO = SsvcDecisionPointValue(name="High", key="H",
+ description="0: (CR:H and VC:H) or (IR:H and VI:H) or (AR:H and VA:H)", )
+EQ6 = CvssDecisionPoint(
+ name="Equivalence Set 6",
+ key="EQ6",
+ description="VC/VI/VA+CR/CI/CA with 2 levels specified in Table 29",
+ version="1.0.0",
+ values=(
+ ONE,
+ ZERO,
+ ),
+)
+
+VERSIONS = (EQ6,)
+LATEST = VERSIONS[-1]
+
+def main():
+ print_versions_and_diffs(VERSIONS)
+
+
+if __name__ == '__main__':
+ main()
+
diff --git a/src/ssvc/decision_points/cvss/exploitability.py b/src/ssvc/decision_points/cvss/exploit_maturity.py
similarity index 96%
rename from src/ssvc/decision_points/cvss/exploitability.py
rename to src/ssvc/decision_points/cvss/exploit_maturity.py
index 06815614..2a3eee2a 100644
--- a/src/ssvc/decision_points/cvss/exploitability.py
+++ b/src/ssvc/decision_points/cvss/exploit_maturity.py
@@ -3,7 +3,7 @@
Model the CVSS Exploitability and Exploit Code Maturity metrics as SSVC decision points.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -17,7 +17,10 @@
# U.S. Patent and Trademark Office by Carnegie Mellon University
from ssvc.decision_points.base import SsvcDecisionPointValue
-from ssvc.decision_points.cvss._not_defined import NOT_DEFINED_ND, NOT_DEFINED_X
+from ssvc.decision_points.cvss._not_defined import (
+ NOT_DEFINED_ND,
+ NOT_DEFINED_X,
+)
from ssvc.decision_points.cvss.base import CvssDecisionPoint
from ssvc.decision_points.helpers import print_versions_and_diffs
@@ -177,16 +180,17 @@
),
)
-versions = [
+VERSIONS = (
EXPLOITABILITY_1,
EXPLOITABILITY_1_1,
EXPLOIT_CODE_MATURITY_1_2,
EXPLOIT_MATURITY_2,
-]
+)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/helpers.py b/src/ssvc/decision_points/cvss/helpers.py
index 6e2e4ba9..25782192 100644
--- a/src/ssvc/decision_points/cvss/helpers.py
+++ b/src/ssvc/decision_points/cvss/helpers.py
@@ -2,7 +2,7 @@
"""
Provides helpers for working with CVSS decision points.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -34,7 +34,7 @@ def _modify_3(dp: SsvcDecisionPoint):
names = [v.name for v in values]
if nd.name not in names:
values.append(nd)
- _dp.values = tuple(values)
+ _dp.values = list(values)
return _dp
@@ -98,7 +98,7 @@ def _modify_4(dp: SsvcDecisionPoint):
)
values = list(_dp.values)
values.append(_SAFETY)
- _dp.values = tuple(values)
+ _dp.values = list(values)
return _dp
diff --git a/src/ssvc/decision_points/cvss/impact_bias.py b/src/ssvc/decision_points/cvss/impact_bias.py
index 744e3576..ad113083 100644
--- a/src/ssvc/decision_points/cvss/impact_bias.py
+++ b/src/ssvc/decision_points/cvss/impact_bias.py
@@ -2,7 +2,7 @@
"""
Model the CVSS Impact Bias as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -59,13 +59,12 @@
Defines Normal, Confidentiality, Integrity, and Availability values for CVSS Impact Bias.
"""
-versions = [
- IMPACT_BIAS_1,
-]
+VERSIONS = (IMPACT_BIAS_1,)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/integrity_impact.py b/src/ssvc/decision_points/cvss/integrity_impact.py
index 1a78dcd3..6cc2584c 100644
--- a/src/ssvc/decision_points/cvss/integrity_impact.py
+++ b/src/ssvc/decision_points/cvss/integrity_impact.py
@@ -3,7 +3,7 @@
Models the CVSS Integrity Impact metric as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -113,11 +113,11 @@
)
-INTEGRITY_IMPACT_2_0_1 = CvssDecisionPoint(
- name="Integrity Impact",
+INTEGRITY_IMPACT_3_0_0 = CvssDecisionPoint(
+ name="Integrity Impact to the Vulnerable System",
description="This metric measures the impact to integrity of a successfully exploited vulnerability.",
- key="I",
- version="2.0.1",
+ key="VI",
+ version="3.0.0",
values=(
_II_NONE_3,
_II_LOW_2,
@@ -125,11 +125,13 @@
),
)
-versions = [INTEGRITY_IMPACT_1, INTEGRITY_IMPACT_2, INTEGRITY_IMPACT_2_0_1]
+
+VERSIONS = (INTEGRITY_IMPACT_1, INTEGRITY_IMPACT_2, INTEGRITY_IMPACT_3_0_0)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/integrity_requirement.py b/src/ssvc/decision_points/cvss/integrity_requirement.py
index a5c5eba5..fed364a6 100644
--- a/src/ssvc/decision_points/cvss/integrity_requirement.py
+++ b/src/ssvc/decision_points/cvss/integrity_requirement.py
@@ -3,7 +3,7 @@
Models the CVSS Integrity Requirement metric as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -17,7 +17,10 @@
# U.S. Patent and Trademark Office by Carnegie Mellon University
from ssvc.decision_points.base import SsvcDecisionPointValue
-from ssvc.decision_points.cvss._not_defined import NOT_DEFINED_ND, NOT_DEFINED_X
+from ssvc.decision_points.cvss._not_defined import (
+ NOT_DEFINED_ND,
+ NOT_DEFINED_X,
+)
from ssvc.decision_points.cvss.base import CvssDecisionPoint
from ssvc.decision_points.helpers import print_versions_and_diffs
@@ -98,7 +101,7 @@
description="This metric enables the consumer to customize the assessment depending on the importance of the "
"affected IT asset to the analyst’s organization, measured in terms of Confidentiality.",
key="IR",
- version="1.0.1",
+ version="1.1.1",
values=(
_LOW_2,
_MEDIUM_2,
@@ -107,15 +110,16 @@
),
)
-versions = [
+VERSIONS = (
INTEGRITY_REQUIREMENT_1,
INTEGRITY_REQUIREMENT_1_1,
INTEGRITY_REQUIREMENT_1_1_1,
-]
+)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/privileges_required.py b/src/ssvc/decision_points/cvss/privileges_required.py
index 62313e28..e9cb0ea5 100644
--- a/src/ssvc/decision_points/cvss/privileges_required.py
+++ b/src/ssvc/decision_points/cvss/privileges_required.py
@@ -2,7 +2,7 @@
"""
Models the CVSS Privileges Required metric as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -101,11 +101,12 @@
),
)
-versions = [PRIVILEGES_REQUIRED_1, PRIVILEGES_REQUIRED_1_0_1]
+VERSIONS = (PRIVILEGES_REQUIRED_1, PRIVILEGES_REQUIRED_1_0_1)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/qualitative_severity.py b/src/ssvc/decision_points/cvss/qualitative_severity.py
new file mode 100644
index 00000000..23358fe7
--- /dev/null
+++ b/src/ssvc/decision_points/cvss/qualitative_severity.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+"""
+Provides a decision point for the [CVSS Qualitative Severity Rating Scale](https://www.first.org/cvss/v4.0/specification-document#Qualitative-Severity-Rating-Scale).
+"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
+from ssvc.decision_points import SsvcDecisionPointValue
+from ssvc.decision_points.cvss.base import CvssDecisionPoint
+from ssvc.decision_points.helpers import print_versions_and_diffs
+
+QS_NONE = SsvcDecisionPointValue(
+ name="None",
+ key="N",
+ description="No severity rating (0.0)",
+)
+
+LOW = SsvcDecisionPointValue(
+ name="Low",
+ key="L",
+ description="Low (0.1 - 3.9)",
+)
+MEDIUM = SsvcDecisionPointValue(
+ name="Medium",
+ key="M",
+ description="Medium (4.0 - 6.9)",
+)
+HIGH = SsvcDecisionPointValue(
+ name="High",
+ key="H",
+ description="High (7.0 - 8.9)",
+)
+CRITICAL = SsvcDecisionPointValue(
+ name="Critical",
+ key="C",
+ description="Critical (9.0 - 10.0)",
+)
+
+QUALITATIVE_SEVERITY = CvssDecisionPoint(
+ name="CVSS Qualitative Severity Rating Scale",
+ key="QS",
+ description="The CVSS Qualitative Severity Rating Scale provides "
+ "a categorical representation of a CVSS Score.",
+ version="1.0.0",
+ values=(
+ QS_NONE,
+ LOW,
+ MEDIUM,
+ HIGH,
+ CRITICAL,
+ ),
+)
+
+VERSIONS = (QUALITATIVE_SEVERITY,)
+LATEST = VERSIONS[-1]
+
+
+def main():
+ print_versions_and_diffs(VERSIONS)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/ssvc/decision_points/cvss/remediation_level.py b/src/ssvc/decision_points/cvss/remediation_level.py
index adc7a5a1..73a031ef 100644
--- a/src/ssvc/decision_points/cvss/remediation_level.py
+++ b/src/ssvc/decision_points/cvss/remediation_level.py
@@ -3,7 +3,7 @@
Models the CVSS Remediation Level metric as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -84,11 +84,12 @@
Adds Not Defined to the CVSS Remediation Level decision point.
"""
-versions = [REMEDIATION_LEVEL_1, REMEDIATION_LEVEL_1_1]
+VERSIONS = (REMEDIATION_LEVEL_1, REMEDIATION_LEVEL_1_1)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/report_confidence.py b/src/ssvc/decision_points/cvss/report_confidence.py
index 7c87f076..93ea24fc 100644
--- a/src/ssvc/decision_points/cvss/report_confidence.py
+++ b/src/ssvc/decision_points/cvss/report_confidence.py
@@ -3,7 +3,7 @@
Models the CVSS Report Confidence metric as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -17,7 +17,10 @@
# U.S. Patent and Trademark Office by Carnegie Mellon University
from ssvc.decision_points.base import SsvcDecisionPointValue
-from ssvc.decision_points.cvss._not_defined import NOT_DEFINED_ND, NOT_DEFINED_X
+from ssvc.decision_points.cvss._not_defined import (
+ NOT_DEFINED_ND,
+ NOT_DEFINED_X,
+)
from ssvc.decision_points.cvss.base import CvssDecisionPoint
from ssvc.decision_points.helpers import print_versions_and_diffs
@@ -123,15 +126,16 @@
"""
-versions = [
+VERSIONS = (
REPORT_CONFIDENCE_1,
REPORT_CONFIDENCE_1_1,
REPORT_CONFIDENCE_2,
-]
+)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/scope.py b/src/ssvc/decision_points/cvss/scope.py
index b6cf95a8..9eaf0b35 100644
--- a/src/ssvc/decision_points/cvss/scope.py
+++ b/src/ssvc/decision_points/cvss/scope.py
@@ -3,7 +3,7 @@
Models CVSS Scope as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -49,13 +49,12 @@
Defines Changed and Unchanged values for CVSS Scope.
"""
-versions = [
- SCOPE_1,
-]
+VERSIONS = (SCOPE_1,)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/subsequent_availability_impact.py b/src/ssvc/decision_points/cvss/subsequent_availability_impact.py
index 927e9326..1a4cf449 100644
--- a/src/ssvc/decision_points/cvss/subsequent_availability_impact.py
+++ b/src/ssvc/decision_points/cvss/subsequent_availability_impact.py
@@ -2,7 +2,7 @@
"""
CVSS Subsequent system availability impact decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -44,7 +44,7 @@
SUBSEQUENT_AVAILABILITY_IMPACT_1 = CvssDecisionPoint(
- name="Subsequent Availability Impact",
+ name="Availability Impact to the Subsequent System",
description="This metric measures the impact on availability a successful exploit of the vulnerability will have "
"on the Subsequent System.",
key="SA",
@@ -56,13 +56,12 @@
),
)
-versions = [
- SUBSEQUENT_AVAILABILITY_IMPACT_1,
-]
+VERSIONS = (SUBSEQUENT_AVAILABILITY_IMPACT_1,)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/subsequent_confidentiality_impact.py b/src/ssvc/decision_points/cvss/subsequent_confidentiality_impact.py
index a8bd1fc4..413dc803 100644
--- a/src/ssvc/decision_points/cvss/subsequent_confidentiality_impact.py
+++ b/src/ssvc/decision_points/cvss/subsequent_confidentiality_impact.py
@@ -2,7 +2,7 @@
"""
CVSS Subsequent System Confidentiality Impact
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -57,13 +57,12 @@
),
)
-versions = [
- SUBSEQUENT_CONFIDENTIALITY_IMPACT_1,
-]
+VERSIONS = (SUBSEQUENT_CONFIDENTIALITY_IMPACT_1,)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/subsequent_integrity_impact.py b/src/ssvc/decision_points/cvss/subsequent_integrity_impact.py
index 3d885aa2..4a2efbf5 100644
--- a/src/ssvc/decision_points/cvss/subsequent_integrity_impact.py
+++ b/src/ssvc/decision_points/cvss/subsequent_integrity_impact.py
@@ -2,7 +2,7 @@
"""
CVSS Subsequent System Integrity Impact Decision Point
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -60,13 +60,12 @@
),
)
-versions = [
- SUBSEQUENT_INTEGRITY_IMPACT_1,
-]
+VERSIONS = (SUBSEQUENT_INTEGRITY_IMPACT_1,)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/supplemental/__init__.py b/src/ssvc/decision_points/cvss/supplemental/__init__.py
index 1c16ef64..b281a4ee 100644
--- a/src/ssvc/decision_points/cvss/supplemental/__init__.py
+++ b/src/ssvc/decision_points/cvss/supplemental/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
diff --git a/src/ssvc/decision_points/cvss/supplemental/automatable.py b/src/ssvc/decision_points/cvss/supplemental/automatable.py
index aef2ae4f..38c1ffe2 100644
--- a/src/ssvc/decision_points/cvss/supplemental/automatable.py
+++ b/src/ssvc/decision_points/cvss/supplemental/automatable.py
@@ -2,7 +2,7 @@
"""
Provides the CVSS supplemental metric Automatable
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -16,10 +16,23 @@
# U.S. Patent and Trademark Office by Carnegie Mellon University
from ssvc.decision_points import SsvcDecisionPointValue
+from ssvc.decision_points.cvss._not_defined import NOT_DEFINED_X
from ssvc.decision_points.cvss.base import CvssDecisionPoint
from ssvc.decision_points.helpers import print_versions_and_diffs
-
+NO = SsvcDecisionPointValue(
+ name="No",
+ key="N",
+ description="Attackers cannot reliably automate all 4 steps of the kill chain for this vulnerability for "
+ "some reason. These steps are reconnaissance, weaponization, delivery, and exploitation.",
+)
+YES = SsvcDecisionPointValue(
+ name="Yes",
+ key="Y",
+ description="Attackers can reliably automate all 4 steps of the kill chain. These steps are "
+ "reconnaissance, weaponization, delivery, and exploitation (e.g., the vulnerability is "
+ '"wormable").',
+)
AUTOMATABLE_1 = CvssDecisionPoint(
name="Automatable",
description='The "Automatable" metric captures the answer to the question "Can an attacker automate exploitation '
@@ -27,29 +40,18 @@
key="AU",
version="1.0.0",
values=(
- SsvcDecisionPointValue(
- name="No",
- key="N",
- description="Attackers cannot reliably automate all 4 steps of the kill chain for this vulnerability for "
- "some reason. These steps are reconnaissance, weaponization, delivery, and exploitation.",
- ),
- SsvcDecisionPointValue(
- name="Yes",
- key="Y",
- description="Attackers can reliably automate all 4 steps of the kill chain. These steps are "
- "reconnaissance, weaponization, delivery, and exploitation (e.g., the vulnerability is "
- '"wormable").',
- ),
+ NO,
+ YES,
+ NOT_DEFINED_X,
),
)
+VERSIONS = (AUTOMATABLE_1,)
+LATEST = AUTOMATABLE_1
-def main():
- versions = [
- AUTOMATABLE_1,
- ]
- print_versions_and_diffs(versions)
+def main():
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/supplemental/provider_urgency.py b/src/ssvc/decision_points/cvss/supplemental/provider_urgency.py
index 3f0176a6..4aaeb452 100644
--- a/src/ssvc/decision_points/cvss/supplemental/provider_urgency.py
+++ b/src/ssvc/decision_points/cvss/supplemental/provider_urgency.py
@@ -2,7 +2,7 @@
"""
Provides the CVSS supplemental metric Provider Urgency as a SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -20,6 +20,26 @@
from ssvc.decision_points.cvss.base import CvssDecisionPoint
from ssvc.decision_points.helpers import print_versions_and_diffs
+RED = SsvcDecisionPointValue(
+ name="Red",
+ key="R",
+ description="Provider has assessed the impact of this vulnerability as having the highest urgency.",
+)
+AMBER = SsvcDecisionPointValue(
+ name="Amber",
+ key="A",
+ description="Provider has assessed the impact of this vulnerability as having a moderate urgency.",
+)
+GREEN = SsvcDecisionPointValue(
+ name="Green",
+ key="G",
+ description="Provider has assessed the impact of this vulnerability as having a reduced urgency.",
+)
+CLEAR = SsvcDecisionPointValue(
+ name="Clear",
+ key="C",
+ description="Provider has assessed the impact of this vulnerability as having no urgency (Informational).",
+)
PROVIDER_URGENCY_1 = CvssDecisionPoint(
name="Provider Urgency",
description="Many vendors currently provide supplemental severity ratings to consumers via product security "
@@ -30,37 +50,19 @@
version="1.0.0",
values=(
NOT_DEFINED_X,
- # Red, Amber, Green, Clear
- SsvcDecisionPointValue(
- name="Red",
- key="R",
- description="Provider has assessed the impact of this vulnerability as having the highest urgency.",
- ),
- SsvcDecisionPointValue(
- name="Amber",
- key="A",
- description="Provider has assessed the impact of this vulnerability as having a moderate urgency.",
- ),
- SsvcDecisionPointValue(
- name="Green",
- key="G",
- description="Provider has assessed the impact of this vulnerability as having a reduced urgency.",
- ),
- SsvcDecisionPointValue(
- name="Clear",
- key="C",
- description="Provider has assessed the impact of this vulnerability as having no urgency (Informational).",
- ),
+ CLEAR,
+ GREEN,
+ AMBER,
+ RED,
),
)
+VERSIONS = (PROVIDER_URGENCY_1,)
+LATEST = PROVIDER_URGENCY_1
-def main():
- versions = [
- PROVIDER_URGENCY_1,
- ]
- print_versions_and_diffs(versions)
+def main():
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/supplemental/recovery.py b/src/ssvc/decision_points/cvss/supplemental/recovery.py
index cc815c28..14ea6ef8 100644
--- a/src/ssvc/decision_points/cvss/supplemental/recovery.py
+++ b/src/ssvc/decision_points/cvss/supplemental/recovery.py
@@ -2,7 +2,7 @@
"""
Provides the CVSS supplemental metric Recovery
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -20,7 +20,22 @@
from ssvc.decision_points.cvss.base import CvssDecisionPoint
from ssvc.decision_points.helpers import print_versions_and_diffs
-
+AUTOMATIC = SsvcDecisionPointValue(
+ name="Automatic",
+ key="A",
+ description="The system recovers services automatically after an attack has been performed.",
+)
+USER = SsvcDecisionPointValue(
+ name="User",
+ key="U",
+ description="The system requires manual intervention by the user to recover services, after an attack has "
+ "been performed.",
+)
+IRRECOVERABLE = SsvcDecisionPointValue(
+ name="Irrecoverable",
+ key="I",
+ description="The system services are irrecoverable by the user, after an attack has been performed.",
+)
RECOVERY_1 = CvssDecisionPoint(
name="Recovery",
description="The Recovery metric describes the resilience of a system to recover services, in terms of performance "
@@ -29,32 +44,18 @@
version="1.0.0",
values=(
NOT_DEFINED_X,
- SsvcDecisionPointValue(
- name="Automatic",
- key="A",
- description="The system recovers services automatically after an attack has been performed.",
- ),
- SsvcDecisionPointValue(
- name="User",
- key="U",
- description="The system requires manual intervention by the user to recover services, after an attack has "
- "been performed.",
- ),
- SsvcDecisionPointValue(
- name="Irrecoverable",
- key="I",
- description="The system services are irrecoverable by the user, after an attack has been performed.",
- ),
+ AUTOMATIC,
+ USER,
+ IRRECOVERABLE,
),
)
+VERSIONS = (RECOVERY_1,)
+LATEST = VERSIONS[-1]
-def main():
- versions = [
- RECOVERY_1,
- ]
- print_versions_and_diffs(versions)
+def main():
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/supplemental/safety.py b/src/ssvc/decision_points/cvss/supplemental/safety.py
index 807d677a..14152687 100644
--- a/src/ssvc/decision_points/cvss/supplemental/safety.py
+++ b/src/ssvc/decision_points/cvss/supplemental/safety.py
@@ -3,7 +3,7 @@
Provides CVSS v4 Supplemental Metric for Safety
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -21,6 +21,18 @@
from ssvc.decision_points.cvss.base import CvssDecisionPoint
from ssvc.decision_points.helpers import print_versions_and_diffs
+PRESENT = SsvcDecisionPointValue(
+ name="Present",
+ key="P",
+ description="Consequences of the vulnerability meet definition of IEC 61508 consequence categories of "
+ '"marginal," "critical," or "catastrophic."',
+)
+NEGLIGIBLE = SsvcDecisionPointValue(
+ name="Negligible",
+ key="N",
+ description="Consequences of the vulnerability meet definition of IEC 61508 consequence category "
+ '"negligible."',
+)
SAFETY_1 = CvssDecisionPoint(
name="Safety",
description="The Safety decision point is a measure of the potential for harm to humans or the environment.",
@@ -28,29 +40,17 @@
version="1.0.0",
values=(
NOT_DEFINED_X,
- # Present, Negligible
- SsvcDecisionPointValue(
- name="Present",
- key="P",
- description="Consequences of the vulnerability meet definition of IEC 61508 consequence categories of "
- '"marginal," "critical," or "catastrophic."',
- ),
- SsvcDecisionPointValue(
- name="Negligible",
- key="N",
- description="Consequences of the vulnerability meet definition of IEC 61508 consequence category "
- '"negligible."',
- ),
+ PRESENT,
+ NEGLIGIBLE,
),
)
+VERSIONS = (SAFETY_1,)
+LATEST = SAFETY_1
-def main():
- versions = [
- SAFETY_1,
- ]
- print_versions_and_diffs(versions)
+def main():
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/supplemental/value_density.py b/src/ssvc/decision_points/cvss/supplemental/value_density.py
index 35e61b7d..4e56c1dc 100644
--- a/src/ssvc/decision_points/cvss/supplemental/value_density.py
+++ b/src/ssvc/decision_points/cvss/supplemental/value_density.py
@@ -2,7 +2,7 @@
"""
Provides the CVSS supplemental metric Value Density
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -20,7 +20,18 @@
from ssvc.decision_points.cvss.base import CvssDecisionPoint
from ssvc.decision_points.helpers import print_versions_and_diffs
-
+DIFFUSE = SsvcDecisionPointValue(
+ name="Diffuse",
+ key="D",
+ description="The vulnerable system has limited resources. That is, the resources that the attacker will "
+ "gain control over with a single exploitation event are relatively small.",
+)
+CONCENTRATED = SsvcDecisionPointValue(
+ name="Concentrated",
+ key="C",
+ description="The vulnerable system is rich in resources. Heuristically, such systems are often the direct "
+ 'responsibility of "system operators" rather than users.',
+)
VALUE_DENSITY_1 = CvssDecisionPoint(
name="Value Density",
description="Value Density describes the resources that the attacker will gain control over with a single "
@@ -29,28 +40,17 @@
version="1.0.0",
values=(
NOT_DEFINED_X,
- SsvcDecisionPointValue(
- name="Diffuse",
- key="D",
- description="The vulnerable system has limited resources. That is, the resources that the attacker will "
- "gain control over with a single exploitation event are relatively small.",
- ),
- SsvcDecisionPointValue(
- name="Concentrated",
- key="C",
- description="The vulnerable system is rich in resources. Heuristically, such systems are often the direct "
- 'responsibility of "system operators" rather than users.',
- ),
+ DIFFUSE,
+ CONCENTRATED,
),
)
+VERSIONS = (VALUE_DENSITY_1,)
+LATEST = VERSIONS[-1]
-def main():
- versions = [
- VALUE_DENSITY_1,
- ]
- print_versions_and_diffs(versions)
+def main():
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/supplemental/vulnerability_response_effort.py b/src/ssvc/decision_points/cvss/supplemental/vulnerability_response_effort.py
index 13f61107..d69077ee 100644
--- a/src/ssvc/decision_points/cvss/supplemental/vulnerability_response_effort.py
+++ b/src/ssvc/decision_points/cvss/supplemental/vulnerability_response_effort.py
@@ -2,7 +2,7 @@
"""
Provides the CVSS supplemental metric Vulnerability Response Effort.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -20,7 +20,27 @@
from ssvc.decision_points.cvss.base import CvssDecisionPoint
from ssvc.decision_points.helpers import print_versions_and_diffs
-
+LOW = SsvcDecisionPointValue(
+ name="Low",
+ key="L",
+ description="The effort required to respond to a vulnerability is low/trivial.",
+)
+MODERATE = SsvcDecisionPointValue(
+ name="Moderate",
+ key="M",
+ description="The actions required to respond to a vulnerability require some effort on behalf of the "
+ "consumer and could cause minimal service impact to implement.",
+)
+HIGH = SsvcDecisionPointValue(
+ name="High",
+ key="H",
+ description="The actions required to respond to a vulnerability are significant and/or difficult, and may "
+ "possibly lead to an extended, scheduled service impact. This would need to be considered for scheduling "
+ "purposes including honoring any embargo on deployment of the selected response. Alternatively, response "
+ "to the vulnerability in the field is not possible remotely. The only resolution to the vulnerability "
+ "involves physical replacement (e.g. units deployed would have to be recalled for a depot level repair or "
+ "replacement).",
+)
VULNERABILITY_RESPONSE_EFFORT_1 = CvssDecisionPoint(
name="Vulnerability Response Effort",
description="The intention of the Vulnerability Response Effort metric is to provide supplemental information on "
@@ -31,37 +51,18 @@
version="1.0.0",
values=(
NOT_DEFINED_X,
- SsvcDecisionPointValue(
- name="Low",
- key="L",
- description="The effort required to respond to a vulnerability is low/trivial.",
- ),
- SsvcDecisionPointValue(
- name="Moderate",
- key="M",
- description="The actions required to respond to a vulnerability require some effort on behalf of the "
- "consumer and could cause minimal service impact to implement.",
- ),
- SsvcDecisionPointValue(
- name="High",
- key="H",
- description="The actions required to respond to a vulnerability are significant and/or difficult, and may "
- "possibly lead to an extended, scheduled service impact. This would need to be considered for scheduling "
- "purposes including honoring any embargo on deployment of the selected response. Alternatively, response "
- "to the vulnerability in the field is not possible remotely. The only resolution to the vulnerability "
- "involves physical replacement (e.g. units deployed would have to be recalled for a depot level repair or "
- "replacement).",
- ),
+ LOW,
+ MODERATE,
+ HIGH,
),
)
+VERSIONS = (VULNERABILITY_RESPONSE_EFFORT_1,)
+LATEST = VERSIONS[-1]
-def main():
- versions = [
- VULNERABILITY_RESPONSE_EFFORT_1,
- ]
- print_versions_and_diffs(versions)
+def main():
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/target_distribution.py b/src/ssvc/decision_points/cvss/target_distribution.py
index 3601f07b..2f408e67 100644
--- a/src/ssvc/decision_points/cvss/target_distribution.py
+++ b/src/ssvc/decision_points/cvss/target_distribution.py
@@ -3,7 +3,7 @@
Models CVSS Target Distribution as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -87,14 +87,15 @@
Introduces Not Defined value.
"""
-versions = [
+VERSIONS = (
TARGET_DISTRIBUTION_1,
TARGET_DISTRIBUTION_1_1,
-]
+)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/cvss/user_interaction.py b/src/ssvc/decision_points/cvss/user_interaction.py
index f5ce27e0..02e75941 100644
--- a/src/ssvc/decision_points/cvss/user_interaction.py
+++ b/src/ssvc/decision_points/cvss/user_interaction.py
@@ -3,7 +3,7 @@
Models CVSS User Interaction as an SSVC decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -89,11 +89,12 @@
),
)
-versions = [USER_INTERACTION_1, USER_INTERACTION_2]
+VERSIONS = (USER_INTERACTION_1, USER_INTERACTION_2)
+LATEST = VERSIONS[-1]
def main():
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/exploitation.py b/src/ssvc/decision_points/exploitation.py
index 15034d9d..bb1a2a52 100644
--- a/src/ssvc/decision_points/exploitation.py
+++ b/src/ssvc/decision_points/exploitation.py
@@ -2,7 +2,7 @@
"""
Provides the Exploitation decision point and its values.
"""
-# Copyright (c) 2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2024-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -74,11 +74,12 @@ def _strip_spaces(s):
),
)
+VERSIONS = (EXPLOITATION_1, EXPLOITATION_1_1_0)
+LATEST = VERSIONS[-1]
-def main():
- versions = [EXPLOITATION_1, EXPLOITATION_1_1_0]
- print_versions_and_diffs(versions)
+def main():
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/helpers.py b/src/ssvc/decision_points/helpers.py
index 84c61bdc..111ec8e1 100644
--- a/src/ssvc/decision_points/helpers.py
+++ b/src/ssvc/decision_points/helpers.py
@@ -1,8 +1,9 @@
#!/usr/bin/env python
+
"""
Provides helper functions for working with SSVC decision points.
"""
-# Copyright (c) 2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2024-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -17,21 +18,9 @@
from typing import Sequence
-from ssvc.decision_points import SsvcDecisionPoint
-
+import semver
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
-# - see Contributors.md for a full list of Contributors
-# - see ContributionInstructions.md for information on how you can Contribute to this project
-# Stakeholder Specific Vulnerability Categorization (SSVC) is
-# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
-# with this Software or contact permission@sei.cmu.edu for full terms.
-# Created, in part, with funding and support from the United States Government
-# (see Acknowledgments file). This program may include and/or can make use of
-# certain third party source code, object code, documentation and other files
-# (“Third Party Software”). See LICENSE.md for more details.
-# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
-# U.S. Patent and Trademark Office by Carnegie Mellon University
+from ssvc.decision_points import SsvcDecisionPoint
def dp_diff(dp1: SsvcDecisionPoint, dp2: SsvcDecisionPoint) -> list[str]:
@@ -132,10 +121,10 @@ def dp_diff(dp1: SsvcDecisionPoint, dp2: SsvcDecisionPoint) -> list[str]:
# did the value keys change?
for name in intersection:
- v1 = {value["name"]: value["key"] for value in dp1.to_dict()["values"]}
+ v1 = {value["name"]: value["key"] for value in dp1.model_dump()["values"]}
v1 = v1[name]
- v2 = {value["name"]: value["key"] for value in dp2.to_dict()["values"]}
+ v2 = {value["name"]: value["key"] for value in dp2.model_dump()["values"]}
v2 = v2[name]
if v1 != v2:
@@ -146,10 +135,14 @@ def dp_diff(dp1: SsvcDecisionPoint, dp2: SsvcDecisionPoint) -> list[str]:
# did the value descriptions change?
for name in intersection:
- v1 = {value["name"]: value["description"] for value in dp1.to_dict()["values"]}
+ v1 = {
+ value["name"]: value["description"] for value in dp1.model_dump()["values"]
+ }
v1 = v1[name]
- v2 = {value["name"]: value["description"] for value in dp2.to_dict()["values"]}
+ v2 = {
+ value["name"]: value["description"] for value in dp2.model_dump()["values"]
+ }
v2 = v2[name]
if v1 != v2:
@@ -162,12 +155,30 @@ def dp_diff(dp1: SsvcDecisionPoint, dp2: SsvcDecisionPoint) -> list[str]:
f"{dp2.name} v{dp2.version} value {name} description did not change"
)
+ v1 = semver.VersionInfo.parse(dp1.version)
+ v2 = semver.VersionInfo.parse(dp2.version)
+
if major:
diffs.append(f"{dp2.name} v{dp2.version} appears to be a major change")
+ expected = v1.bump_major()
+ if v2 != expected:
+ diffs.append(
+ f"Expected version to be bumped to {expected}, but was bumped to {v2}"
+ )
elif minor:
diffs.append(f"{dp2.name} v{dp2.version} appears to be a minor change")
- elif patch:
+ expected = v1.bump_minor()
+ if v2 != expected:
+ diffs.append(
+ f"Expected version to be bumped to {expected}, but was bumped to {v2}"
+ )
+ elif patch and not any([maybe_minor, maybe_major]):
diffs.append(f"{dp2.name} v{dp2.version} appears to be a patch change")
+ expected = v1.bump_patch()
+ if v2 != expected:
+ diffs.append(
+ f"Expected version to be bumped to {expected}, but was bumped to {v2}"
+ )
if maybe_new_obj:
diffs.append(
@@ -176,13 +187,23 @@ def dp_diff(dp1: SsvcDecisionPoint, dp2: SsvcDecisionPoint) -> list[str]:
)
if not major:
+ check_possible = False
+ possible1 = v1.bump_major()
+ possible2 = v1.bump_minor()
if maybe_major:
diffs.append(
- f"{dp2.name} v{dp2.version} could be a major change depending on context"
+ f"{dp2.name} v{dp2.version} could be a major change ({v1} -> {possible1}) depending on context"
)
+ check_possible = True
if maybe_minor:
diffs.append(
- f"{dp2.name} v{dp2.version} could be a minor change depending on context"
+ f"{dp2.name} v{dp2.version} could be a minor change ({v1} -> {possible2}) depending on context"
+ )
+ check_possible = True
+ # did one of possible1 or possible2 match v2?
+ if check_possible and v2 not in [possible1, possible2]:
+ diffs.append(
+ f"Expected version to be bumped to {possible1} or {possible2}, but was bumped to {v2}"
)
if not any([major, minor, patch, maybe_major, maybe_minor]):
@@ -213,7 +234,7 @@ def print_versions_and_diffs(versions: Sequence[SsvcDecisionPoint]) -> None:
None
"""
for version in versions:
- print(version.to_json(indent=2))
+ print(version.model_dump_json(indent=2))
show_diffs(versions)
diff --git a/src/ssvc/decision_points/high_value_asset.py b/src/ssvc/decision_points/high_value_asset.py
index 9cf95f96..35d2c9d4 100644
--- a/src/ssvc/decision_points/high_value_asset.py
+++ b/src/ssvc/decision_points/high_value_asset.py
@@ -1,8 +1,9 @@
#!/usr/bin/env python
+
"""
Models a high value asset as a decision point.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -16,6 +17,7 @@
# U.S. Patent and Trademark Office by Carnegie Mellon University
from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue
+from ssvc.decision_points.helpers import print_versions_and_diffs
YES = SsvcDecisionPointValue(
name="Yes",
@@ -40,9 +42,12 @@
),
)
+VERSIONS = (HIGH_VALUE_ASSET_1,)
+LATEST = VERSIONS[-1]
+
def main():
- print(HIGH_VALUE_ASSET_1.to_json(indent=2))
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/human_impact.py b/src/ssvc/decision_points/human_impact.py
index bc3c48ad..ac2deac0 100644
--- a/src/ssvc/decision_points/human_impact.py
+++ b/src/ssvc/decision_points/human_impact.py
@@ -1,8 +1,9 @@
#!/usr/bin/env python
+
"""
Provides the Human Impact decision point and its values.
"""
-# Copyright (c) 2023-2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -119,11 +120,16 @@
),
)
+VERSIONS = (
+ MISSION_AND_WELL_BEING_IMPACT_1,
+ HUMAN_IMPACT_2,
+ HUMAN_IMPACT_2_0_1,
+)
+LATEST = VERSIONS[-1]
+
def main():
- versions = (MISSION_AND_WELL_BEING_IMPACT_1, HUMAN_IMPACT_2, HUMAN_IMPACT_2_0_1)
-
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/in_kev.py b/src/ssvc/decision_points/in_kev.py
index 70772dba..31466aaa 100644
--- a/src/ssvc/decision_points/in_kev.py
+++ b/src/ssvc/decision_points/in_kev.py
@@ -2,7 +2,7 @@
"""
Provides a decision point representing whether a vulnerability is in the CISA Known Exploited Vulnerabilities (KEV) list.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -16,6 +16,7 @@
# U.S. Patent and Trademark Office by Carnegie Mellon University
from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue
+from ssvc.decision_points.helpers import print_versions_and_diffs
YES = SsvcDecisionPointValue(
name="Yes",
@@ -40,9 +41,12 @@
),
)
+VERSIONS = (IN_KEV_1,)
+LATEST = VERSIONS[-1]
+
def main():
- print(IN_KEV_1.to_json(indent=2))
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/mission_impact.py b/src/ssvc/decision_points/mission_impact.py
index 299657ff..d0a3a132 100644
--- a/src/ssvc/decision_points/mission_impact.py
+++ b/src/ssvc/decision_points/mission_impact.py
@@ -1,11 +1,10 @@
#!/usr/bin/env python
+
"""
-file: mission_impact
-author: adh
-created_at: 9/21/23 10:20 AM
+Provides the Mission Impact decision point and its values.
"""
-# Copyright (c) 2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2024-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -81,11 +80,12 @@
values=(DEGRADED, MEF_CRIPPLED, MEF_FAILURE, MISSION_FAILURE),
)
+VERSIONS = (MISSION_IMPACT_1, MISSION_IMPACT_2)
+LATEST = VERSIONS[-1]
-def main():
- versions = (MISSION_IMPACT_1, MISSION_IMPACT_2)
- print_versions_and_diffs(versions)
+def main():
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/mission_prevalence.py b/src/ssvc/decision_points/mission_prevalence.py
index 73998bcc..8bf55920 100644
--- a/src/ssvc/decision_points/mission_prevalence.py
+++ b/src/ssvc/decision_points/mission_prevalence.py
@@ -1,4 +1,10 @@
-# Copyright (c) 2024 Carnegie Mellon University and Contributors.
+#!/usr/bin/env python
+
+"""
+Provides the Mission Prevalence decision point and its values.
+"""
+
+# Copyright (c) 2024-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -46,7 +52,9 @@
),
)
+VERSIONS = (MISSION_PREVALENCE,)
+LATEST = VERSIONS[-1]
+
if __name__ == "__main__":
- versions = (MISSION_PREVALENCE,)
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
diff --git a/src/ssvc/decision_points/public_safety_impact.py b/src/ssvc/decision_points/public_safety_impact.py
index cb857797..54df0a8e 100644
--- a/src/ssvc/decision_points/public_safety_impact.py
+++ b/src/ssvc/decision_points/public_safety_impact.py
@@ -1,9 +1,10 @@
#!/usr/bin/env python
+
"""
Provides the Public Safety Impact decision point and its values.
"""
-# Copyright (c) 2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2024-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -108,14 +109,16 @@
),
)
+VERSIONS = (
+ PUBLIC_WELL_BEING_IMPACT_1,
+ PUBLIC_SAFETY_IMPACT_2,
+ PUBLIC_SAFETY_IMPACT_2_0_1,
+)
+LATEST = VERSIONS[-1]
+
def main():
- versions = (
- PUBLIC_WELL_BEING_IMPACT_1,
- PUBLIC_SAFETY_IMPACT_2,
- PUBLIC_SAFETY_IMPACT_2_0_1,
- )
- print_versions_and_diffs(versions)
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/public_value_added.py b/src/ssvc/decision_points/public_value_added.py
index 6f8158de..87b4700a 100644
--- a/src/ssvc/decision_points/public_value_added.py
+++ b/src/ssvc/decision_points/public_value_added.py
@@ -1,9 +1,10 @@
#!/usr/bin/env python
+
"""
This module provides the Public Value Added decision point for the Stakeholder Specific Vulnerability Categorization (SSVC) framework.
"""
-# Copyright (c) 2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2024-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -46,10 +47,12 @@
)
-def main():
- versions = (PUBLIC_VALUE_ADDED_1,)
+VERSIONS = (PUBLIC_VALUE_ADDED_1,)
+LATEST = VERSIONS[-1]
- print_versions_and_diffs(versions)
+
+def main():
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/report_credibility.py b/src/ssvc/decision_points/report_credibility.py
index 93ff6c4b..1e4cf105 100644
--- a/src/ssvc/decision_points/report_credibility.py
+++ b/src/ssvc/decision_points/report_credibility.py
@@ -1,9 +1,10 @@
#!/usr/bin/env python
+
"""
Provides the SSVC Report Credibility Decision Point
"""
-# Copyright (c) 2023-2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -17,6 +18,7 @@
# U.S. Patent and Trademark Office by Carnegie Mellon University
from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue
+from ssvc.decision_points.helpers import print_versions_and_diffs
NOT_CREDIBLE = SsvcDecisionPointValue(
name="Not Credible",
@@ -41,9 +43,12 @@
),
)
+VERSIONS = (REPORT_CREDIBILITY_1,)
+LATEST = VERSIONS[-1]
+
def main():
- print(REPORT_CREDIBILITY_1.to_json(indent=2))
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/report_public.py b/src/ssvc/decision_points/report_public.py
index 7947e1fb..a072e185 100644
--- a/src/ssvc/decision_points/report_public.py
+++ b/src/ssvc/decision_points/report_public.py
@@ -1,8 +1,9 @@
#!/usr/bin/env python
+
"""
Provides the SSVC Report Public Decision Point
"""
-# Copyright (c) 2023-2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -16,6 +17,7 @@
# U.S. Patent and Trademark Office by Carnegie Mellon University
from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue
+from ssvc.decision_points.helpers import print_versions_and_diffs
YES = SsvcDecisionPointValue(
name="Yes",
@@ -40,9 +42,12 @@
),
)
+VERSIONS = (REPORT_PUBLIC_1,)
+LATEST = VERSIONS[-1]
+
def main():
- print(REPORT_PUBLIC_1.to_json(indent=2))
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/safety_impact.py b/src/ssvc/decision_points/safety_impact.py
index 691263c2..5a5c16ae 100644
--- a/src/ssvc/decision_points/safety_impact.py
+++ b/src/ssvc/decision_points/safety_impact.py
@@ -1,9 +1,10 @@
#!/usr/bin/env python
+
"""
Provides the Safety Impact decision point and its values.
"""
-# Copyright (c) 2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2024-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -158,10 +159,12 @@
)
-def main():
- versions = (SAFETY_IMPACT_1, SAFETY_IMPACT_2)
+VERSIONS = (SAFETY_IMPACT_1, SAFETY_IMPACT_2)
+LATEST = VERSIONS[-1]
- print_versions_and_diffs(versions)
+
+def main():
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/supplier_cardinality.py b/src/ssvc/decision_points/supplier_cardinality.py
index ebde9d27..934ebfdf 100644
--- a/src/ssvc/decision_points/supplier_cardinality.py
+++ b/src/ssvc/decision_points/supplier_cardinality.py
@@ -1,10 +1,23 @@
#!/usr/bin/env python
+
"""
-file: supplier_cardinality
-author: adh
-created_at: 9/21/23 11:20 AM
+Provides the Supplier Cardinality decision point and its values.
"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue
+from ssvc.decision_points.helpers import print_versions_and_diffs
MULTIPLE = SsvcDecisionPointValue(
name="Multiple",
@@ -29,9 +42,12 @@
),
)
+VERSIONS = (SUPPLIER_CARDINALITY_1,)
+LATEST = VERSIONS[-1]
+
def main():
- print(SUPPLIER_CARDINALITY_1.to_json(indent=2))
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/supplier_contacted.py b/src/ssvc/decision_points/supplier_contacted.py
index eff08419..f3586008 100644
--- a/src/ssvc/decision_points/supplier_contacted.py
+++ b/src/ssvc/decision_points/supplier_contacted.py
@@ -1,10 +1,22 @@
#!/usr/bin/env python
"""
-file: supplier_contacted
-author: adh
-created_at: 9/21/23 11:17 AM
+Provides the Supplier Engagement decision point and its values.
"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue
+from ssvc.decision_points.helpers import print_versions_and_diffs
YES = SsvcDecisionPointValue(
name="Yes",
@@ -29,9 +41,12 @@
),
)
+VERSIONS = (SUPPLIER_CONTACTED_1,)
+LATEST = VERSIONS[-1]
+
def main():
- print(SUPPLIER_CONTACTED_1.to_json(indent=2))
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/supplier_engagement.py b/src/ssvc/decision_points/supplier_engagement.py
index 69380931..cb0aef24 100644
--- a/src/ssvc/decision_points/supplier_engagement.py
+++ b/src/ssvc/decision_points/supplier_engagement.py
@@ -1,11 +1,24 @@
#!/usr/bin/env python
+
"""
-file: supplier_engagement
-author: adh
-created_at: 9/21/23 11:22 AM
+Provides the Supplier Engagement decision point and its values.
"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue
+from ssvc.decision_points.helpers import print_versions_and_diffs
UNRESPONSIVE = SsvcDecisionPointValue(
name="Unresponsive",
@@ -30,9 +43,12 @@
),
)
+VERSIONS = (SUPPLIER_ENGAGEMENT_1,)
+LATEST = VERSIONS[-1]
+
def main():
- print(SUPPLIER_ENGAGEMENT_1.to_json(indent=2))
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/supplier_involvement.py b/src/ssvc/decision_points/supplier_involvement.py
index afc3ce07..823afd4d 100644
--- a/src/ssvc/decision_points/supplier_involvement.py
+++ b/src/ssvc/decision_points/supplier_involvement.py
@@ -1,11 +1,23 @@
#!/usr/bin/env python
"""
-file: supplier_involvement
-author: adh
-created_at: 9/21/23 11:28 AM
+Provides the Supplier Involvement decision point and its values.
"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue
+from ssvc.decision_points.helpers import print_versions_and_diffs
UNCOOPERATIVE = SsvcDecisionPointValue(
name="Uncooperative/Unresponsive",
@@ -37,9 +49,12 @@
),
)
+VERSIONS = (SUPPLIER_INVOLVEMENT_1,)
+LATEST = VERSIONS[-1]
+
def main():
- print(SUPPLIER_INVOLVEMENT_1.to_json(indent=2))
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/system_exposure.py b/src/ssvc/decision_points/system_exposure.py
index 61dc6766..9f0c813a 100644
--- a/src/ssvc/decision_points/system_exposure.py
+++ b/src/ssvc/decision_points/system_exposure.py
@@ -1,10 +1,23 @@
#!/usr/bin/env python
+
"""
-file: exposure
-author: adh
-created_at: 9/21/23 10:16 AM
+Provides the System Exposure decision point and its values.
"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue
+from ssvc.decision_points.helpers import print_versions_and_diffs
EXP_UNAVOIDABLE = SsvcDecisionPointValue(
name="Unavoidable",
@@ -64,10 +77,12 @@
),
)
+VERSIONS = (SYSTEM_EXPOSURE_1, SYSTEM_EXPOSURE_1_0_1)
+LATEST = VERSIONS[-1]
+
def main():
- print(SYSTEM_EXPOSURE_1.to_json(indent=2))
- print(SYSTEM_EXPOSURE_1_0_1.to_json(indent=2))
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/technical_impact.py b/src/ssvc/decision_points/technical_impact.py
index da042f62..3fa10eff 100644
--- a/src/ssvc/decision_points/technical_impact.py
+++ b/src/ssvc/decision_points/technical_impact.py
@@ -1,10 +1,24 @@
#!/usr/bin/env python
+
"""
-file: technical_impact
-author: adh
-created_at: 9/21/23 9:49 AM
+Provides the Technical Impact decision point and its values.
"""
+
+# Copyright (c) 2024-2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue
+from ssvc.decision_points.helpers import print_versions_and_diffs
TOTAL = SsvcDecisionPointValue(
name="Total",
@@ -29,9 +43,12 @@
),
)
+VERSIONS = (TECHNICAL_IMPACT_1,)
+LATEST = VERSIONS[-1]
+
def main():
- print(TECHNICAL_IMPACT_1.to_json(indent=2))
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/utility.py b/src/ssvc/decision_points/utility.py
index 9aace78a..10b43924 100644
--- a/src/ssvc/decision_points/utility.py
+++ b/src/ssvc/decision_points/utility.py
@@ -1,6 +1,10 @@
#!/usr/bin/env python
-# Copyright (c) 2024 Carnegie Mellon University and Contributors.
-# - see Contributors.md for a full list of Contributors
+
+"""
+Provides the Utility decision point and its values.
+"""
+
+# Copyright (c) 2024-2025 Carnegie Mellon University and Contributors.
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
@@ -28,7 +32,9 @@
)
LABORIOUS_2 = SsvcDecisionPointValue(
- name="Laborious", key="L", description="Automatable:No AND Value Density:Diffuse"
+ name="Laborious",
+ key="L",
+ description="Automatable:No AND Value Density:Diffuse",
)
SUPER_EFFECTIVE = SsvcDecisionPointValue(
@@ -44,7 +50,9 @@
)
LABORIOUS = SsvcDecisionPointValue(
- name="Laborious", key="L", description="Virulence:Slow and Value Density:Diffuse"
+ name="Laborious",
+ key="L",
+ description="Virulence:Slow and Value Density:Diffuse",
)
UTILITY_1 = SsvcDecisionPoint(
@@ -71,11 +79,12 @@
),
)
+VERSIONS = (UTILITY_1, UTILITY_1_0_1)
+LATEST = VERSIONS[-1]
-def main():
- versions = (UTILITY_1, UTILITY_1_0_1)
- print_versions_and_diffs(versions)
+def main():
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/decision_points/value_density.py b/src/ssvc/decision_points/value_density.py
index eac48a13..81b9fd14 100644
--- a/src/ssvc/decision_points/value_density.py
+++ b/src/ssvc/decision_points/value_density.py
@@ -1,10 +1,23 @@
#!/usr/bin/env python
"""
-file: value_density
-author: adh
-created_at: 9/21/23 10:01 AM
+Provides the Value Density decision point and its values.
"""
+
+# Copyright (c) 2024-2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue
+from ssvc.decision_points.helpers import print_versions_and_diffs
CONCENTRATED = SsvcDecisionPointValue(
name="Concentrated",
@@ -29,9 +42,12 @@
),
)
+VERSIONS = (VALUE_DENSITY_1,)
+LATEST = VERSIONS[-1]
+
def main():
- print(VALUE_DENSITY_1.to_json(indent=2))
+ print_versions_and_diffs(VERSIONS)
if __name__ == "__main__":
diff --git a/src/ssvc/doc_helpers.py b/src/ssvc/doc_helpers.py
new file mode 100644
index 00000000..16a48bc8
--- /dev/null
+++ b/src/ssvc/doc_helpers.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python
+"""
+file: doc_helpers
+author: adh
+created_at: 2/14/25 2:54 PM
+"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
+from ssvc.decision_points.base import SsvcDecisionPoint
+
+MD_TABLE_ROW_TEMPLATE = "| {value.name} | {value.description} |"
+
+
+def markdown_table(dp: SsvcDecisionPoint, indent: int = 0) -> str:
+ """
+ Generate a markdown table for a decision point.
+
+ Args:
+ dp (SsvcDecisionPoint): The decision point to generate a markdown table for.
+
+ Returns:
+ str: The markdown table.
+ """
+ rows = []
+ # prepend the header
+ _indent = " " * indent
+ rows.append(f"{_indent}{dp.description}")
+ rows.append("")
+ rows.append(f"{_indent}| Value | Definition |")
+ rows.append(f"{_indent}|:-----|:-----------|")
+
+ # add a row for each value
+ for value in dp.values:
+ rows.append(_indent + MD_TABLE_ROW_TEMPLATE.format(value=value))
+
+ return "\n".join(rows)
+
+
+def example_block_tabbed(dp: SsvcDecisionPoint, indent=4) -> str:
+ """Given a decision point, return a markdown block that contains an example of the decision point."""
+
+ indent_ = " " * 4
+ rows = []
+ rows.append(f'!!! note "{dp.name} v{dp.version}"')
+ rows.append("")
+
+ rows.append(indent_ + '=== "Table"')
+ rows.append("")
+ for row in markdown_table(dp, indent=4).splitlines():
+ rows.append(indent_ + row)
+ rows.append("")
+
+ rows.append(indent_ + '=== "JSON"')
+ rows.append("")
+ for row in json_example(dp, indent=4).splitlines():
+ rows.append(indent_ + row)
+
+ return "\n".join(rows)
+
+
+def example_block(
+ dp: SsvcDecisionPoint, indent: int = 4, include_json: bool = True
+) -> str:
+ """Given a decision point, return a markdown block that contains an example of the decision point."""
+
+ indent_ = " " * indent
+ rows = []
+ rows.append(f'!!! note "{dp.name} v{dp.version}"')
+ rows.append("")
+
+ for row in markdown_table(dp).splitlines():
+ rows.append(indent_ + row)
+ rows.append("")
+
+ if include_json:
+ rows.append(indent_ + f'??? example "{dp.name} v{dp.version} JSON Example"')
+ rows.append("")
+ for row in json_example(dp, indent=4).splitlines():
+ rows.append(indent_ + row)
+
+ return "\n".join(rows)
+
+
+def prior_version(dp: SsvcDecisionPoint, indent=4) -> str:
+ """Given a decision point, return a markdown block that contains an example of the decision point."""
+
+ indent_ = " " * 4
+ rows = []
+ rows.append(f'!!! note "{dp.name} v{dp.version}"')
+ rows.append("")
+
+ rows.append("")
+ for row in markdown_table(dp, indent=0).splitlines():
+ rows.append(indent_ + row)
+
+ return "\n".join(rows)
+
+
+def json_example(dp, indent=0):
+ """
+ Generate a markdown block that contains a JSON example.
+
+ Args:
+ dp: the decision point object
+ jstr:
+ collapsible:
+
+ Returns:
+
+ """
+ indent_ = " " * indent
+ json_rows = [
+ indent_ + "```json",
+ ]
+
+ jstr = dp.model_dump_json(indent=2).strip()
+
+ for line in jstr.splitlines():
+ json_rows.append(indent_ + line)
+
+ json_rows.append(
+ indent_ + "```",
+ )
+ json_block = "\n".join(json_rows)
+ return json_block
+
+
+def main():
+ pass
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/ssvc/doctools.py b/src/ssvc/doctools.py
index 5baca11b..93b4591c 100644
--- a/src/ssvc/doctools.py
+++ b/src/ssvc/doctools.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -15,27 +15,28 @@
Provides tools to assist with generating documentation for SSVC decision points.
Writes the following files for each decision point:
-- a markdown table that can be used in the decision point documentation
- a json example that can be used in the decision point documentation
-- a markdown file that builds an insert using mkdocs tabs to switch between the markdown description and the json
- example
Examples
To generate the documentation for the decision points, use the following command:
- python -m ssvc.doctools --overwrite --outdir ./tmp/md_out --jsondir ./tmp/json_out`
+ python -m ssvc.doctools --overwrite --jsondir ./tmp/json_out`
To regenerate the existing docs, use the following command:
- python -m ssvc.doctools --overwrite --outdir docs/_generated/decision_points --jsondir data/json/decision_points
+ python -m ssvc.doctools --overwrite --jsondir data/json/decision_points
"""
import logging
import os
-from ssvc.decision_points.base import REGISTERED_DECISION_POINTS, SsvcDecisionPoint
-from ssvc.dp_groups.ssvc.collections import SSVCv1, SSVCv2, SSVCv2_1 # noqa
+import ssvc.dp_groups.cvss.collections # noqa
+import ssvc.dp_groups.ssvc.collections # noqa
+from ssvc.decision_points.base import (
+ REGISTERED_DECISION_POINTS,
+ SsvcDecisionPoint,
+)
logger = logging.getLogger(__name__)
@@ -53,49 +54,6 @@ def _filename_friendly(name: str) -> str:
return name.lower().replace(" ", "_").replace(".", "_")
-MD_TABLE_ROW_TEMPLATE = "| {value.name} | {value.description} |"
-
-# indent by 4 spaces to make it a code block
-MD_INCLUDE_TEMPLATE = """
-!!! note "{dp.name} v{dp.version}"
-
-=== "Text"
-
-{table}
-
-=== "JSON"
-
- ```json
- {{% include "{json_file}" %}}
- ```
-"""
-
-
-def to_markdown_table(dp: SsvcDecisionPoint) -> str:
- """
- Generate a markdown table for a decision point.
-
- Args:
- dp (SsvcDecisionPoint): The decision point to generate a markdown table for.
-
- Returns:
- str: The markdown table.
- """
- rows = []
- # prepend the header
- rows.append(f" {dp.description}")
- rows.append("")
- indent = " " * 4
- rows.append(f"{indent}| Value | Definition |")
- rows.append(f"{indent}|:-----|:-----------|")
-
- # add a row for each value
- for value in dp.values:
- rows.append(indent + MD_TABLE_ROW_TEMPLATE.format(value=value))
-
- return "\n".join(rows)
-
-
# create a runtime context that ensures that dir exists
class EnsureDirExists:
"""
@@ -134,9 +92,8 @@ def remove_if_exists(file):
logger.debug(f"File {file} does not exist, nothing to remove")
-def dump_decision_point(
- jsondir: str, outdir: str, dp: SsvcDecisionPoint, overwrite: bool
-) -> dict:
+def dump_decision_point(jsondir: str, dp: SsvcDecisionPoint, overwrite: bool
+) -> None:
"""
Generate the markdown table, json example, and markdown table file for a decision point.
@@ -152,69 +109,10 @@ def dump_decision_point(
- symlink: The path to the symlink that points to the markdown table file.
- json_file: The path to the json example file.
"""
- # - generate markdown table
# make dp.name safe for use in a filename
basename = _filename_friendly(dp.name) + f"_{_filename_friendly(dp.version)}"
# - generate json example
- json_file = dump_json(basename, dp, jsondir, overwrite)
-
- # - generate markdown table file
- r = dump_markdown(basename, dp, json_file, outdir, overwrite)
- r["json_file"] = json_file
- return r
-
-
-def dump_markdown(
- basename: str, dp: SsvcDecisionPoint, json_file: str, outdir: str, overwrite: bool
-) -> dict:
- """
- Generate the markdown table file for a decision point.
-
- Args:
- basename (str): The basename of the markdown table file.
- dp (SsvcDecisionPoint): The decision point to generate documentation for.
- json_file (str): The path to the json example file.
- outdir (str): The directory to write the markdown table file to.
- overwrite (bool): Whether to overwrite existing files.
-
- Returns:
- dict: A dictionary with the following keys:
- - include_file: The path to the markdown table file.
- - symlink: The path to the symlink that points to the markdown table file.
- """
- include_file = f"{outdir}/{basename}.md"
-
- relative_json_file = os.path.relpath(json_file, outdir)
-
- if overwrite:
- remove_if_exists(include_file)
- with EnsureDirExists(outdir):
- try:
- with open(include_file, "x") as f:
- formatted_template = MD_INCLUDE_TEMPLATE.format(
- dp=dp,
- json_file=relative_json_file,
- table=(to_markdown_table(dp)),
- )
- f.write(formatted_template)
- except FileExistsError:
- logger.warning(
- f"File {include_file} already exists, use --overwrite to replace"
- )
-
- # update the symlink
- # because we don't want to have to edit each markdown file every time something changes
- symlink = f"{outdir}/{_filename_friendly(dp.name)}.md"
- remove_if_exists(symlink)
- relative_md_file = os.path.relpath(include_file, outdir)
- os.symlink(relative_md_file, symlink)
-
- result = {
- "include_file": include_file,
- "symlink": symlink,
- }
-
- return result
+ dump_json(basename, dp, jsondir, overwrite)
def dump_json(
@@ -232,13 +130,24 @@ def dump_json(
Returns:
str: The path to the json example file.
"""
- json_file = f"{jsondir}/{basename}.json"
+ # if namespace is ssvc, it goes in jsondir
+ filename = f"{basename}.json"
+ parts = [
+ jsondir,
+ ]
+ if dp.namespace != "ssvc":
+ parts.append(_filename_friendly(dp.namespace))
+ parts.append(filename)
+
+ json_file = os.path.join(*parts)
+
if overwrite:
remove_if_exists(json_file)
with EnsureDirExists(jsondir):
try:
with open(json_file, "x") as f:
- f.write(dp.to_json(indent=2))
+ f.write(dp.model_dump_json(indent=2))
+ f.write("\n") # newline at end of file
except FileExistsError:
logger.warning(
f"File {json_file} already exists, use --overwrite to replace"
@@ -266,19 +175,17 @@ def main():
default=False,
)
- parser.add_argument("--outdir", help="output directory", default="./tmp/md_out")
parser.add_argument(
"--jsondir", help="json output directory", default="./tmp/json_out"
)
args = parser.parse_args()
overwrite = args.overwrite
- outdir = args.outdir
jsondir = args.jsondir
# for each decision point:
for dp in REGISTERED_DECISION_POINTS:
- dump_decision_point(jsondir, outdir, dp, overwrite)
+ dump_decision_point(jsondir, dp, overwrite)
if __name__ == "__main__":
diff --git a/src/ssvc/dp_groups/__init__.py b/src/ssvc/dp_groups/__init__.py
index 90a47e3a..216dc3ea 100644
--- a/src/ssvc/dp_groups/__init__.py
+++ b/src/ssvc/dp_groups/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
diff --git a/src/ssvc/dp_groups/base.py b/src/ssvc/dp_groups/base.py
index 3c37512b..d198a0df 100644
--- a/src/ssvc/dp_groups/base.py
+++ b/src/ssvc/dp_groups/base.py
@@ -4,7 +4,7 @@
author: adh
created_at: 9/20/23 4:47 PM
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -17,23 +17,18 @@
# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
# U.S. Patent and Trademark Office by Carnegie Mellon University
-from dataclasses import dataclass
-from typing import Iterable
-
-from dataclasses_json import dataclass_json
+from pydantic import BaseModel
from ssvc._mixins import _Base, _Versioned
from ssvc.decision_points.base import SsvcDecisionPoint
-@dataclass_json
-@dataclass(kw_only=True)
-class SsvcDecisionPointGroup(_Base, _Versioned):
+class SsvcDecisionPointGroup(_Base, _Versioned, BaseModel):
"""
Models a group of decision points.
"""
- decision_points: Iterable[SsvcDecisionPoint]
+ decision_points: list[SsvcDecisionPoint]
def __iter__(self):
"""
@@ -45,11 +40,13 @@ def __len__(self):
"""
Allow len() to be called on the group.
"""
- return len(self.decision_points)
+ dplist = list(self.decision_points)
+ l = len(dplist)
+ return l
def get_all_decision_points_from(
- *groups: Iterable[SsvcDecisionPointGroup],
+ *groups: list[SsvcDecisionPointGroup],
) -> list[SsvcDecisionPoint]:
"""
Given a list of SsvcDecisionPointGroup objects, return a list of all
diff --git a/src/ssvc/dp_groups/cvss/__init__.py b/src/ssvc/dp_groups/cvss/__init__.py
index 7b9251b9..29615765 100644
--- a/src/ssvc/dp_groups/cvss/__init__.py
+++ b/src/ssvc/dp_groups/cvss/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
diff --git a/src/ssvc/dp_groups/cvss/collections.py b/src/ssvc/dp_groups/cvss/collections.py
index e7adf706..55e3c300 100644
--- a/src/ssvc/dp_groups/cvss/collections.py
+++ b/src/ssvc/dp_groups/cvss/collections.py
@@ -2,7 +2,7 @@
"""
Models CVSS vectors as SSVC decision point groups
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -28,11 +28,14 @@
ATTACK_VECTOR_3,
ATTACK_VECTOR_3_0_1,
)
-from ssvc.decision_points.cvss.authentication import AUTHENTICATION_1, AUTHENTICATION_2
+from ssvc.decision_points.cvss.authentication import (
+ AUTHENTICATION_1,
+ AUTHENTICATION_2,
+)
from ssvc.decision_points.cvss.availability_impact import (
AVAILABILITY_IMPACT_1,
AVAILABILITY_IMPACT_2,
- AVAILABILITY_IMPACT_2_0_1,
+ AVAILABILITY_IMPACT_3_0_0,
)
from ssvc.decision_points.cvss.availability_requirement import (
AVAILABILITY_REQUIREMENT_1,
@@ -46,15 +49,20 @@
from ssvc.decision_points.cvss.confidentiality_impact import (
CONFIDENTIALITY_IMPACT_1,
CONFIDENTIALITY_IMPACT_2,
- CONFIDENTIALITY_IMPACT_2_0_1,
+ CONFIDENTIALITY_IMPACT_3_0_0,
)
from ssvc.decision_points.cvss.confidentiality_requirement import (
CONFIDENTIALITY_REQUIREMENT_1,
CONFIDENTIALITY_REQUIREMENT_1_1,
CONFIDENTIALITY_REQUIREMENT_1_1_1,
)
-from ssvc.decision_points.cvss.eq_sets import EQ1, EQ2, EQ3, EQ4, EQ5, EQ6
-from ssvc.decision_points.cvss.exploitability import (
+from ssvc.decision_points.cvss.equivalence_set_1 import EQ1
+from ssvc.decision_points.cvss.equivalence_set_2 import EQ2
+from ssvc.decision_points.cvss.equivalence_set_3 import EQ3
+from ssvc.decision_points.cvss.equivalence_set_4 import EQ4
+from ssvc.decision_points.cvss.equivalence_set_5 import EQ5
+from ssvc.decision_points.cvss.equivalence_set_6 import EQ6
+from ssvc.decision_points.cvss.exploit_maturity import (
EXPLOITABILITY_1,
EXPLOITABILITY_1_1,
EXPLOIT_CODE_MATURITY_1_2,
@@ -65,7 +73,7 @@
from ssvc.decision_points.cvss.integrity_impact import (
INTEGRITY_IMPACT_1,
INTEGRITY_IMPACT_2,
- INTEGRITY_IMPACT_2_0_1,
+ INTEGRITY_IMPACT_3_0_0,
)
from ssvc.decision_points.cvss.integrity_requirement import (
INTEGRITY_REQUIREMENT_1,
@@ -96,10 +104,14 @@
SUBSEQUENT_INTEGRITY_IMPACT_1,
)
from ssvc.decision_points.cvss.supplemental.automatable import AUTOMATABLE_1
-from ssvc.decision_points.cvss.supplemental.provider_urgency import PROVIDER_URGENCY_1
+from ssvc.decision_points.cvss.supplemental.provider_urgency import (
+ PROVIDER_URGENCY_1,
+)
from ssvc.decision_points.cvss.supplemental.recovery import RECOVERY_1
from ssvc.decision_points.cvss.supplemental.safety import SAFETY_1
-from ssvc.decision_points.cvss.supplemental.value_density import VALUE_DENSITY_1
+from ssvc.decision_points.cvss.supplemental.value_density import (
+ VALUE_DENSITY_1,
+)
from ssvc.decision_points.cvss.supplemental.vulnerability_response_effort import (
VULNERABILITY_RESPONSE_EFFORT_1,
)
@@ -261,9 +273,9 @@
USER_INTERACTION_2,
]
_IMPACT_4 = [
- CONFIDENTIALITY_IMPACT_2_0_1,
- INTEGRITY_IMPACT_2_0_1,
- AVAILABILITY_IMPACT_2_0_1,
+ CONFIDENTIALITY_IMPACT_3_0_0,
+ INTEGRITY_IMPACT_3_0_0,
+ AVAILABILITY_IMPACT_3_0_0,
SUBSEQUENT_CONFIDENTIALITY_IMPACT_1,
SUBSEQUENT_INTEGRITY_IMPACT_1,
SUBSEQUENT_AVAILABILITY_IMPACT_1,
@@ -366,7 +378,7 @@ def main():
CVSSv4,
]:
print(f"## {group.name} v{group.version}")
- print(group.to_json(indent=2))
+ print(group.model_dump_json(indent=2))
print()
diff --git a/src/ssvc/dp_groups/ssvc/__init__.py b/src/ssvc/dp_groups/ssvc/__init__.py
index ebb125a0..37d9487d 100644
--- a/src/ssvc/dp_groups/ssvc/__init__.py
+++ b/src/ssvc/dp_groups/ssvc/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
diff --git a/src/ssvc/dp_groups/ssvc/collections.py b/src/ssvc/dp_groups/ssvc/collections.py
index 006d824a..c7b2b527 100644
--- a/src/ssvc/dp_groups/ssvc/collections.py
+++ b/src/ssvc/dp_groups/ssvc/collections.py
@@ -2,7 +2,7 @@
"""
Provides collections of decision points for each version of the SSVC.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -16,10 +16,19 @@
# U.S. Patent and Trademark Office by Carnegie Mellon University
-from ssvc.dp_groups.base import SsvcDecisionPointGroup, get_all_decision_points_from
-from ssvc.dp_groups.ssvc.coordinator_publication import COORDINATOR_PUBLICATION_1
+from ssvc.dp_groups.base import (
+ SsvcDecisionPointGroup,
+ get_all_decision_points_from,
+)
+from ssvc.dp_groups.ssvc.coordinator_publication import (
+ COORDINATOR_PUBLICATION_1,
+)
from ssvc.dp_groups.ssvc.coordinator_triage import COORDINATOR_TRIAGE_1
-from ssvc.dp_groups.ssvc.deployer import DEPLOYER_2, DEPLOYER_3, PATCH_APPLIER_1
+from ssvc.dp_groups.ssvc.deployer import (
+ DEPLOYER_2,
+ DEPLOYER_3,
+ PATCH_APPLIER_1,
+)
from ssvc.dp_groups.ssvc.supplier import PATCH_DEVELOPER_1, SUPPLIER_2
@@ -27,7 +36,9 @@
name="SSVCv1",
description="The first version of the SSVC.",
version="1.0.0",
- decision_points=get_all_decision_points_from(PATCH_APPLIER_1, PATCH_DEVELOPER_1),
+ decision_points=get_all_decision_points_from(
+ PATCH_APPLIER_1, PATCH_DEVELOPER_1
+ ),
)
SSVCv2 = SsvcDecisionPointGroup(
name="SSVCv2",
@@ -49,7 +60,7 @@
def main():
for dpg in [SSVCv1, SSVCv2, SSVCv2_1]:
- print(dpg.to_json(indent=2))
+ print(dpg.model_dump_json(indent=2))
if __name__ == "__main__":
diff --git a/src/ssvc/dp_groups/ssvc/coordinator_publication.py b/src/ssvc/dp_groups/ssvc/coordinator_publication.py
index 90258406..35423fd9 100644
--- a/src/ssvc/dp_groups/ssvc/coordinator_publication.py
+++ b/src/ssvc/dp_groups/ssvc/coordinator_publication.py
@@ -4,7 +4,7 @@
author: adh
created_at: 9/21/23 11:40 AM
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -45,7 +45,7 @@
def main():
- print(COORDINATOR_PUBLICATION_1.to_json(indent=2))
+ print(COORDINATOR_PUBLICATION_1.model_dump_json(indent=2))
if __name__ == "__main__":
diff --git a/src/ssvc/dp_groups/ssvc/coordinator_triage.py b/src/ssvc/dp_groups/ssvc/coordinator_triage.py
index 8a8f52e5..2fedb785 100644
--- a/src/ssvc/dp_groups/ssvc/coordinator_triage.py
+++ b/src/ssvc/dp_groups/ssvc/coordinator_triage.py
@@ -4,7 +4,7 @@
author: adh
created_at: 9/21/23 11:40 AM
"""
-# Copyright (c) 2023-2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -66,7 +66,7 @@
def main():
- print(COORDINATOR_TRIAGE_1.to_json(indent=2))
+ print(COORDINATOR_TRIAGE_1.model_dump_json(indent=2))
if __name__ == "__main__":
diff --git a/src/ssvc/dp_groups/ssvc/deployer.py b/src/ssvc/dp_groups/ssvc/deployer.py
index feb2b4b4..76218acd 100644
--- a/src/ssvc/dp_groups/ssvc/deployer.py
+++ b/src/ssvc/dp_groups/ssvc/deployer.py
@@ -5,7 +5,7 @@
created_at: 9/21/23 11:40 AM
"""
-# Copyright (c) 2023-2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -21,7 +21,10 @@
from ssvc.decision_points.automatable import AUTOMATABLE_2
from ssvc.decision_points.exploitation import EXPLOITATION_1
from ssvc.decision_points.human_impact import HUMAN_IMPACT_2
-from ssvc.decision_points.mission_impact import MISSION_IMPACT_1, MISSION_IMPACT_2
+from ssvc.decision_points.mission_impact import (
+ MISSION_IMPACT_1,
+ MISSION_IMPACT_2,
+)
from ssvc.decision_points.safety_impact import SAFETY_IMPACT_1
from ssvc.decision_points.system_exposure import (
SYSTEM_EXPOSURE_1,
@@ -121,9 +124,9 @@
def main():
- print(PATCH_APPLIER_1.to_json(indent=2))
- print(DEPLOYER_2.to_json(indent=2))
- print(DEPLOYER_3.to_json(indent=2))
+ print(PATCH_APPLIER_1.model_dump_json(indent=2))
+ print(DEPLOYER_2.model_dump_json(indent=2))
+ print(DEPLOYER_3.model_dump_json(indent=2))
if __name__ == "__main__":
diff --git a/src/ssvc/dp_groups/ssvc/supplier.py b/src/ssvc/dp_groups/ssvc/supplier.py
index b245d035..05fb092c 100644
--- a/src/ssvc/dp_groups/ssvc/supplier.py
+++ b/src/ssvc/dp_groups/ssvc/supplier.py
@@ -5,7 +5,7 @@
created_at: 9/21/23 11:41 AM
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -91,8 +91,8 @@
def main():
- print(PATCH_DEVELOPER_1.to_json(indent=2))
- print(SUPPLIER_2.to_json(indent=2))
+ print(PATCH_DEVELOPER_1.model_dump_json(indent=2))
+ print(SUPPLIER_2.model_dump_json(indent=2))
if __name__ == "__main__":
diff --git a/src/ssvc/md_gen.py b/src/ssvc/md_gen.py
new file mode 100644
index 00000000..beca4fcd
--- /dev/null
+++ b/src/ssvc/md_gen.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+"""
+file: md_gen
+author: adh
+created_at: 2/17/25 4:25 PM
+"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
+import os
+
+
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
+
+def cutfname(fname):
+ # foo_bar_1_0_0 -> foo_bar
+ parts = fname.split("_")
+ parts_ = []
+
+ for part in parts:
+ if part.isnumeric():
+ break
+ parts_.append(part)
+
+ return "_".join(parts_)
+
+
+PAGE_TOP_TEMPLATE = """# {dp_name}
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.{module} import LATEST
+from ssvc.doc_helpers import example_block
+
+print(example_block(LATEST))
+```
+"""
+
+PAGE_BOTTOM_TEMPLATE = """## Previous Versions
+
+```python exec="true" idprefix=""
+from ssvc.decision_points.cvss.{module} import VERSIONS
+from ssvc.doc_helpers import example_block
+
+versions = VERSIONS[:-1]
+for version in versions:
+ print(example_block(version))
+ print("\\n---\\n")
+```
+"""
+
+
+def snake_to_title(s):
+ return " ".join([w.capitalize() for w in s.split("_")])
+
+
+def main():
+ # add overwrite command line argument
+ import argparse
+
+ parser = argparse.ArgumentParser(
+ description="Generate decision point documentation"
+ )
+ parser.add_argument(
+ "--overwrite",
+ action="store_true",
+ help="overwrite existing files",
+ default=False,
+ )
+
+ parser.add_argument(
+ "--outdir",
+ help="output directory",
+ default="../../docs/reference/decision_points/cvss",
+ )
+
+ parser.add_argument(
+ "--jsondir",
+ help="json directory",
+ default="../../data/json/decision_points/cvss",
+ )
+
+ args = parser.parse_args()
+
+ json_dir = args.jsondir
+ json_files = sorted(os.listdir(json_dir))
+ print(json_files)
+
+ dp_fnames = sorted(list(set([cutfname(f) for f in json_files])))
+ print(dp_fnames)
+
+ md_dir = args.outdir
+ os.makedirs(md_dir, exist_ok=True)
+
+ for dp_fname in dp_fnames:
+
+ fname = dp_fname + ".md"
+ path = os.path.join(md_dir, fname)
+
+ if os.path.exists(path):
+ if not args.overwrite:
+ # skip if file already exists
+ continue
+ else:
+ os.remove(path)
+
+ # does the module exist?
+ module = f"ssvc.decision_points.cvss.{dp_fname}"
+ try:
+ # try to import the module
+ mod = __import__(module)
+ print(f"Module {mod} exists")
+ except ImportError:
+ print(f"Module {module} does not exist")
+ continue
+
+ with open(os.path.join(md_dir, fname), "w") as f:
+ f.write(
+ PAGE_TOP_TEMPLATE.format(
+ dp_name=snake_to_title(dp_fname), module=dp_fname
+ )
+ )
+ f.write("\n")
+ if len(mod.VERSIONS) > 1:
+ f.write("\n")
+ f.write(PAGE_BOTTOM_TEMPLATE.format(module=dp_fname))
+ f.write("\n")
+
+ pass
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/ssvc/outcomes/__init__.py b/src/ssvc/outcomes/__init__.py
index 063e8a7a..37d9487d 100644
--- a/src/ssvc/outcomes/__init__.py
+++ b/src/ssvc/outcomes/__init__.py
@@ -1 +1,12 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
diff --git a/src/ssvc/outcomes/base.py b/src/ssvc/outcomes/base.py
index 09235be2..11eaf873 100644
--- a/src/ssvc/outcomes/base.py
+++ b/src/ssvc/outcomes/base.py
@@ -2,7 +2,7 @@
"""
Provides outcome group and outcome value classes for SSVC.
"""
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -15,30 +15,23 @@
# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
# U.S. Patent and Trademark Office by Carnegie Mellon University
-from dataclasses import dataclass
-from typing import Iterable
+from pydantic import BaseModel
-from dataclasses_json import dataclass_json
+from ssvc._mixins import _Base, _Keyed, _Versioned
-from ssvc._mixins import _Base, _Keyed
-
-@dataclass_json
-@dataclass(kw_only=True)
-class OutcomeValue(_Base, _Keyed):
+class OutcomeValue(_Base, _Keyed, BaseModel):
"""
Models a single value option for an SSVC outcome.
"""
-@dataclass_json
-@dataclass(kw_only=True)
-class OutcomeGroup(_Base):
+class OutcomeGroup(_Base, _Versioned, BaseModel):
"""
Models an outcome group.
"""
- outcomes: Iterable[OutcomeValue]
+ outcomes: list[OutcomeValue]
def __iter__(self):
"""
@@ -50,6 +43,8 @@ def __len__(self):
"""
Allow len() to be called on the group.
"""
- return len(self.outcomes)
+ olist = list(self.outcomes)
+ l = len(olist)
+ return l
# register all instances
diff --git a/src/ssvc/outcomes/groups.py b/src/ssvc/outcomes/groups.py
index 0da720a5..5326b6d9 100644
--- a/src/ssvc/outcomes/groups.py
+++ b/src/ssvc/outcomes/groups.py
@@ -2,7 +2,7 @@
"""
Provides a set of outcome groups for use in SSVC.
"""
-# Copyright (c) 2023-2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -23,6 +23,7 @@
DSOI = OutcomeGroup(
name="Defer, Scheduled, Out-of-Cycle, Immediate",
description="The original SSVC outcome group.",
+ version="1.0.0",
outcomes=(
OutcomeValue(name="Defer", key="D", description="Defer"),
OutcomeValue(name="Scheduled", key="S", description="Scheduled"),
@@ -37,8 +38,11 @@
PUBLISH = OutcomeGroup(
name="Publish, Do Not Publish",
description="The publish outcome group.",
+ version="1.0.0",
outcomes=(
- OutcomeValue(name="Do Not Publish", key="N", description="Do Not Publish"),
+ OutcomeValue(
+ name="Do Not Publish", key="N", description="Do Not Publish"
+ ),
OutcomeValue(name="Publish", key="P", description="Publish"),
),
)
@@ -49,6 +53,7 @@
COORDINATE = OutcomeGroup(
name="Decline, Track, Coordinate",
description="The coordinate outcome group.",
+ version="1.0.0",
outcomes=(
OutcomeValue(name="Decline", key="D", description="Decline"),
OutcomeValue(name="Track", key="T", description="Track"),
@@ -62,6 +67,7 @@
MOSCOW = OutcomeGroup(
name="Must, Should, Could, Won't",
description="The Moscow outcome group.",
+ version="1.0.0",
outcomes=(
OutcomeValue(name="Won't", key="W", description="Won't"),
OutcomeValue(name="Could", key="C", description="Could"),
@@ -76,6 +82,7 @@
EISENHOWER = OutcomeGroup(
name="Do, Schedule, Delegate, Delete",
description="The Eisenhower outcome group.",
+ version="1.0.0",
outcomes=(
OutcomeValue(name="Delete", key="D", description="Delete"),
OutcomeValue(name="Delegate", key="G", description="Delegate"),
@@ -90,6 +97,7 @@
CVSS = OutcomeGroup(
name="CVSS Levels",
description="The CVSS outcome group.",
+ version="1.0.0",
outcomes=(
OutcomeValue(name="Low", key="L", description="Low"),
OutcomeValue(name="Medium", key="M", description="Medium"),
@@ -105,6 +113,7 @@
name="CISA Levels",
description="The CISA outcome group. "
"CISA uses its own SSVC decision tree model to prioritize relevant vulnerabilities into four possible decisions: Track, Track*, Attend, and Act.",
+ version="1.0.0",
outcomes=(
OutcomeValue(
name="Track",
@@ -144,6 +153,7 @@
YES_NO = OutcomeGroup(
name="Yes, No",
description="The Yes/No outcome group.",
+ version="1.0.0",
outcomes=(
OutcomeValue(name="No", key="N", description="No"),
OutcomeValue(name="Yes", key="Y", description="Yes"),
@@ -156,10 +166,13 @@
VALUE_COMPLEXITY = OutcomeGroup(
name="Value, Complexity",
description="The Value/Complexity outcome group.",
+ version="1.0.0",
outcomes=(
# drop, reconsider later, easy win, do first
OutcomeValue(name="Drop", key="D", description="Drop"),
- OutcomeValue(name="Reconsider Later", key="R", description="Reconsider Later"),
+ OutcomeValue(
+ name="Reconsider Later", key="R", description="Reconsider Later"
+ ),
OutcomeValue(name="Easy Win", key="E", description="Easy Win"),
OutcomeValue(name="Do First", key="F", description="Do First"),
),
@@ -171,9 +184,12 @@
THE_PARANOIDS = OutcomeGroup(
name="theParanoids",
description="PrioritizedRiskRemediation outcome group based on TheParanoids.",
+ version="1.0.0",
outcomes=(
OutcomeValue(name="Track 5", key="5", description="Track"),
- OutcomeValue(name="Track Closely 4", key="4", description="Track Closely"),
+ OutcomeValue(
+ name="Track Closely 4", key="4", description="Track Closely"
+ ),
OutcomeValue(name="Attend 3", key="3", description="Attend"),
OutcomeValue(name="Attend 2", key="2", description="Attend"),
OutcomeValue(name="Act 1", key="1", description="Act"),
diff --git a/src/ssvc/policy_generator.py b/src/ssvc/policy_generator.py
index b52e20b1..38e75f01 100644
--- a/src/ssvc/policy_generator.py
+++ b/src/ssvc/policy_generator.py
@@ -3,7 +3,7 @@
Provides a Policy Generator class for SSVC decision point groups.
"""
-# Copyright (c) 2023-2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -19,7 +19,6 @@
import itertools
import logging
import math
-from typing import List, Tuple
import networkx as nx
import pandas as pd
@@ -48,7 +47,7 @@ def __init__(
self,
dp_group: SsvcDecisionPointGroup = None,
outcomes: OutcomeGroup = None,
- outcome_weights: List[float] = None,
+ outcome_weights: list[float] = None,
validate: bool = False,
):
"""
@@ -87,15 +86,17 @@ def __init__(
# validate that the outcome weights sum to 1.0
total = sum(outcome_weights)
if not math.isclose(total, 1.0):
- raise ValueError(f"Outcome weights must sum to 1.0, but sum to {total}")
+ raise ValueError(
+ f"Outcome weights must sum to 1.0, but sum to {total}"
+ )
self.outcome_weights = outcome_weights
logger.debug(f"Outcome weights: {self.outcome_weights}")
self.policy: pd.DataFrame = None
self.G: nx.DiGraph = nx.DiGraph()
- self.top: Tuple[int] = None
- self.bottom: Tuple[int] = None
+ self.top: tuple[int] = None
+ self.bottom: tuple[int] = None
self._enumerated_vec = None
self._check_valid_paths = validate
@@ -203,7 +204,9 @@ def _assign_outcomes(self):
logger.debug(f"Layer count: {len(layers)}")
logger.debug(f"Layer sizes: {[len(layer) for layer in layers]}")
- outcome_counts = [round(node_count * weight) for weight in self.outcome_weights]
+ outcome_counts = [
+ round(node_count * weight) for weight in self.outcome_weights
+ ]
toposort = list(nx.topological_sort(self.G))
logger.debug(f"Toposort: {toposort[:4]}...{toposort[-4:]}")
@@ -292,11 +295,15 @@ def _confirm_topological_order(self, node_order: list) -> None:
# all nodes must be in the graph
for node in node_order:
if node not in self.G.nodes:
- raise ValueError(f"Node order contains node {node} not in the graph")
+ raise ValueError(
+ f"Node order contains node {node} not in the graph"
+ )
for node in self.G.nodes:
if node not in node_order:
- raise ValueError(f"Graph contains node {node} not in the node order")
+ raise ValueError(
+ f"Graph contains node {node} not in the node order"
+ )
node_idx = {node: i for i, node in enumerate(node_order)}
@@ -353,7 +360,9 @@ def main():
)
with PolicyGenerator(
- dp_group=dpg, outcomes=DSOI, outcome_weights=[0.097, 0.583, 0.278, 0.042]
+ dp_group=dpg,
+ outcomes=DSOI,
+ outcome_weights=[0.097, 0.583, 0.278, 0.042],
) as pg:
pg.emit_policy()
diff --git a/src/ssvc_v2.py b/src/ssvc_v2.py
index 9b73caae..01f041bf 100644
--- a/src/ssvc_v2.py
+++ b/src/ssvc_v2.py
@@ -1,38 +1,49 @@
#!/usr/bin/env python
-'''
+"""
file: ssvc_v2
author: adh
created_at: 3/23/21 3:23 PM
-'''
+"""
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
import os
+
import pandas as pd
-DATAPATH="../data/csvs"
+DATAPATH = "../data/csvs"
PATHS = {
- 'coord_pub': os.path.join(DATAPATH,"coord-publish-options.csv"),
- 'coord_triage': os.path.join(DATAPATH,"coord-triage-options.csv"),
- 'deployer': os.path.join(DATAPATH,"deployer-options.csv"),
- 'supplier': os.path.join(DATAPATH,"supplier-options.csv"),
+ "coord_pub": os.path.join(DATAPATH, "coord-publish-options.csv"),
+ "coord_triage": os.path.join(DATAPATH, "coord-triage-options.csv"),
+ "deployer": os.path.join(DATAPATH, "deployer-options.csv"),
+ "supplier": os.path.join(DATAPATH, "supplier-options.csv"),
}
DEFAULTS = {
- 'coord_pub': {
+ "coord_pub": {
# An analyst should feel comfortable selecting none if they (or their search scripts) have performed searches
# in the appropriate places for public PoCs and active exploitation (as described above) and found none.
"Exploitation": "none",
},
- 'coord_triage': {
-
- },
- 'deployer': {
+ "coord_triage": {},
+ "deployer": {
# An analyst should feel comfortable selecting none if they (or their search scripts) have performed searches
# in the appropriate places for public PoCs and active exploitation (as described above) and found none.
"Exploitation": "none",
- "Exposure": "unavoidable"
+ "Exposure": "unavoidable",
},
- 'supplier': {
+ "supplier": {
# An analyst should feel comfortable selecting none if they (or their search scripts) have performed searches
# in the appropriate places for public PoCs and active exploitation (as described above) and found none.
"Exploitation": "none",
@@ -40,28 +51,30 @@
}
# confirm that PATHS and DEFAULTS keys match
-assert(set(PATHS.keys()) == set(DEFAULTS.keys()))
+assert set(PATHS.keys()) == set(DEFAULTS.keys())
+
def _load_csvs(path_dict):
data = {}
- for key,path in path_dict.items():
+ for key, path in path_dict.items():
df = pd.read_csv(path)
data[key] = df
return data
+
DATA = _load_csvs(PATHS)
# confirm that PATHS and DATA keys match
-assert(set(PATHS.keys()) == set(DATA.keys()))
+assert set(PATHS.keys()) == set(DATA.keys())
-def lookup(key, query_dict,use_defaults=True):
+def lookup(key, query_dict, use_defaults=True):
# get the full table
df = DATA[key]
if use_defaults:
# copy the defaults before we use them
- defaults = DEFAULTS.get(key,{})
+ defaults = DEFAULTS.get(key, {})
q = dict(defaults)
else:
q = {}
@@ -69,21 +82,22 @@ def lookup(key, query_dict,use_defaults=True):
q.update(query_dict)
# with each pass, slice the table
- for k,v in q.items():
+ for k, v in q.items():
df = df.loc[df[k] == v]
return df
-def outcome_dist(df,normalize=True):
- '''
+
+def outcome_dist(df, normalize=True):
+ """
Given a dataframe representing an SSVC tree fragment,
compute and return the distribution of outcomes
- '''
- return df['Priority'].value_counts(normalize=normalize)
+ """
+ return df["Priority"].value_counts(normalize=normalize)
def main():
- for key,df in DATA.items():
+ for key, df in DATA.items():
print(key, df.columns)
print()
@@ -91,24 +105,25 @@ def main():
"Utility": "laborious",
"Public_Safety_Impact": "minimal",
}
- df = lookup('coord_triage',query)
+ df = lookup("coord_triage", query)
print(query)
print(df)
- print(outcome_dist(df).round(decimals=3).to_dict())
+ print(outcome_dist(df).round(decimals=3).model_dump())
print()
- query = {'Value added': "precedence"}
- df = lookup('coord_pub',query)
+ query = {"Value added": "precedence"}
+ df = lookup("coord_pub", query)
print(query)
print(df)
- print(outcome_dist(df).round(decimals=3).to_dict())
+ print(outcome_dist(df).round(decimals=3).model_dump())
print()
query = {"Public-Safety Impact": "minimal"}
- df = lookup('supplier',query)
+ df = lookup("supplier", query)
print(query)
print(df)
- print(outcome_dist(df).round(decimals=3).to_dict())
+ print(outcome_dist(df).round(decimals=3).model_dump())
+
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
diff --git a/src/test/__init__.py b/src/test/__init__.py
index cf0a9163..35c6fca4 100644
--- a/src/test/__init__.py
+++ b/src/test/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
diff --git a/src/test/test_csv_analyzer.py b/src/test/test_csv_analyzer.py
index b010367a..81be7572 100644
--- a/src/test/test_csv_analyzer.py
+++ b/src/test/test_csv_analyzer.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023-2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -26,7 +26,8 @@ def test_col_norm(self):
# fold to lowercase
self.assertEqual(acsv._col_norm("Exploitation"), "exploitation")
self.assertEqual(
- acsv._col_norm("AbcdEfghIjklmnOpqrstUvwxYz"), "abcdefghijklmnopqrstuvwxyz"
+ acsv._col_norm("AbcdEfghIjklmnOpqrstUvwxYz"),
+ "abcdefghijklmnopqrstuvwxyz",
)
# replace strings of non-alphanumeric characters with underscores
@@ -40,7 +41,12 @@ def test_imp_df(self):
# imp_df should return a dataframe with the column names and feature importances
# sorted in descending order by feature importance
- column_names = ["exploitation", "human_impact", "automatable", "exposure"]
+ column_names = [
+ "exploitation",
+ "human_impact",
+ "automatable",
+ "exposure",
+ ]
importances = [0.347222, 0.291667, 0.180556, 0.166667]
df = acsv._imp_df(column_names, importances)
self.assertEqual(df["feature"][0], "exploitation")
@@ -81,7 +87,9 @@ def test_drop_col_feat_imp(self):
)
def test_split_data(self):
- df = pd.DataFrame({"A": [1, 2, 3, 4], "B": [5, 6, 7, 8], "C": [9, 10, 11, 12]})
+ df = pd.DataFrame(
+ {"A": [1, 2, 3, 4], "B": [5, 6, 7, 8], "C": [9, 10, 11, 12]}
+ )
x, y = acsv._split_data(df, "C")
self.assertTrue(x.equals(df.drop("C", axis=1)))
diff --git a/src/test/test_cvss_helpers.py b/src/test/test_cvss_helpers.py
index 70f0fb4b..a5dd413e 100644
--- a/src/test/test_cvss_helpers.py
+++ b/src/test/test_cvss_helpers.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
diff --git a/src/test/test_doc_helpers.py b/src/test/test_doc_helpers.py
new file mode 100644
index 00000000..76b217f4
--- /dev/null
+++ b/src/test/test_doc_helpers.py
@@ -0,0 +1,85 @@
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
+import unittest
+
+from ssvc.decision_points import SsvcDecisionPoint, SsvcDecisionPointValue
+from ssvc.doc_helpers import example_block, markdown_table
+
+
+class MyTestCase(unittest.TestCase):
+ def setUp(self):
+ self.dp = SsvcDecisionPoint(
+ namespace="test",
+ name="test name",
+ description="test description",
+ key="TK",
+ version="1.0.0",
+ values=(
+ SsvcDecisionPointValue(
+ name="A", key="A", description="A Definition"
+ ),
+ SsvcDecisionPointValue(
+ name="B", key="B", description="B Definition"
+ ),
+ ),
+ )
+
+ def tearDown(self):
+ pass
+
+ def test_markdown_table(self):
+ result = markdown_table(self.dp)
+
+ expected = (
+ "test description\n"
+ "\n"
+ "| Value | Definition |\n"
+ "|:-----|:-----------|\n"
+ "| A | A Definition |\n"
+ "| B | B Definition |"
+ )
+
+ self.assertEqual(result, expected)
+
+ indented = markdown_table(self.dp, indent=4)
+
+ expected_indented = (
+ " test description\n"
+ "\n"
+ " | Value | Definition |\n"
+ " |:-----|:-----------|\n"
+ " | A | A Definition |\n"
+ " | B | B Definition |"
+ )
+
+ self.assertEqual(indented, expected_indented)
+
+ def test_example_block(self):
+
+ result = example_block(self.dp)
+
+ self.assertIn("!!! note", result)
+ self.assertIn("\n | Value | Definition |", result)
+ self.assertIn("\n | A | A Definition |", result)
+ self.assertIn("\n | B | B Definition |", result)
+ self.assertIn("\n ??? example", result)
+ self.assertIn("\n ```json", result)
+
+ for value in self.dp.values:
+ self.assertIn(value.name, result)
+ self.assertIn(value.description, result)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/test/test_doctools.py b/src/test/test_doctools.py
index a41095cd..c59226a5 100644
--- a/src/test/test_doctools.py
+++ b/src/test/test_doctools.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -22,9 +22,7 @@
_filename_friendly,
dump_decision_point,
dump_json,
- dump_markdown,
remove_if_exists,
- to_markdown_table,
)
_dp_dict = {
@@ -42,7 +40,7 @@
class MyTestCase(unittest.TestCase):
def setUp(self) -> None:
- self.dp = SsvcDecisionPoint.from_dict(_dp_dict)
+ self.dp = SsvcDecisionPoint.model_validate(_dp_dict)
# create a temp working dir
self.tempdir = tempfile.TemporaryDirectory()
@@ -60,18 +58,6 @@ def test__filename_friendly(self):
# lowercase the string
self.assertEqual("foo_bar", _filename_friendly("Foo.Bar"))
- def test_to_markdown_table(self):
- dp = self.dp
-
- table = to_markdown_table(dp)
- self.assertIn(dp.description, table)
- # self.assertIn(dp.name, table)
- # self.assertIn(dp.version, table)
- for value in dp.values:
- self.assertIn(value.name, table)
- self.assertIn(value.description, table)
- self.assertIn(value.key, table)
-
def test_ensure_dir_exists(self):
path = os.path.join(self.tempdir.name, "foo")
self.assertFalse(os.path.exists(path))
@@ -98,76 +84,26 @@ def test_remove_if_exists(self):
def test_dump_decision_point(self):
jsondir = os.path.join(self.tempdir.name, "json")
- outdir = os.path.join(self.tempdir.name, "out")
dp = self.dp
overwrite = False
+ # should create the files in the expected places
+ self.assertFalse(os.path.exists(jsondir))
self.assertEqual(0, len(os.listdir(self.tempdir.name)))
- # should create the files in the expected places
- r = dump_decision_point(jsondir, outdir, dp, overwrite)
- self.assertTrue(os.path.exists(r["include_file"]))
- self.assertTrue(os.path.exists(r["symlink"]))
- self.assertTrue(os.path.exists(r["json_file"]))
+ r = dump_decision_point(jsondir, dp, overwrite)
- # not checking these thoroughly, just making sure they are there
- # because they are tested elsewhere in dump_markdown and dump_json
+ self.assertTrue(os.path.exists(jsondir))
+ self.assertIn("json", os.listdir(self.tempdir.name))
+ self.assertEqual(1, len(os.listdir(jsondir)))
- def test_dump_markdown(self):
- # dump_markdown should create a file, write to it, and then create a generic symlink
- basename = "foo"
- dp = self.dp
- json_file = os.path.join(self.tempdir.name, f"{basename}.json")
- outdir = self.tempdir.name
- overwrite = False
+ file_created = os.listdir(jsondir)[0]
- # should create the file in the expected place
- include_file = os.path.join(outdir, f"{basename}.md")
- symlink = os.path.join(outdir, f"{_filename_friendly(dp.name)}.md")
-
- self.assertFalse(os.path.exists(include_file))
- self.assertFalse(os.path.exists(symlink))
- r = dump_markdown(basename, dp, json_file, outdir, overwrite)
- self.assertTrue(os.path.exists(include_file))
-
- self.assertEqual(include_file, r["include_file"])
- self.assertEqual(symlink, r["symlink"])
-
- # the file contains text based on the dp
- with open(include_file, "r") as f:
- text = f.read()
-
- self.assertIn(dp.description, text)
- self.assertIn(dp.name, text)
- self.assertIn(dp.version, text)
- for value in dp.values:
- self.assertIn(value.name, text)
- self.assertIn(value.description, text)
- self.assertIn(value.key, text)
-
- # should create the symlink in the expected place
- self.assertTrue(os.path.exists(symlink), symlink)
- # should be a symlink
- self.assertTrue(os.path.islink(symlink))
- # should point to the include file
- self.assertEqual(os.path.realpath(symlink), os.path.realpath(include_file))
+ for word in dp.name.split():
+ self.assertIn(word.lower(), file_created)
- # should not overwrite the file
- overwrite = False
- # capture logger output
- with self.assertLogs() as cm:
- dump_markdown(basename, dp, json_file, outdir, overwrite)
- # logger warns that the file exists
- self.assertIn("already exists", cm.output[0])
-
- # should overwrite the file
- overwrite = True
- dp.name = "Different Decision Point"
- # capture logger output
- with self.assertLogs(level=logging.DEBUG) as cm:
- dump_markdown(basename, dp, json_file, outdir, overwrite)
- # logger warns that the file was removed
- self.assertIn("Removed", cm.output[0])
+ # not checking these thoroughly, just making sure they are there
+ # because they are tested elsewhere in dump_markdown and dump_json
def test_dump_json(self):
basename = "foo"
@@ -185,7 +121,7 @@ def test_dump_json(self):
# file is loadable json
d = json.load(open(json_file))
- for k, v in dp.to_dict().items():
+ for k, v in dp.model_dump().items():
self.assertEqual(v, d[k])
# should not overwrite the file
diff --git a/src/test/test_dp_base.py b/src/test/test_dp_base.py
index 7d0214d4..c6b580e6 100644
--- a/src/test/test_dp_base.py
+++ b/src/test/test_dp_base.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -34,7 +34,7 @@ def setUp(self) -> None:
key="bar",
description="baz",
version="1.0.0",
- namespace="ns",
+ namespace="name1",
values=tuple(self.values),
)
@@ -90,30 +90,30 @@ def test_ssvc_decision_point(self):
self.assertEqual(obj.key, "bar")
self.assertEqual(obj.description, "baz")
self.assertEqual(obj.version, "1.0.0")
- self.assertEqual(obj.namespace, "ns")
+ self.assertEqual(obj.namespace, "name1")
self.assertEqual(len(self.values), len(obj.values))
def test_ssvc_value_json_roundtrip(self):
for i, obj in enumerate(self.values):
- json = obj.to_json()
+ json = obj.model_dump_json()
self.assertIsInstance(json, str)
self.assertGreater(len(json), 0)
- obj2 = base.SsvcDecisionPointValue.from_json(json)
+ obj2 = base.SsvcDecisionPointValue.model_validate_json(json)
self.assertEqual(obj, obj2)
def test_ssvc_decision_point_json_roundtrip(self):
obj = self.dp
- json = obj.to_json()
+ json = obj.model_dump_json()
self.assertIsInstance(json, str)
self.assertGreater(len(json), 0)
- obj2 = base.SsvcDecisionPoint.from_json(json)
+ obj2 = base.SsvcDecisionPoint.model_validate_json(json)
# the objects should be equal
self.assertEqual(obj, obj2)
- self.assertEqual(obj.to_dict(), obj2.to_dict())
+ self.assertEqual(obj.model_dump(), obj2.model_dump())
if __name__ == "__main__":
diff --git a/src/test/test_dp_groups.py b/src/test/test_dp_groups.py
index 08c48b29..e4c2397e 100644
--- a/src/test/test_dp_groups.py
+++ b/src/test/test_dp_groups.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -27,9 +27,15 @@ def setUp(self) -> None:
description=f"Description of Decision Point {i}",
version="1.0.0",
values=(
- SsvcDecisionPointValue(name="foo", key="FOO", description="foo"),
- SsvcDecisionPointValue(name="bar", key="BAR", description="bar"),
- SsvcDecisionPointValue(name="baz", key="BAZ", description="baz"),
+ SsvcDecisionPointValue(
+ name="foo", key="FOO", description="foo"
+ ),
+ SsvcDecisionPointValue(
+ name="bar", key="BAR", description="bar"
+ ),
+ SsvcDecisionPointValue(
+ name="baz", key="BAZ", description="baz"
+ ),
),
)
self.dps.append(dp)
@@ -40,7 +46,9 @@ def tearDown(self) -> None:
def test_iter(self):
# add them to a decision point group
g = dpg.SsvcDecisionPointGroup(
- name="Test Group", description="Test Group", decision_points=self.dps
+ name="Test Group",
+ description="Test Group",
+ decision_points=self.dps,
)
self.assertTrue(hasattr(g, "__iter__"))
@@ -52,25 +60,30 @@ def test_iter(self):
def test_len(self):
# add them to a decision point group
g = dpg.SsvcDecisionPointGroup(
- name="Test Group", description="Test Group", decision_points=self.dps
+ name="Test Group",
+ description="Test Group",
+ decision_points=self.dps,
)
- self.assertEqual(len(self.dps), len(g.decision_points))
+ self.assertGreater(len(self.dps), 0)
+ self.assertEqual(len(self.dps), len(list(g.decision_points)))
self.assertEqual(len(self.dps), len(g))
def test_json_roundtrip(self):
# add them to a decision point group
g = dpg.SsvcDecisionPointGroup(
- name="Test Group", description="Test Group", decision_points=self.dps
+ name="Test Group",
+ description="Test Group",
+ decision_points=self.dps,
)
# serialize the group to json
- g_json = g.to_json()
+ g_json = g.model_dump_json()
# deserialize the json to a new group
- g2 = dpg.SsvcDecisionPointGroup.from_json(g_json)
+ g2 = dpg.SsvcDecisionPointGroup.model_validate_json(g_json)
# assert that the new group is the same as the old group
- self.assertEqual(g.to_dict(), g2.to_dict())
+ self.assertEqual(g_json, g2.model_dump_json())
if __name__ == "__main__":
diff --git a/src/test/test_dp_helpers.py b/src/test/test_dp_helpers.py
index 97e06046..3502419c 100644
--- a/src/test/test_dp_helpers.py
+++ b/src/test/test_dp_helpers.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
diff --git a/src/test/test_mixins.py b/src/test/test_mixins.py
index 53e7c517..f86ae5c1 100644
--- a/src/test/test_mixins.py
+++ b/src/test/test_mixins.py
@@ -1,9 +1,21 @@
+# Copyright (c) 2025 Carnegie Mellon University and Contributors.
+# - see Contributors.md for a full list of Contributors
+# - see ContributionInstructions.md for information on how you can Contribute to this project
+# Stakeholder Specific Vulnerability Categorization (SSVC) is
+# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed
+# with this Software or contact permission@sei.cmu.edu for full terms.
+# Created, in part, with funding and support from the United States Government
+# (see Acknowledgments file). This program may include and/or can make use of
+# certain third party source code, object code, documentation and other files
+# (“Third Party Software”). See LICENSE.md for more details.
+# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the
+# U.S. Patent and Trademark Office by Carnegie Mellon University
+
import unittest
-from dataclasses import dataclass
-from dataclasses_json import dataclass_json
+from pydantic import BaseModel, ValidationError
-from ssvc._mixins import _Base, _Keyed, _Versioned, _Namespaced
+from ssvc._mixins import _Base, _Keyed, _Namespaced, _Versioned
class TestMixins(unittest.TestCase):
@@ -16,34 +28,33 @@ def test_ssvc_base_create(self):
self.assertEqual(obj.description, "baz")
# empty
- self.assertRaises(TypeError, _Base)
+ self.assertRaises(ValidationError, _Base)
# no name
- self.assertRaises(TypeError, _Base, description="baz")
+ self.assertRaises(ValidationError, _Base, description="baz")
# no description
- self.assertRaises(TypeError, _Base, name="foo")
+ self.assertRaises(ValidationError, _Base, name="foo")
def test_json_roundtrip(self):
obj = self.obj
- json = obj.to_json()
+ json = obj.model_dump_json()
# is it a string?
self.assertIsInstance(json, str)
# does it look right?
- self.assertEqual(json, '{"name": "foo", "description": "baz"}')
+ self.assertEqual(json, '{"name":"foo","description":"baz"}')
# modify the raw json string
json = json.replace("foo", "quux")
- self.assertEqual(json, '{"name": "quux", "description": "baz"}')
+ self.assertEqual(json, '{"name":"quux","description":"baz"}')
# does it load?
- obj2 = _Base.from_json(json)
+ obj2 = _Base.model_validate_json(json)
self.assertEqual(obj2.name, "quux")
self.assertEqual(obj2.description, "baz")
def test_asdict_roundtrip(self):
- from dataclasses import asdict
obj = self.obj
- d = asdict(obj)
+ d = obj.model_dump()
self.assertIsInstance(d, dict)
self.assertEqual(d["name"], "foo")
@@ -75,16 +86,26 @@ def test_keyed_create(self):
obj = _Keyed(key="foo")
self.assertEqual(obj.key, "foo")
- self.assertRaises(TypeError, _Keyed)
+ self.assertRaises(ValidationError, _Keyed)
def test_mixin_combos(self):
# We need to test all the combinations
mixins = [
{"class": _Keyed, "args": {"key": "fizz"}, "has_default": False},
- {"class": _Namespaced, "args": {"namespace": "buzz"}, "has_default": True},
- {"class": _Versioned, "args": {"version": "1.2.3"}, "has_default": True},
+ {
+ "class": _Namespaced,
+ "args": {"namespace": "buzz"},
+ "has_default": True,
+ },
+ {
+ "class": _Versioned,
+ "args": {"version": "1.2.3"},
+ "has_default": True,
+ },
+ ]
+ keys_with_defaults = [
+ x["args"].keys() for x in mixins if x["has_default"]
]
- keys_with_defaults = [x["args"].keys() for x in mixins if x["has_default"]]
# flatten the list
keys_with_defaults = [
item for sublist in keys_with_defaults for item in sublist
@@ -99,9 +120,7 @@ def test_mixin_combos(self):
args = {k: v for x in combo for k, v in x["args"].items()}
# create an object with the mixins
- @dataclass_json
- @dataclass(kw_only=True)
- class Foo(_Base, *classes):
+ class Foo(_Base, *classes, BaseModel):
pass
# make sure it breaks if we leave out a required arg
@@ -113,10 +132,14 @@ class Foo(_Base, *classes):
# expect success
obj = Foo(name="foo", description="baz", **args_copy)
# make sure the key is defaulted
- self.assertEqual(getattr(Foo, k), getattr(obj, k))
+ self.assertIsNotNone(getattr(obj, k))
else:
self.assertRaises(
- TypeError, Foo, name="foo", description="baz", **args_copy
+ ValidationError,
+ Foo,
+ name="foo",
+ description="baz",
+ **args_copy,
)
# instantiate the object
@@ -128,19 +151,19 @@ class Foo(_Base, *classes):
self.assertEqual(getattr(obj, k), v)
# test json roundtrip
- json = obj.to_json()
+ json = obj.model_dump_json()
# is it a string?
self.assertIsInstance(json, str)
# does it look right?
- self.assertIn('"name": "foo"', json)
- self.assertIn('"description": "baz"', json)
+ self.assertIn('"name":"foo"', json)
+ self.assertIn('"description":"baz"', json)
for k, v in args.items():
- self.assertIn(f'"{k}": "{v}"', json)
+ self.assertIn(f'"{k}":"{v}"', json)
# change the name and description
json = json.replace("foo", "quux")
json = json.replace("baz", "fizz")
# does it load?
- obj2 = Foo.from_json(json)
+ obj2 = Foo.model_validate_json(json)
self.assertEqual(obj2.name, "quux")
self.assertEqual(obj2.description, "fizz")
# make sure the args are set
diff --git a/src/test/test_outcomes.py b/src/test/test_outcomes.py
index 3645c8b1..4f5738e9 100644
--- a/src/test/test_outcomes.py
+++ b/src/test/test_outcomes.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -27,8 +27,6 @@ def test_outcome_value(self):
self.assertEqual(ov.description, x)
def test_outcome_group(self):
- ALPHABET
-
values = []
for x in ALPHABET:
values.append(OutcomeValue(key=x, name=x, description=x))
@@ -42,10 +40,11 @@ def test_outcome_group(self):
self.assertEqual(len(og), len(ALPHABET))
+ og_outcomes = list(og.outcomes)
for i, letter in enumerate(ALPHABET):
- self.assertEqual(og.outcomes[i].key, letter)
- self.assertEqual(og.outcomes[i].name, letter)
- self.assertEqual(og.outcomes[i].description, letter)
+ self.assertEqual(og_outcomes[i].key, letter)
+ self.assertEqual(og_outcomes[i].name, letter)
+ self.assertEqual(og_outcomes[i].description, letter)
if __name__ == "__main__":
diff --git a/src/test/test_policy_generator.py b/src/test/test_policy_generator.py
index bce92cf4..18162550 100644
--- a/src/test/test_policy_generator.py
+++ b/src/test/test_policy_generator.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023-2024 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -34,7 +34,8 @@ def setUp(self) -> None:
name="test",
description="test",
outcomes=[
- OutcomeValue(key=c, name=c, description=c) for c in self.og_names
+ OutcomeValue(key=c, name=c, description=c)
+ for c in self.og_names
],
)
self.dpg = SsvcDecisionPointGroup(
@@ -317,8 +318,12 @@ def test_confirm_topological_order(self):
self.assertIsNone(pg._confirm_topological_order([0, 1, 2, 3, 4, 5]))
self.assertIsNone(pg._confirm_topological_order([0, 1, 3, 2, 4, 5]))
- self.assertRaises(ValueError, pg._confirm_topological_order, [0, 1, 2, 4, 3, 5])
- self.assertRaises(ValueError, pg._confirm_topological_order, [0, 1, 2, 3, 5])
+ self.assertRaises(
+ ValueError, pg._confirm_topological_order, [0, 1, 2, 4, 3, 5]
+ )
+ self.assertRaises(
+ ValueError, pg._confirm_topological_order, [0, 1, 2, 3, 5]
+ )
if __name__ == "__main__":
diff --git a/src/test/test_schema.py b/src/test/test_schema.py
index 4fb346fb..a8835bf8 100644
--- a/src/test/test_schema.py
+++ b/src/test/test_schema.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Carnegie Mellon University and Contributors.
+# Copyright (c) 2023-2025 Carnegie Mellon University and Contributors.
# - see Contributors.md for a full list of Contributors
# - see ContributionInstructions.md for information on how you can Contribute to this project
# Stakeholder Specific Vulnerability Categorization (SSVC) is
@@ -13,12 +13,12 @@
import json
import logging
+import os
import unittest
import jsonschema
from jsonschema import Draft202012Validator
from referencing import Registry, Resource
-import os
import ssvc.decision_points # noqa F401
from ssvc.decision_points.base import REGISTERED_DECISION_POINTS
@@ -27,19 +27,34 @@
from ssvc.decision_points.critical_software import CRITICAL_SOFTWARE_1 # noqa
from ssvc.decision_points.high_value_asset import HIGH_VALUE_ASSET_1 # noqa
from ssvc.decision_points.in_kev import IN_KEV_1
-from ssvc.dp_groups.cvss.collections import CVSSv1, CVSSv2, CVSSv3, CVSSv4 # noqa
+from ssvc.dp_groups.cvss.collections import (
+ CVSSv1,
+ CVSSv2,
+ CVSSv3,
+ CVSSv4,
+) # noqa
# importing these causes the decision points to register themselves
from ssvc.dp_groups.ssvc.collections import SSVCv1, SSVCv2, SSVCv2_1 # noqa
-def retrieve_local(uri):
- fileuri = uri.replace("https://certcc.github.io/SSVC", os.getcwd())
- if os.path.exists(fileuri):
- fh = open(fileuri)
+
+def retrieve_local(uri: str) -> Resource:
+ # retrieve_local gets called anytime we're trying to get a schema.
+ # Because our schemas refer to each other by https: uris, we need this function
+ # to load the schema from a local file instead of trying to download it from the internet
+
+ # here we compute the path to the data directory where the schemas are stored
+ my_file_path = os.path.abspath(__file__)
+ my_dir = os.path.dirname(my_file_path)
+ data_path = os.path.join(my_dir, "..", "..", "data")
+ data_path = os.path.abspath(data_path)
+
+ fileuri = uri.replace("https://certcc.github.io/SSVC/data", data_path)
+
+ with open(fileuri) as fh:
schema = json.load(fh)
- fh.close()
- return Resource.from_contents(schema)
- raise FileNotFoundError(f"Could not find DEBUG path issues {fileuri}")
+ return Resource.from_contents(schema)
+
registry = Registry(retrieve=retrieve_local)
@@ -75,15 +90,19 @@ def test_decision_point_validation(self):
for dp in decision_points:
exp = None
- as_json = dp.to_json()
+ as_json = dp.model_dump_json()
loaded = json.loads(as_json)
try:
- Draft202012Validator({"$ref": schema_url}, registry=registry).validate(loaded)
+ Draft202012Validator(
+ {"$ref": schema_url}, registry=registry
+ ).validate(loaded)
except jsonschema.exceptions.ValidationError as e:
exp = e
- self.assertIsNone(exp, f"Validation failed for {dp.name} {dp.version}")
+ self.assertIsNone(
+ exp, f"Validation failed for {dp.name} {dp.version}"
+ )
self.logger.debug(
f"Validation passed for Decision Point ({dp.namespace}) {dp.name} v{dp.version}"
)
@@ -92,16 +111,27 @@ def test_decision_point_group_validation(self):
schema_url = "https://certcc.github.io/SSVC/data/schema/current/Decision_Point_Group.schema.json"
for dpg in self.dpgs:
exp = None
- as_json = dpg.to_json()
+ as_json = dpg.model_dump_json()
loaded = json.loads(as_json)
try:
- Draft202012Validator({"$ref": schema_url},registry=registry).validate(loaded)
+ Draft202012Validator(
+ {"$ref": schema_url}, registry=registry
+ ).validate(loaded)
except jsonschema.exceptions.ValidationError as e:
exp = e
- self.assertIsNone(exp, f"Validation failed for {dpg.name} {dpg.version}")
- self.logger.debug(f"Validation passed for Decision Point Group {dpg.name} v{dpg.version}")
+ self.assertIsNone(
+ exp, f"Validation failed for {dpg.name} {dpg.version}"
+ )
+ self.logger.debug(
+ f"Validation passed for Decision Point Group {dpg.name} v{dpg.version}"
+ )
+
+ @unittest.skip("Test not implemented")
+ def test_outcome_group_schema_validation(self):
+ # TODO: Implement test
+ self.fail()
if __name__ == "__main__":