diff --git a/data/schema/Decision_Point.schema.json b/data/schema/Decision_Point.schema.json new file mode 100644 index 00000000..eef6d770 --- /dev/null +++ b/data/schema/Decision_Point.schema.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Decision Point schema definition", + "$id": "https://certcc.github.io/SSVC/docs/reference/schema/Decision_Point.schema.json", + "description": "Decision points are the basic building blocks of SSVC decision functions. Individual decision points describe a single aspect of the input to a decision function.", + "type": "object", + "additionalProperties": false, + "properties": { + "namespace": { + "type": "string", + "description": "Namespace (a short, unique string): For example, \"ssvc\" or \"cvss\" to indicate the source of the decision point" + }, + "version": { + "type": "string", + "description": "Version (a semantic version string) that identifies this object" + }, + "key": { + "type": "string", + "description": "A key (a short, unique string) that can be used to identify the Decision Point/Decision Point value in a shorthand way" + }, + "name": { + "type": "string", + "description": "A short label that captures the description of the Decision Point or the Group of Decision Points." + }, + "description": { + "type": "string", + "description": "Description of the Decision Point or the Group of Decision Points." + }, + "values": { + "description": "Decision Point Values are valid results from a Decision Point", + "uniqueItems": true, + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "A key (a short, unique string) that can be used to identify the Decision Point/Decision Point value in a shorthand way" + }, + "name": { + "type": "string", + "description": "A short label that captures the description of the Decision Point or the Group of Decision Points." + }, + "description": { + "type": "string", + "description": "Description of the Decision Point or the Group of Decision Points." + } + } + } + } + }, + "required": [ + "namespace", + "version", + "key", + "name", + "description", + "values" + ] +} diff --git a/data/schema/Decision_Point_Group.schema.json b/data/schema/Decision_Point_Group.schema.json new file mode 100644 index 00000000..47f43e29 --- /dev/null +++ b/data/schema/Decision_Point_Group.schema.json @@ -0,0 +1,79 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Decision Points Group schema definition", + "$id": "https://certcc.github.io/SSVC/docs/reference/schema/Decision_Point_Group.schema.json", + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "type": "string", + "description": "Version (a semantic version string) that identifies this object" + }, + "name": { + "type": "string", + "description": "A short label that captures the description of the Decision Point or the Group of Decision Points." + }, + "description": { + "type": "string", + "description": "Description of the Decision Point or the Group of Decision Points." + }, + "decision_points": { + "description": "Decision points are the basic building blocks of SSVC decision functions. Individual decision points describe a single aspect of the input to a decision function.", + "additionalProperties": false, + "type": "array", + "items": { + "type": "object", + "properties": { + "namespace": { + "type": "string", + "description": "Namespace (a short, unique string): For example, \"ssvc\" or \"cvss\" to indicate the source of the decision point" + }, + "version": { + "type": "string", + "description": "Version (a semantic version string) that identifies this object" + }, + "key": { + "type": "string", + "description": "A key (a short, unique string) that can be used to identify the Decision Point/Decision Point value in a shorthand way" + }, + "name": { + "type": "string", + "description": "A short label that captures the description of the Decision Point or the Group of Decision Points." + }, + "description": { + "type": "string", + "description": "Description of the Decision Point or the Group of Decision Points." + }, + "values": { + "description": "Decision Point Values are valid results from a Decision Point", + "uniqueItems": true, + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "A key (a short, unique string) that can be used to identify the Decision Point/Decision Point value in a shorthand way" + }, + "name": { + "type": "string", + "description": "A short label that captures the description of the Decision Point or the Group of Decision Points." + }, + "description": { + "type": "string", + "description": "Description of the Decision Point or the Group of Decision Points." + } + } + } + } + } + } + } + }, + "required": [ + "version", + "name", + "description", + "decision_points" + ] +} diff --git a/docs/reference/code/analyze_csv.md b/docs/reference/code/analyze_csv.md index bb886969..1f47e1ab 100644 --- a/docs/reference/code/analyze_csv.md +++ b/docs/reference/code/analyze_csv.md @@ -1,2 +1,4 @@ -::: analyze_csv +# SSVC CSV Analyzer + +::: ssvc.csv_analyzer diff --git a/docs/reference/decision_points/automatable.md b/docs/reference/decision_points/automatable.md index 334b7f6d..53b090f8 100644 --- a/docs/reference/decision_points/automatable.md +++ b/docs/reference/decision_points/automatable.md @@ -1,36 +1,21 @@ !!! note "Automatable" - === "Text" - Can an attacker reliably automate creating exploitation events for this vulnerability? - - | Value | Definition | - | :--- | :---------- | - | no | Attackers cannot reliably automate steps 1-4 of the kill chain [@hutchins2011intelligence] for this vulnerability. | - | yes | Attackers can reliably automate steps 1-4 of the kill chain. | + === "Text" + {% include-markdown "../examples/automatable.md" %} === "JSON" - This is a made up example and does not reflect the correct format. - The actual JSON should be generated by the tooling, and this content can be replaced by - an include of the generated JSON. - ```json - { - key: "A", - values = [ - { - value: "no" - description: "Attackers cannot reliably automate steps 1-4 of the kill chain for this vulnerability." - }, - { - value: "yes" - description: "Attackers can reliably automate steps 1-4 of the kill chain." - } - ], - } + {% include-markdown "../examples/automatable.json" %} + ``` + === "Python" + ```python + {% include-markdown "../../../src/ssvc/decision_points/automatable.py" %} ``` + + [*Automatable*](#automatable) captures the answer to the question “Can an attacker reliably automate creating exploitation events for this vulnerability?” !!! question "What are Steps 1-4 of the Kill Chain?" @@ -78,3 +63,4 @@ Due to vulnerability chaining, there is some nuance as to whether reconnaissance Furthermore, discovery of a vulnerable service is not automatable in a situation where only two hosts are misconfigured to expose the service out of 2 million hosts that are properly configured. As discussed in in [Reasoning Steps Forward](#reasoning-steps-forward), the analyst should consider *credible* effects based on *known* use cases of the software system to be pragmatic about scope and providing values to decision points. + diff --git a/docs/reference/decision_points/exploitation.md b/docs/reference/decision_points/exploitation.md index a25d2e6b..2569adef 100644 --- a/docs/reference/decision_points/exploitation.md +++ b/docs/reference/decision_points/exploitation.md @@ -1,12 +1,24 @@ !!! note "Exploitation" - Evidence of Active Exploitation of a Vulnerability + === "Text" + {% include-markdown "../examples/exploitation.md" %} + === "JSON" + ```json + {% include-markdown "../examples/exploitation.json" %} + ``` + === "Python" + ```python + {% include-markdown "../../../src/ssvc/decision_points/exploitation.py" %} + ``` + +!!! example + + Some examples of condition (3) include + + - open-source web proxies serve as the PoC code for how to exploit any vulnerability in the vein of improper validation of TLS certificates + - Wireshark serves as a PoC for packet replay attacks on ethernet or WiFi networks. + - A publicly-known hard-coded or default password would also meet this criteria. - | Value | Definition | - | :--- |:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| - | None | There is no evidence of active exploitation and no public proof of concept (PoC) of how to exploit the vulnerability. | - | PoC
(Proof of Concept) | One of the following cases is true: (1) exploit code is sold or traded on underground or restricted fora; (2) a typical public PoC in places such as Metasploit or ExploitDB; or (3) the vulnerability has a well-known method of exploitation. Some examples of condition (3) are open-source web proxies serve as the PoC code for how to exploit any vulnerability in the vein of improper validation of TLS certificates. As another example, Wireshark serves as a PoC for packet replay attacks on ethernet or WiFi networks. A publicly-known hard-coded or default password would also meet this criteria. | - | Active | Shared, observable, reliable evidence that the exploit is being used in the wild by real attackers; there is credible public reporting. | 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]. diff --git a/docs/reference/decision_points/mission_impact.md b/docs/reference/decision_points/mission_impact.md index 02764ff4..d55c7303 100644 --- a/docs/reference/decision_points/mission_impact.md +++ b/docs/reference/decision_points/mission_impact.md @@ -2,14 +2,23 @@ !!! note "Mission Impact" - Impact on Mission Essential Functions of the Organization - - | Value | Description | - | :--- | :---------- | - | Degraded | Little to no impact up to degradation of non-essential functions; chronic degradation would eventually harm essential functions | - | MEF Support Crippled | Activities that directly support essential functions are crippled; essential functions continue for a time | - | MEF Failure | Any one mission essential function fails for period of time longer than acceptable; overall mission of the organization degraded but can still be accomplished for a time | - | Mission Failure | Multiple or all mission essential functions fail; ability to recover those functions degraded; organization’s ability to deliver its overall mission fails | + === "Text" + Impact on Mission Essential Functions of the Organization + + | Value | Description | + | :--- | :---------- | + | Degraded | Little to no impact up to degradation of non-essential functions; chronic degradation would eventually harm essential functions | + | MEF Support Crippled | Activities that directly support essential functions are crippled; essential functions continue for a time | + | MEF Failure | Any one mission essential function fails for period of time longer than acceptable; overall mission of the organization degraded but can still be accomplished for a time | + | Mission Failure | Multiple or all mission essential functions fail; ability to recover those functions degraded; organization’s ability to deliver its overall mission fails | + === "JSON" + ```json + {% include-markdown "../examples/mission_impact.json" %} + ``` + === "Python" + ```python + {% include-markdown "../../../src/ssvc/decision_points/mission_impact.py" %} + ``` 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]. diff --git a/docs/reference/decision_points/public_value_added.md b/docs/reference/decision_points/public_value_added.md index 7392c64a..f2d222d4 100644 --- a/docs/reference/decision_points/public_value_added.md +++ b/docs/reference/decision_points/public_value_added.md @@ -1,13 +1,22 @@ !!! note "Public Value Added" - This decision point asks how much value a publication from the coordinator would benefit the broader community. - The value is based on the state of existing public information about the vulnerablity. - - | Value | Description | - | :---: | :--- | - | Precedence | The publication would be the first publicly available, or be coincident with the first publicly available. | - | Ampliative | Amplifies and/or augments the existing public information about the vulnerability, for example, adds additional detail, addresses or corrects errors in other public information, draws further attention to the vulnerability, etc. | - | Limited | Minimal value added to the existing public information because existing information is already high quality and in multiple outlets. | + === "Text" + This decision point asks how much value a publication from the coordinator would benefit the broader community. + The value is based on the state of existing public information about the vulnerablity. + + | Value | Description | + | :--- | :--- | + | Precedence | The publication would be the first publicly available, or be coincident with the first publicly available. | + | Ampliative | Amplifies and/or augments the existing public information about the vulnerability, for example, adds additional detail, addresses or corrects errors in other public information, draws further attention to the vulnerability, etc. | + | Limited | Minimal value added to the existing public information because existing information is already high quality and in multiple outlets. | + === "JSON" + ```json + {% include-markdown "../examples/public_value_added.json" %} + ``` + === "Python" + ```python + {% include-markdown "../../../src/ssvc/decision_points/public_value_added.py" %} + ``` The intent of the definition is that one rarely if ever transitions from limited to ampliative or ampliative to precedence. diff --git a/docs/reference/decision_points/report_credibility.md b/docs/reference/decision_points/report_credibility.md index c1ea7180..22ae6f1c 100644 --- a/docs/reference/decision_points/report_credibility.md +++ b/docs/reference/decision_points/report_credibility.md @@ -1,11 +1,21 @@ !!! note "Report Credibility" - Assessing the credibility of a report is complex, but the assessment must reach a conclusion of either: + === "Text" + Assessing the credibility of a report is complex, but the assessment must reach a conclusion of either: + + | Value | Description | + | :--- | :--- | + | Credible | The report is credible. | + | Not credible | The report is not credible. | + === "JSON" + ```json + {% include-markdown "../examples/report_credibility.json" %} + ``` + === "Python" + ```python + {% include-markdown "../../../src/ssvc/decision_points/report_credibility.py" %} + ``` - | Value | Description | - | :---: | :--- | - | Credible | The report is credible. | - | Not credible | The report is not credible. | !!! tip inline "See Also" diff --git a/docs/reference/decision_points/report_public.md b/docs/reference/decision_points/report_public.md index ed8ef39d..a6386fa0 100644 --- a/docs/reference/decision_points/report_public.md +++ b/docs/reference/decision_points/report_public.md @@ -1,9 +1,18 @@ !!! note "Report Public" - Is a viable report of the details of the vulnerability already publicly available? - - | Value | Description | - | :---: | :--- | - | Yes | A viable report of the details of the vulnerability is already publicly available. | - | No | A viable report of the details of the vulnerability is not already publicly available. | + === "Text" + Is a viable report of the details of the vulnerability already publicly available? + + | Value | Description | + | :--- | :--- | + | Yes | A viable report of the details of the vulnerability is already publicly available. | + | No | A viable report of the details of the vulnerability is not already publicly available. | + === "JSON" + ```json + {% include-markdown "../examples/report_public.json" %} + ``` + === "Python" + ```python + {% include-markdown "../../../src/ssvc/decision_points/report_public.py" %} + ``` diff --git a/docs/reference/decision_points/safety_impact.md b/docs/reference/decision_points/safety_impact.md index 97b8bcfa..43ba851e 100644 --- a/docs/reference/decision_points/safety_impact.md +++ b/docs/reference/decision_points/safety_impact.md @@ -2,27 +2,36 @@ !!! note "Safety Impact" - Safety Impacts of Affected System Compromise - - | Value | Type of Harm | Definition | - | --: | :-- | :----------- | - | None | All | Does *not* mean no impact *literally*; the effect is below the threshold for all aspects described in Minor | - | Minor | Physical Harm | Physical discomfort for users of the system OR a minor occupational safety hazard OR reduction in physical system safety margins | - | Minor | Environment | Minor externalities (property damage, environmental damage, etc.) imposed on other parties | - | Minor | Financial | Financial losses, which are not readily absorbable, to multiple persons | - | Minor | Psychological | Emotional or psychological harm, sufficient to be cause for counseling or therapy, to multiple persons | - | Major | Physical Harm | Physical distress and injuries for users of the system OR a significant occupational safety hazard OR failure of physical system functional capabilities that support safe operation | - | Major | Environment | Major externalities (property damage, environmental damage, etc.) imposed on other parties | - | Major | Financial | Financial losses that likely lead to bankruptcy of multiple persons | - | Major | Psychological | Widespread emotional or psychological harm, sufficient to be cause for counseling or therapy, to populations of people | - | Hazardous | Physical Harm | Serious or fatal injuries, where fatalities are plausibly preventable via emergency services or other measures OR parts of the cyber-physical system that support safe operation break | - | Hazardous | Environment | Serious externalities (threat to life as well as property, widespread environmental damage, measurable public health risks, etc.) imposed on other parties | - | Hazardous | Financial | Socio-technical system (elections, financial grid, etc.) of which the affected component is a part is actively destabilized and enters unsafe state | - | Hazardous | Psychological | N/A | - | Catastrophic | Physical Harm | Multiple immediate fatalities (emergency response probably cannot save the victims.) | - | Catastrophic | Environment | Extreme externalities (immediate public health threat, environmental damage leading to small ecosystem collapse, etc.) imposed on other parties | - | Catastrophic | Financial | Social systems (elections, financial grid, etc.) supported by the software collapse | - | Catastrophic | Psychological | N/A | + === "Text" + Safety Impacts of Affected System Compromise + + | Value | Type of Harm | Definition | + | :--- | :--- | :----------- | + | None | All | Does *not* mean no impact *literally*; the effect is below the threshold for all aspects described in Minor | + | Minor | Physical Harm | Physical discomfort for users of the system OR a minor occupational safety hazard OR reduction in physical system safety margins | + | Minor | Environment | Minor externalities (property damage, environmental damage, etc.) imposed on other parties | + | Minor | Financial | Financial losses, which are not readily absorbable, to multiple persons | + | Minor | Psychological | Emotional or psychological harm, sufficient to be cause for counseling or therapy, to multiple persons | + | Major | Physical Harm | Physical distress and injuries for users of the system OR a significant occupational safety hazard OR failure of physical system functional capabilities that support safe operation | + | Major | Environment | Major externalities (property damage, environmental damage, etc.) imposed on other parties | + | Major | Financial | Financial losses that likely lead to bankruptcy of multiple persons | + | Major | Psychological | Widespread emotional or psychological harm, sufficient to be cause for counseling or therapy, to populations of people | + | Hazardous | Physical Harm | Serious or fatal injuries, where fatalities are plausibly preventable via emergency services or other measures OR parts of the cyber-physical system that support safe operation break | + | Hazardous | Environment | Serious externalities (threat to life as well as property, widespread environmental damage, measurable public health risks, etc.) imposed on other parties | + | Hazardous | Financial | Socio-technical system (elections, financial grid, etc.) of which the affected component is a part is actively destabilized and enters unsafe state | + | Hazardous | Psychological | N/A | + | Catastrophic | Physical Harm | Multiple immediate fatalities (emergency response probably cannot save the victims.) | + | Catastrophic | Environment | Extreme externalities (immediate public health threat, environmental damage leading to small ecosystem collapse, etc.) imposed on other parties | + | Catastrophic | Financial | Social systems (elections, financial grid, etc.) supported by the software collapse | + | Catastrophic | Psychological | N/A | + === "JSON" + ```json + {% include-markdown "../examples/safety_impact.json" %} + ``` + === "Python" + ```python + {% include-markdown "../../../src/ssvc/decision_points/safety_impact.py" %} + ``` diff --git a/docs/reference/decision_points/supplier_cardinality.md b/docs/reference/decision_points/supplier_cardinality.md index 887642cb..06afc600 100644 --- a/docs/reference/decision_points/supplier_cardinality.md +++ b/docs/reference/decision_points/supplier_cardinality.md @@ -1,8 +1,17 @@ !!! note "Supplier Cardinality" - How many suppliers are responsible for the vulnerable component and its remediation or mitigation plan? - - | Value | Description | - | :---: | :--- | - | One | One supplier is responsible for the vulnerable component and its remediation or mitigation plan. | - | Multiple | Multiple suppliers are responsible for the vulnerable component and its remediation or mitigation plan. | + === "Text" + How many suppliers are responsible for the vulnerable component and its remediation or mitigation plan? + + | Value | Description | + | :--- | :--- | + | One | One supplier is responsible for the vulnerable component and its remediation or mitigation plan. | + | Multiple | Multiple suppliers are responsible for the vulnerable component and its remediation or mitigation plan. | + === "JSON" + ```json + {% include-markdown "../examples/supplier_cardinality.json" %} + ``` + === "Python" + ```python + {% include-markdown "../../../src/ssvc/decision_points/supplier_cardinality.py" %} + ``` diff --git a/docs/reference/decision_points/supplier_contacted.md b/docs/reference/decision_points/supplier_contacted.md index 40e473e7..97e132bd 100644 --- a/docs/reference/decision_points/supplier_contacted.md +++ b/docs/reference/decision_points/supplier_contacted.md @@ -5,10 +5,19 @@ !!! note "Supplier Contacted" - Has the reporter made a good-faith effort to contact the supplier of the vulnerable component using a quality contact method? - - | Value | Description | - | :---: | :--- | - | Yes | The reporter has made a good-faith effort to contact the supplier of the vulnerable component using a quality contact method. | - | No | The reporter has not made a good-faith effort to contact the supplier of the vulnerable component using a quality contact method. | + === "Text" + Has the reporter made a good-faith effort to contact the supplier of the vulnerable component using a quality contact method? + + | Value | Description | + | :--- | :--- | + | Yes | The reporter has made a good-faith effort to contact the supplier of the vulnerable component using a quality contact method. | + | No | The reporter has not made a good-faith effort to contact the supplier of the vulnerable component using a quality contact method. | + === "JSON" + ```json + {% include-markdown "../examples/supplier_contacted.json" %} + ``` + === "Python" + ```python + {% include-markdown "../../../src/ssvc/decision_points/supplier_contacted.py" %} + ``` diff --git a/docs/reference/decision_points/supplier_engagement.md b/docs/reference/decision_points/supplier_engagement.md index ddb820cb..9bf5c78e 100644 --- a/docs/reference/decision_points/supplier_engagement.md +++ b/docs/reference/decision_points/supplier_engagement.md @@ -1,9 +1,18 @@ !!! note "Supplier Engagement" - Is the supplier responding to the reporter's contact effort and actively participating in the coordination effort? - - | Value | Description | - | :---: | :--- | - | Active | The supplier is responding to the reporter's contact effort and actively participating in the coordination effort. | - | Unresponsive | The supplier is not responding to the reporter's contact effort and is not actively participating in the coordination effort. | + === "Text" + Is the supplier responding to the reporter's contact effort and actively participating in the coordination effort? + + | Value | Description | + | :--- | :--- | + | Active | The supplier is responding to the reporter's contact effort and actively participating in the coordination effort. | + | Unresponsive | The supplier is not responding to the reporter's contact effort and is not actively participating in the coordination effort. | + === "JSON" + ```json + {% include-markdown "../examples/supplier_engagement.json" %} + ``` + === "Python" + ```python + {% include-markdown "../../../src/ssvc/decision_points/supplier_engagement.py" %} + ``` diff --git a/docs/reference/decision_points/supplier_involvement.md b/docs/reference/decision_points/supplier_involvement.md index 3a0ee4b3..26f1f4f9 100644 --- a/docs/reference/decision_points/supplier_involvement.md +++ b/docs/reference/decision_points/supplier_involvement.md @@ -1,10 +1,19 @@ !!! note "Supplier Involvement" - This decision point accounts for the state of the supplier's work on addressing the vulnerability. - - | Value | Description | - | :---: | :--- | - | Fix Ready | The supplier has provided a patch or fix. | - | Cooperative | The supplier is actively generating a patch or fix; they may or may not have provided a mitigation or work-around in the mean time. | - | Uncooperative/Unresponsive | The supplier has not responded, declined to generate a remediation, or no longer exists. | + === "Text" + This decision point accounts for the state of the supplier's work on addressing the vulnerability. + + | Value | Description | + | :--- | :--- | + | Fix Ready | The supplier has provided a patch or fix. | + | Cooperative | The supplier is actively generating a patch or fix; they may or may not have provided a mitigation or work-around in the mean time. | + | Uncooperative/Unresponsive | The supplier has not responded, declined to generate a remediation, or no longer exists. | + === "JSON" + ```json + {% include-markdown "../examples/supplier_involvement.json" %} + ``` + === "Python" + ```python + {% include-markdown "../../../src/ssvc/decision_points/supplier_involvement.py" %} + ``` diff --git a/docs/reference/decision_points/system_exposure.md b/docs/reference/decision_points/system_exposure.md index d54f7be7..3130992c 100644 --- a/docs/reference/decision_points/system_exposure.md +++ b/docs/reference/decision_points/system_exposure.md @@ -2,13 +2,23 @@ !!! note "System Exposure" - The Accessible Attack Surface of the Affected System or Service + === "Text" + The Accessible Attack Surface of the Affected System or Service + + | Value | Description | + | :--- | :------------ | + | Small | Local service or program; highly controlled network | + | Controlled | Networked service with some access restrictions or mitigations already in place (whether locally or on the network). A successful mitigation must reliably interrupt the adversary’s attack, which requires the attack is detectable both reliably and quickly enough to respond. *Controlled* covers the situation in which a vulnerability can be exploited through chaining it with other vulnerabilities. The assumption is that the number of steps in the attack path is relatively low; if the path is long enough that it is implausible for an adversary to reliably execute it, then *exposure* should be *small*. | + | Open | Internet or another widely accessible network where access cannot plausibly be restricted or controlled (e.g., DNS servers, web servers, VOIP servers, email servers) | + === "JSON" + ```json + {% include-markdown "../examples/system_exposure.json" %} + ``` + === "Python" + ```python + {% include-markdown "../../../src/ssvc/decision_points/system_exposure.py" %} + ``` - | Value | Description | - | :--- | :------------ | - | Small | Local service or program; highly controlled network | - | Controlled | Networked service with some access restrictions or mitigations already in place (whether locally or on the network). A successful mitigation must reliably interrupt the adversary’s attack, which requires the attack is detectable both reliably and quickly enough to respond. *Controlled* covers the situation in which a vulnerability can be exploited through chaining it with other vulnerabilities. The assumption is that the number of steps in the attack path is relatively low; if the path is long enough that it is implausible for an adversary to reliably execute it, then *exposure* should be *small*. | - | Open | Internet or another widely accessible network where access cannot plausibly be restricted or controlled (e.g., DNS servers, web servers, VOIP servers, email servers) | 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. diff --git a/docs/reference/decision_points/technical_impact.md b/docs/reference/decision_points/technical_impact.md index 847e00f4..8c81212d 100644 --- a/docs/reference/decision_points/technical_impact.md +++ b/docs/reference/decision_points/technical_impact.md @@ -1,11 +1,20 @@ !!! note "Technical Impact" - Technical Impact of Exploiting the Vulnerability - - | Value | Definition | - | :--- | :------------- | - | Partial | The exploit gives the adversary *limited* control over, or information exposure about, the behavior of the software that contains the vulnerability. Or the exploit gives the adversary an importantly low stochastic opportunity for total control. In this context, “low” means that the attacker cannot reasonably make enough attempts to overcome the low chance of each attempt not working. Denial of service is a form of limited control over the behavior of the vulnerable component. | - | Total | The exploit gives the adversary *total* control over the behavior of the software, or it gives total disclosure of all information on the system that contains the vulnerability | + === "Text" + Technical Impact of Exploiting the Vulnerability + + | Value | Definition | + | :--- | :------------- | + | Partial | The exploit gives the adversary *limited* control over, or information exposure about, the behavior of the software that contains the vulnerability. Or the exploit gives the adversary an importantly low stochastic opportunity for total control. In this context, “low” means that the attacker cannot reasonably make enough attempts to overcome the low chance of each attempt not working. Denial of service is a form of limited control over the behavior of the vulnerable component. | + | Total | The exploit gives the adversary *total* control over the behavior of the software, or it gives total disclosure of all information on the system that contains the vulnerability | + === "JSON" + ```json + {% include-markdown "../examples/technical_impact.json" %} + ``` + === "Python" + ```python + {% include-markdown "../../../src/ssvc/decision_points/technical_impact.py" %} + ``` When evaluating [*Technical Impact*](#technical-impact), recall the scope definition in the [Scope Section](#scope). Total control is relative to the affected component where the vulnerability resides. diff --git a/docs/reference/decision_points/value_density.md b/docs/reference/decision_points/value_density.md index f0ca68a6..622b6735 100644 --- a/docs/reference/decision_points/value_density.md +++ b/docs/reference/decision_points/value_density.md @@ -1,11 +1,20 @@ !!! note "Value Density" - [*Value Density*](#value-density) is described as *diffuse* or *concentrated* based on the resources that the adversary will gain control over with a single exploitation event: - - | Value | Definition | - | :--- | :---------- | - | diffuse | The system that contains the vulnerable component has limited resources. That is, the resources that the adversary will gain control over with a single exploitation event are relatively small.| - | concentrated | The system that contains the vulnerable component is rich in resources. Heuristically, such systems are often the direct responsibility of “system operators” rather than users. | + === "Text" + [*Value Density*](#value-density) is described as *diffuse* or *concentrated* based on the resources that the adversary will gain control over with a single exploitation event: + + | Value | Definition | + | :--- | :---------- | + | Diffuse | The system that contains the vulnerable component has limited resources. That is, the resources that the adversary will gain control over with a single exploitation event are relatively small.| + | Concentrated | The system that contains the vulnerable component is rich in resources. Heuristically, such systems are often the direct responsibility of “system operators” rather than users. | + === "JSON" + ```json + {% include-markdown "../examples/value_density.json" %} + ``` + === "Python" + ```python + {% include-markdown "../../../src/ssvc/decision_points/value_density.py" %} + ``` !!! info inline end "User vs. System Operator" diff --git a/docs/reference/examples/automatable.json b/docs/reference/examples/automatable.json new file mode 100644 index 00000000..589590fb --- /dev/null +++ b/docs/reference/examples/automatable.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Automatable", + "description": "Can an attacker reliably automate creating exploitation events for this vulnerability?", + "key": "A", + "values": [ + { + "name": "No", + "description": "Attackers cannot reliably automate steps 1-4 of the kill chain for this vulnerability. These steps are (1) reconnaissance, (2) weaponization, (3) delivery, and (4) exploitation.", + "key": "N" + }, + { + "name": "Yes", + "description": "Attackers can reliably automate steps 1-4 of the kill chain.", + "key": "Y" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/automatable.md b/docs/reference/examples/automatable.md new file mode 100644 index 00000000..2b652185 --- /dev/null +++ b/docs/reference/examples/automatable.md @@ -0,0 +1,6 @@ +Can an attacker reliably automate creating exploitation events for this vulnerability? + +| Value | Key | Description | +| --- | --- | --- | +| No | N | Attackers cannot reliably automate steps 1-4 of the kill chain for this vulnerability. These steps are (1) reconnaissance, (2) weaponization, (3) delivery, and (4) exploitation. | +| Yes | Y | Attackers can reliably automate steps 1-4 of the kill chain. | \ No newline at end of file diff --git a/docs/reference/examples/exploitation.json b/docs/reference/examples/exploitation.json new file mode 100644 index 00000000..83e140f3 --- /dev/null +++ b/docs/reference/examples/exploitation.json @@ -0,0 +1,24 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Exploitation", + "description": "The present state of exploitation of the vulnerability.", + "key": "E", + "values": [ + { + "name": "None", + "description": "There is no evidence of active exploitation and no public proof of concept (PoC) of how to exploit the vulnerability.", + "key": "N" + }, + { + "name": "PoC", + "description": "One of the following cases is true: (1) private evidence of exploitation is attested but not shared; (2) widespread hearsay attests to exploitation; (3) typical public PoC in places such as Metasploit or ExploitDB; or (4) the vulnerability has a well-known method of exploitation.", + "key": "P" + }, + { + "name": "Active", + "description": "Shared, observable, reliable evidence that the exploit is being used in the wild by real attackers; there is credible public reporting.", + "key": "A" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/exploitation.md b/docs/reference/examples/exploitation.md new file mode 100644 index 00000000..e1e7a00e --- /dev/null +++ b/docs/reference/examples/exploitation.md @@ -0,0 +1,7 @@ +The present state of exploitation of the vulnerability. + +| Value | Key | Description | +| --- | --- | --- | +| None | N | There is no evidence of active exploitation and no public proof of concept (PoC) of how to exploit the vulnerability. | +| PoC | P | One of the following cases is true: (1) private evidence of exploitation is attested but not shared; (2) widespread hearsay attests to exploitation; (3) typical public PoC in places such as Metasploit or ExploitDB; or (4) the vulnerability has a well-known method of exploitation. | +| Active | A | Shared, observable, reliable evidence that the exploit is being used in the wild by real attackers; there is credible public reporting. | \ No newline at end of file diff --git a/docs/reference/examples/human_impact.json b/docs/reference/examples/human_impact.json new file mode 100644 index 00000000..25ad3eb4 --- /dev/null +++ b/docs/reference/examples/human_impact.json @@ -0,0 +1,29 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Human Impact", + "description": "Human Impact", + "key": "HI", + "values": [ + { + "name": "Low", + "description": "Safety=None/Minor, Mission=None/Degraded/Crippled", + "key": "L" + }, + { + "name": "Medium", + "description": "Safety=None/Minor, Mission=MEF Failure OR Safety=Major, Mission=None/Degraded/Crippled", + "key": "M" + }, + { + "name": "High", + "description": "Safety=Hazardous, Mission=None/Degraded/Crippled/MEF Failure OR Safety=Major, Mission=MEF Failure", + "key": "H" + }, + { + "name": "Very High", + "description": "Safety=Catastrophic OR Mission=Mission Failure", + "key": "VH" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/human_impact.md b/docs/reference/examples/human_impact.md new file mode 100644 index 00000000..74721088 --- /dev/null +++ b/docs/reference/examples/human_impact.md @@ -0,0 +1,8 @@ +Human Impact + +| Value | Key | Description | +| --- | --- | --- | +| Low | L | Safety=None/Minor, Mission=None/Degraded/Crippled | +| Medium | M | Safety=None/Minor, Mission=MEF Failure OR Safety=Major, Mission=None/Degraded/Crippled | +| High | H | Safety=Hazardous, Mission=None/Degraded/Crippled/MEF Failure OR Safety=Major, Mission=MEF Failure | +| Very High | VH | Safety=Catastrophic OR Mission=Mission Failure | \ No newline at end of file diff --git a/docs/reference/examples/mission_impact.json b/docs/reference/examples/mission_impact.json new file mode 100644 index 00000000..fa9f1303 --- /dev/null +++ b/docs/reference/examples/mission_impact.json @@ -0,0 +1,29 @@ +{ + "namespace": "ssvc", + "version": "2.0.0", + "name": "Mission Impact", + "description": "Impact on Mission Essential Functions of the Organization", + "key": "MI", + "values": [ + { + "name": "Degraded", + "description": "Little to no impact up to degradation of non-essential functions; chronic degradation would eventually harm essential functions", + "key": "D" + }, + { + "name": "MEF Support Crippled", + "description": "Activities that directly support essential functions are crippled; essential functions continue for a time", + "key": "MSC" + }, + { + "name": "MEF Failure", + "description": "Any one mission essential function fails for period of time longer than acceptable; overall mission of the organization degraded but can still be accomplished for a time", + "key": "MEF" + }, + { + "name": "Mission Failure", + "description": "Multiple or all mission essential functions fail; ability to recover those functions degraded; organization\u2019s ability to deliver its overall mission fails", + "key": "MF" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/mission_impact.md b/docs/reference/examples/mission_impact.md new file mode 100644 index 00000000..626226ca --- /dev/null +++ b/docs/reference/examples/mission_impact.md @@ -0,0 +1,8 @@ +Impact on Mission Essential Functions of the Organization + +| Value | Key | Description | +| --- | --- | --- | +| Degraded | D | Little to no impact up to degradation of non-essential functions; chronic degradation would eventually harm essential functions | +| MEF Support Crippled | MSC | Activities that directly support essential functions are crippled; essential functions continue for a time | +| MEF Failure | MEF | Any one mission essential function fails for period of time longer than acceptable; overall mission of the organization degraded but can still be accomplished for a time | +| Mission Failure | MF | Multiple or all mission essential functions fail; ability to recover those functions degraded; organization’s ability to deliver its overall mission fails | \ No newline at end of file diff --git a/docs/reference/examples/public_safety_impact.json b/docs/reference/examples/public_safety_impact.json new file mode 100644 index 00000000..7cfe1a47 --- /dev/null +++ b/docs/reference/examples/public_safety_impact.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Public Safety Impact", + "description": "A coarse-grained representation of impact to public safety.", + "key": "PSI", + "values": [ + { + "name": "Minimal", + "description": "Safety impact of None or Minor.", + "key": "M" + }, + { + "name": "Significant", + "description": "Safety impact of Major, Hazardous, or Catastrophic.", + "key": "S" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/public_safety_impact.md b/docs/reference/examples/public_safety_impact.md new file mode 100644 index 00000000..7f3ba7e0 --- /dev/null +++ b/docs/reference/examples/public_safety_impact.md @@ -0,0 +1,6 @@ +A coarse-grained representation of impact to public safety. + +| Value | Key | Description | +| --- | --- | --- | +| Minimal | M | Safety impact of None or Minor. | +| Significant | S | Safety impact of Major, Hazardous, or Catastrophic. | \ No newline at end of file diff --git a/docs/reference/examples/public_value_added.json b/docs/reference/examples/public_value_added.json new file mode 100644 index 00000000..19642c67 --- /dev/null +++ b/docs/reference/examples/public_value_added.json @@ -0,0 +1,24 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Public Value Added", + "description": "How much value would a publication from the coordinator benefit the broader community?", + "key": "PVA", + "values": [ + { + "name": "Precedence", + "description": "The publication would be the first publicly available, or be coincident with the first publicly available.", + "key": "P" + }, + { + "name": "Ampliative", + "description": "Amplifies and/or augments the existing public information about the vulnerability, for example, adds additional detail, addresses or corrects errors in other public information, draws further attention to the vulnerability, etc.", + "key": "A" + }, + { + "name": "Limited", + "description": "Minimal value added to the existing public information because existing information is already high quality and in multiple outlets.", + "key": "L" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/public_value_added.md b/docs/reference/examples/public_value_added.md new file mode 100644 index 00000000..5cf0efe6 --- /dev/null +++ b/docs/reference/examples/public_value_added.md @@ -0,0 +1,7 @@ +How much value would a publication from the coordinator benefit the broader community? + +| Value | Key | Description | +| --- | --- | --- | +| Precedence | P | The publication would be the first publicly available, or be coincident with the first publicly available. | +| Ampliative | A | Amplifies and/or augments the existing public information about the vulnerability, for example, adds additional detail, addresses or corrects errors in other public information, draws further attention to the vulnerability, etc. | +| Limited | L | Minimal value added to the existing public information because existing information is already high quality and in multiple outlets. | \ No newline at end of file diff --git a/docs/reference/examples/report_credibility.json b/docs/reference/examples/report_credibility.json new file mode 100644 index 00000000..1dedf078 --- /dev/null +++ b/docs/reference/examples/report_credibility.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Report Credibility", + "description": "Is the report credible?", + "key": "RC", + "values": [ + { + "name": "Credible", + "description": "The report is credible.", + "key": "C" + }, + { + "name": "Not Credible", + "description": "The report is not credible.", + "key": "NC" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/report_credibility.md b/docs/reference/examples/report_credibility.md new file mode 100644 index 00000000..f1418767 --- /dev/null +++ b/docs/reference/examples/report_credibility.md @@ -0,0 +1,6 @@ +Is the report credible? + +| Value | Key | Description | +| --- | --- | --- | +| Credible | C | The report is credible. | +| Not Credible | NC | The report is not credible. | \ No newline at end of file diff --git a/docs/reference/examples/report_public.json b/docs/reference/examples/report_public.json new file mode 100644 index 00000000..238443be --- /dev/null +++ b/docs/reference/examples/report_public.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Report Public", + "description": "Is a viable report of the details of the vulnerability already publicly available?", + "key": "RP", + "values": [ + { + "name": "No", + "description": "No public report of the vulnerability exists.", + "key": "N" + }, + { + "name": "Yes", + "description": "A public report of the vulnerability exists.", + "key": "Y" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/report_public.md b/docs/reference/examples/report_public.md new file mode 100644 index 00000000..e1e0fac0 --- /dev/null +++ b/docs/reference/examples/report_public.md @@ -0,0 +1,6 @@ +Is a viable report of the details of the vulnerability already publicly available? + +| Value | Key | Description | +| --- | --- | --- | +| No | N | No public report of the vulnerability exists. | +| Yes | Y | A public report of the vulnerability exists. | \ No newline at end of file diff --git a/docs/reference/examples/safety_impact.json b/docs/reference/examples/safety_impact.json new file mode 100644 index 00000000..c74f0080 --- /dev/null +++ b/docs/reference/examples/safety_impact.json @@ -0,0 +1,34 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Safety Impact", + "description": "The safety impact of the vulnerability.", + "key": "SI", + "values": [ + { + "name": "None", + "description": "The effect is below the threshold for all aspects described in Minor.", + "key": "N" + }, + { + "name": "Minor", + "description": "Any one or more of these conditions hold. Physical harm: Physical discomfort for users (not operators) of the system. Operator resiliency: Requires action by system operator to maintain safe system state as a result of exploitation of the vulnerability where operator actions would be well within expected operator abilities; OR causes a minor occupational safety hazard. System resiliency: Small reduction in built-in system safety margins; OR small reduction in system functional capabilities that support safe operation. Environment Minor externalities (property damage, environmental damage, etc.) imposed on other parties. Financial Financial losses, which are not readily absorbable, to multiple persons. Psychological: Emotional or psychological harm, sufficient to be cause for counselling or therapy, to multiple persons.", + "key": "M" + }, + { + "name": "Major", + "description": "Any one or more of these conditions hold. Physical harm: Physical distress and injuries for users (not operators) of the system. Operator resiliency: Requires action by system operator to maintain safe system state as a result of exploitation of the vulnerability where operator actions would be within their capabilities but the actions require their full attention and effort; OR significant distraction or discomfort to operators; OR causes significant occupational safety hazard. System resiliency: System safety margin effectively eliminated but no actual harm; OR failure of system functional capabilities that support safe operation. Environment: Major externalities (property damage, environmental damage, etc.) imposed on other parties. Financial: Financial losses that likely lead to bankruptcy of multiple persons. Psychological: Widespread emotional or psychological harm, sufficient to be cause for counselling or therapy, to populations of people.", + "key": "J" + }, + { + "name": "Hazardous", + "description": "Any one or more of these conditions hold. Physical harm: Serious or fatal injuries, where fatalities are plausibly preventable via emergency services or other measures. Operator resiliency: Actions that would keep the system in a safe state are beyond system operator capabilities, resulting in adverse conditions; OR great physical distress to system operators such that they cannot be expected to operate the system properly. System resiliency: Parts of the cyber-physical system break; system\u2019s ability to recover lost functionality remains intact. Environment: Serious externalities (threat to life as well as property, widespread environmental damage, measurable public health risks, etc.) imposed on other parties. Financial: Socio-technical system (elections, financial grid, etc.) of which the affected component is a part is actively destabilized and enters unsafe state. Psychological: N/A.", + "key": "H" + }, + { + "name": "Catastrophic", + "description": "Any one or more of these conditions hold. Physical harm: Multiple immediate fatalities (Emergency response probably cannot save the victims.) Operator resiliency: Operator incapacitated (includes fatality or otherwise incapacitated). System resiliency: Total loss of whole cyber-physical system, of which the software is a part. Environment: Extreme externalities (immediate public health threat, environmental damage leading to small ecosystem collapse, etc.) imposed on other parties. Financial: Social systems (elections, financial grid, etc.) supported by the software collapse. Psychological: N/A.", + "key": "C" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/safety_impact.md b/docs/reference/examples/safety_impact.md new file mode 100644 index 00000000..17a429f5 --- /dev/null +++ b/docs/reference/examples/safety_impact.md @@ -0,0 +1,9 @@ +The safety impact of the vulnerability. + +| Value | Key | Description | +| --- | --- | --- | +| None | N | The effect is below the threshold for all aspects described in Minor. | +| Minor | M | Any one or more of these conditions hold. Physical harm: Physical discomfort for users (not operators) of the system. Operator resiliency: Requires action by system operator to maintain safe system state as a result of exploitation of the vulnerability where operator actions would be well within expected operator abilities; OR causes a minor occupational safety hazard. System resiliency: Small reduction in built-in system safety margins; OR small reduction in system functional capabilities that support safe operation. Environment Minor externalities (property damage, environmental damage, etc.) imposed on other parties. Financial Financial losses, which are not readily absorbable, to multiple persons. Psychological: Emotional or psychological harm, sufficient to be cause for counselling or therapy, to multiple persons. | +| Major | J | Any one or more of these conditions hold. Physical harm: Physical distress and injuries for users (not operators) of the system. Operator resiliency: Requires action by system operator to maintain safe system state as a result of exploitation of the vulnerability where operator actions would be within their capabilities but the actions require their full attention and effort; OR significant distraction or discomfort to operators; OR causes significant occupational safety hazard. System resiliency: System safety margin effectively eliminated but no actual harm; OR failure of system functional capabilities that support safe operation. Environment: Major externalities (property damage, environmental damage, etc.) imposed on other parties. Financial: Financial losses that likely lead to bankruptcy of multiple persons. Psychological: Widespread emotional or psychological harm, sufficient to be cause for counselling or therapy, to populations of people. | +| Hazardous | H | Any one or more of these conditions hold. Physical harm: Serious or fatal injuries, where fatalities are plausibly preventable via emergency services or other measures. Operator resiliency: Actions that would keep the system in a safe state are beyond system operator capabilities, resulting in adverse conditions; OR great physical distress to system operators such that they cannot be expected to operate the system properly. System resiliency: Parts of the cyber-physical system break; system’s ability to recover lost functionality remains intact. Environment: Serious externalities (threat to life as well as property, widespread environmental damage, measurable public health risks, etc.) imposed on other parties. Financial: Socio-technical system (elections, financial grid, etc.) of which the affected component is a part is actively destabilized and enters unsafe state. Psychological: N/A. | +| Catastrophic | C | Any one or more of these conditions hold. Physical harm: Multiple immediate fatalities (Emergency response probably cannot save the victims.) Operator resiliency: Operator incapacitated (includes fatality or otherwise incapacitated). System resiliency: Total loss of whole cyber-physical system, of which the software is a part. Environment: Extreme externalities (immediate public health threat, environmental damage leading to small ecosystem collapse, etc.) imposed on other parties. Financial: Social systems (elections, financial grid, etc.) supported by the software collapse. Psychological: N/A. | \ No newline at end of file diff --git a/docs/reference/examples/supplier_cardinality.json b/docs/reference/examples/supplier_cardinality.json new file mode 100644 index 00000000..fb26d3b3 --- /dev/null +++ b/docs/reference/examples/supplier_cardinality.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Supplier Cardinality", + "description": "How many suppliers are responsible for the vulnerable component and its remediation or mitigation plan?", + "key": "SC", + "values": [ + { + "name": "One", + "description": "There is only one supplier of the vulnerable component.", + "key": "O" + }, + { + "name": "Multiple", + "description": "There are multiple suppliers of the vulnerable component.", + "key": "M" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/supplier_cardinality.md b/docs/reference/examples/supplier_cardinality.md new file mode 100644 index 00000000..ee35bd40 --- /dev/null +++ b/docs/reference/examples/supplier_cardinality.md @@ -0,0 +1,6 @@ +How many suppliers are responsible for the vulnerable component and its remediation or mitigation plan? + +| Value | Key | Description | +| --- | --- | --- | +| One | O | There is only one supplier of the vulnerable component. | +| Multiple | M | There are multiple suppliers of the vulnerable component. | \ No newline at end of file diff --git a/docs/reference/examples/supplier_contacted.json b/docs/reference/examples/supplier_contacted.json new file mode 100644 index 00000000..85c6364b --- /dev/null +++ b/docs/reference/examples/supplier_contacted.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Supplier Contacted", + "description": "Has the reporter made a good-faith effort to contact the supplier of the vulnerable component using a quality contact method?", + "key": "SC", + "values": [ + { + "name": "No", + "description": "The supplier has not been contacted.", + "key": "N" + }, + { + "name": "Yes", + "description": "The supplier has been contacted.", + "key": "Y" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/supplier_contacted.md b/docs/reference/examples/supplier_contacted.md new file mode 100644 index 00000000..ec465f09 --- /dev/null +++ b/docs/reference/examples/supplier_contacted.md @@ -0,0 +1,6 @@ +Has the reporter made a good-faith effort to contact the supplier of the vulnerable component using a quality contact method? + +| Value | Key | Description | +| --- | --- | --- | +| No | N | The supplier has not been contacted. | +| Yes | Y | The supplier has been contacted. | \ No newline at end of file diff --git a/docs/reference/examples/supplier_engagement.json b/docs/reference/examples/supplier_engagement.json new file mode 100644 index 00000000..66ee28ca --- /dev/null +++ b/docs/reference/examples/supplier_engagement.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Supplier Engagement", + "description": "Is the supplier responding to the reporter\u2019s contact effort and actively participating in the coordination effort?", + "key": "SE", + "values": [ + { + "name": "Active", + "description": "The supplier is responding to the reporter\u2019s contact effort and actively participating in the coordination effort.", + "key": "A" + }, + { + "name": "Unresponsive", + "description": "The supplier is not responding to the reporter\u2019s contact effort and not actively participating in the coordination effort.", + "key": "U" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/supplier_engagement.md b/docs/reference/examples/supplier_engagement.md new file mode 100644 index 00000000..611b33d8 --- /dev/null +++ b/docs/reference/examples/supplier_engagement.md @@ -0,0 +1,6 @@ +Is the supplier responding to the reporter’s contact effort and actively participating in the coordination effort? + +| Value | Key | Description | +| --- | --- | --- | +| Active | A | The supplier is responding to the reporter’s contact effort and actively participating in the coordination effort. | +| Unresponsive | U | The supplier is not responding to the reporter’s contact effort and not actively participating in the coordination effort. | \ No newline at end of file diff --git a/docs/reference/examples/supplier_involvement.json b/docs/reference/examples/supplier_involvement.json new file mode 100644 index 00000000..af0b4e65 --- /dev/null +++ b/docs/reference/examples/supplier_involvement.json @@ -0,0 +1,24 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Supplier Involvement", + "description": "What is the state of the supplier\u2019s work on addressing the vulnerability?", + "key": "SI", + "values": [ + { + "name": "Fix Ready", + "description": "The supplier has provided a patch or fix.", + "key": "FR" + }, + { + "name": "Cooperative", + "description": "The supplier is actively generating a patch or fix; they may or may not have provided a mitigation or work-around in the mean time.", + "key": "C" + }, + { + "name": "Uncooperative/Unresponsive", + "description": "The supplier has not responded, declined to generate a remediation, or no longer exists.", + "key": "UU" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/supplier_involvement.md b/docs/reference/examples/supplier_involvement.md new file mode 100644 index 00000000..7738fcad --- /dev/null +++ b/docs/reference/examples/supplier_involvement.md @@ -0,0 +1,7 @@ +What is the state of the supplier’s work on addressing the vulnerability? + +| Value | Key | Description | +| --- | --- | --- | +| Fix Ready | FR | The supplier has provided a patch or fix. | +| Cooperative | C | The supplier is actively generating a patch or fix; they may or may not have provided a mitigation or work-around in the mean time. | +| Uncooperative/Unresponsive | UU | The supplier has not responded, declined to generate a remediation, or no longer exists. | \ No newline at end of file diff --git a/docs/reference/examples/system_exposure.json b/docs/reference/examples/system_exposure.json new file mode 100644 index 00000000..763df0f2 --- /dev/null +++ b/docs/reference/examples/system_exposure.json @@ -0,0 +1,24 @@ +{ + "namespace": "ssvc", + "version": "1.0.1", + "name": "System Exposure", + "description": "The Accessible Attack Surface of the Affected System or Service", + "key": "EXP", + "values": [ + { + "name": "Small", + "description": "Local service or program; highly controlled network", + "key": "S" + }, + { + "name": "Controlled", + "description": "Networked service with some access restrictions or mitigations already in place (whether locally or on the network). A successful mitigation must reliably interrupt the adversary\u2019s attack, which requires the attack is detectable both reliably and quickly enough to respond. Controlled covers the situation in which a vulnerability can be exploited through chaining it with other vulnerabilities. The assumption is that the number of steps in the attack path is relatively low; if the path is long enough that it is implausible for an adversary to reliably execute it, then exposure should be small.", + "key": "C" + }, + { + "name": "Open", + "description": "Internet or another widely accessible network where access cannot plausibly be restricted or controlled (e.g., DNS servers, web servers, VOIP servers, email servers)", + "key": "O" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/system_exposure.md b/docs/reference/examples/system_exposure.md new file mode 100644 index 00000000..a899691b --- /dev/null +++ b/docs/reference/examples/system_exposure.md @@ -0,0 +1,7 @@ +The Accessible Attack Surface of the Affected System or Service + +| Value | Key | Description | +| --- | --- | --- | +| Small | S | Local service or program; highly controlled network | +| Controlled | C | Networked service with some access restrictions or mitigations already in place (whether locally or on the network). A successful mitigation must reliably interrupt the adversary’s attack, which requires the attack is detectable both reliably and quickly enough to respond. Controlled covers the situation in which a vulnerability can be exploited through chaining it with other vulnerabilities. The assumption is that the number of steps in the attack path is relatively low; if the path is long enough that it is implausible for an adversary to reliably execute it, then exposure should be small. | +| Open | O | Internet or another widely accessible network where access cannot plausibly be restricted or controlled (e.g., DNS servers, web servers, VOIP servers, email servers) | \ No newline at end of file diff --git a/docs/reference/examples/technical_impact.json b/docs/reference/examples/technical_impact.json new file mode 100644 index 00000000..82f85e1e --- /dev/null +++ b/docs/reference/examples/technical_impact.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Technical Impact", + "description": "The technical impact of the vulnerability.", + "key": "TI", + "values": [ + { + "name": "Partial", + "description": "The exploit gives the adversary limited control over, or information exposure about, the behavior of the software that contains the vulnerability. Or the exploit gives the adversary an importantly low stochastic opportunity for total control.", + "key": "P" + }, + { + "name": "Total", + "description": "The exploit gives the adversary total control over the behavior of the software, or it gives total disclosure of all information on the system that contains the vulnerability.", + "key": "T" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/technical_impact.md b/docs/reference/examples/technical_impact.md new file mode 100644 index 00000000..38457e62 --- /dev/null +++ b/docs/reference/examples/technical_impact.md @@ -0,0 +1,6 @@ +The technical impact of the vulnerability. + +| Value | Key | Description | +| --- | --- | --- | +| Partial | P | The exploit gives the adversary limited control over, or information exposure about, the behavior of the software that contains the vulnerability. Or the exploit gives the adversary an importantly low stochastic opportunity for total control. | +| Total | T | The exploit gives the adversary total control over the behavior of the software, or it gives total disclosure of all information on the system that contains the vulnerability. | \ No newline at end of file diff --git a/docs/reference/examples/utility.json b/docs/reference/examples/utility.json new file mode 100644 index 00000000..ec2f1cde --- /dev/null +++ b/docs/reference/examples/utility.json @@ -0,0 +1,24 @@ +{ + "namespace": "ssvc", + "version": "1.0.1", + "name": "Utility", + "description": "The Usefulness of the Exploit to the Adversary", + "key": "U", + "values": [ + { + "name": "Laborious", + "description": "No to automatable and diffuse value", + "key": "L" + }, + { + "name": "Efficient", + "description": "Yes to automatable and diffuse value OR No to automatable and concentrated value", + "key": "E" + }, + { + "name": "Super Effective", + "description": "Yes to automatable and concentrated value", + "key": "S" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/utility.md b/docs/reference/examples/utility.md new file mode 100644 index 00000000..dadc5a34 --- /dev/null +++ b/docs/reference/examples/utility.md @@ -0,0 +1,7 @@ +The Usefulness of the Exploit to the Adversary + +| Value | Key | Description | +| --- | --- | --- | +| Laborious | L | No to automatable and diffuse value | +| Efficient | E | Yes to automatable and diffuse value OR No to automatable and concentrated value | +| Super Effective | S | Yes to automatable and concentrated value | \ No newline at end of file diff --git a/docs/reference/examples/value_density.json b/docs/reference/examples/value_density.json new file mode 100644 index 00000000..adae9dbc --- /dev/null +++ b/docs/reference/examples/value_density.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Value Density", + "description": "The concentration of value in the target", + "key": "VD", + "values": [ + { + "name": "Diffuse", + "description": "The system that contains the vulnerable component has limited resources. That is, the resources that the adversary will gain control over with a single exploitation event are relatively small.", + "key": "D" + }, + { + "name": "Concentrated", + "description": "The system that contains the vulnerable component is rich in resources. Heuristically, such systems are often the direct responsibility of \u201csystem operators\u201d rather than users.", + "key": "C" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/value_density.md b/docs/reference/examples/value_density.md new file mode 100644 index 00000000..da329e8d --- /dev/null +++ b/docs/reference/examples/value_density.md @@ -0,0 +1,6 @@ +The concentration of value in the target + +| Value | Key | Description | +| --- | --- | --- | +| Diffuse | D | The system that contains the vulnerable component has limited resources. That is, the resources that the adversary will gain control over with a single exploitation event are relatively small. | +| Concentrated | C | The system that contains the vulnerable component is rich in resources. Heuristically, such systems are often the direct responsibility of “system operators” rather than users. | \ No newline at end of file diff --git a/docs/reference/examples/virulence.json b/docs/reference/examples/virulence.json new file mode 100644 index 00000000..0b0d1a4f --- /dev/null +++ b/docs/reference/examples/virulence.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "name": "Virulence", + "description": "The speed at which the vulnerability can be exploited.", + "key": "V", + "values": [ + { + "name": "Slow", + "description": "Steps 1-4 of the kill chain cannot be reliably automated for this vulnerability for some reason. These steps are reconnaissance, weaponization, delivery, and exploitation. Example reasons for why a step may not be reliably automatable include (1) the vulnerable component is not searchable or enumerable on the network, (2) weaponization may require human direction for each target, (3) delivery may require channels that widely deployed network security configurations block, and (3) exploitation may be frustrated by adequate exploit-prevention techniques enabled by default; ASLR is an example of an exploit-prevention tool.", + "key": "S" + }, + { + "name": "Rapid", + "description": "Steps 1-4 of the of the kill chain can be reliably automated. If the vulnerability allows remote code execution or command injection, the default response should be rapid.", + "key": "R" + } + ] +} \ No newline at end of file diff --git a/docs/reference/examples/virulence.md b/docs/reference/examples/virulence.md new file mode 100644 index 00000000..4d4f28ca --- /dev/null +++ b/docs/reference/examples/virulence.md @@ -0,0 +1,6 @@ +The speed at which the vulnerability can be exploited. + +| Value | Key | Description | +| --- | --- | --- | +| Slow | S | Steps 1-4 of the kill chain cannot be reliably automated for this vulnerability for some reason. These steps are reconnaissance, weaponization, delivery, and exploitation. Example reasons for why a step may not be reliably automatable include (1) the vulnerable component is not searchable or enumerable on the network, (2) weaponization may require human direction for each target, (3) delivery may require channels that widely deployed network security configurations block, and (3) exploitation may be frustrated by adequate exploit-prevention techniques enabled by default; ASLR is an example of an exploit-prevention tool. | +| Rapid | R | Steps 1-4 of the of the kill chain can be reliably automated. If the vulnerability allows remote code execution or command injection, the default response should be rapid. | \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e72e7d93..8cd33e14 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,5 +7,7 @@ mkdocs-material-extensions mkdocstrings mkdocstrings-python mkdocs-print-site-plugin -pandas~=2.1.1 -scikit-learn~=1.3.1 \ No newline at end of file +dataclasses-json +pandas +scikit-learn +jsonschema diff --git a/src/pyproject.toml b/src/pyproject.toml new file mode 100644 index 00000000..83b62dbb --- /dev/null +++ b/src/pyproject.toml @@ -0,0 +1,70 @@ +[build-system] +# SetupTools +requires = ["setuptools>66", "setuptools-scm"] +build-backend = "setuptools.build_meta" +# Flit +#requires = ["flit_core >=3.2,<4"] +#build-backend = "flit_core.buildapi" +# Hatchling +#requires = ["hatchling"] +#build-backend = "hatchling.build" +# PDM-Backend +#requires = ["pdm-backend"] +#build-backend = "pdm.backend" + +[project] +name = "ssvc" +authors = [ + { name = "Allen D. Householder", email="adh@cert.org" }, + { name = "Vijay Sarvepalli", email="vssarvepalli@cert.org"} +] +description = "Tools for working with a Stakeholder Specific Vulnerability Categorization (SSVC)" +readme = {file="README.md", content-type="text/markdown"} +requires-python = ">=3.8" +keywords =["ssvc","vulnerability management","vulnerability management"] +license = {file="LICENSE.md"} +classifiers = [ + "Development Status :: 4 - Beta", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Topic :: Security", + "Topic :: Software Development :: Libraries :: Python Modules", +] +dependencies = [ + "mkdocs","mkdocs-material","mkdocs-material-extensions","mkdocstrings","mkdocstrings-python", + "mkdocs-include-markdown-plugin", "pandas","scipy", "dataclasses-json", "jsonschema" +] +dynamic = ["version",] + +[project.scripts] +ssvc_csv_analyzer="ssvc.csv_analyzer:main" + +[project.urls] +"Homepage" = "https://certcc.github.io/SSVC" +"Project" = "https://github.com/CERTCC/SSVC" +"Bug Tracker" = "https://github.com/CERTCC/SSVC/issues" + +[tool.setuptools.packages.find] +where = ["."] # list of folders that contain the packages (["."] by default) +include = ["ssvc*"] # package names should match these glob patterns (["*"] by default) +exclude = ["test*"] # exclude packages matching these glob patterns (empty by default) +#namespaces = false # to disable scanning PEP 420 namespaces (true by default) + +[tool.setuptools_scm] +version_file = "ssvc/_version.py" +root = ".." +relative_to = "pyproject.toml" + + +#[tools.setuptools.dynamic] + +[tool.black] +line-length = 79 +target-version = ['py38', 'py39', 'py310', 'py311'] + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "-ra -q" +testpaths = [ + "test", +] \ No newline at end of file diff --git a/src/ssvc/__init__.py b/src/ssvc/__init__.py new file mode 100644 index 00000000..03989558 --- /dev/null +++ b/src/ssvc/__init__.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +''' +file: __init__.py +author: adh +created_at: 9/20/23 10:36 AM +''' + + +def main(): + pass + + +if __name__ == '__main__': + main() diff --git a/src/ssvc/_mixins.py b/src/ssvc/_mixins.py new file mode 100644 index 00000000..48359e5d --- /dev/null +++ b/src/ssvc/_mixins.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +""" +file: _basics +author: adh +created_at: 9/20/23 4:51 PM +""" +from dataclasses import dataclass, field +from typing import Optional + +from dataclasses_json import config, dataclass_json + + +@dataclass_json +@dataclass(kw_only=True) +class _Versioned: + """ + Mixin class for versioned SSVC objects. + """ + + version: str = "0.0.0" + + +@dataclass_json +@dataclass(kw_only=True) +class _Namespaced: + """ + Mixin class for namespaced SSVC objects. + """ + + namespace: str = "ssvc" + + +@dataclass_json +@dataclass(kw_only=True) +class _Keyed: + """ + Mixin class for keyed SSVC objects. + """ + + key: str + + +def exclude_if_none(value): + return value is None + + +@dataclass_json +@dataclass(kw_only=True) +class _Base: + """ + Base class for SSVC objects. + """ + + name: str + description: str + _comment: Optional[str] = field( + default=None, metadata=config(exclude=exclude_if_none) + ) + + +def main(): + pass + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/_version.py b/src/ssvc/_version.py new file mode 100644 index 00000000..c157fc07 --- /dev/null +++ b/src/ssvc/_version.py @@ -0,0 +1,16 @@ +# file generated by setuptools_scm +# don't change, don't track in version control +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple, Union + VERSION_TUPLE = Tuple[Union[int, str], ...] +else: + VERSION_TUPLE = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE + +__version__ = version = '2.1.2.dev56+g4e67e02.d20231010' +__version_tuple__ = version_tuple = (2, 1, 2, 'dev56', 'g4e67e02.d20231010') diff --git a/src/analyze_csv.py b/src/ssvc/csv_analyzer.py similarity index 98% rename from src/analyze_csv.py rename to src/ssvc/csv_analyzer.py index 21c22a5a..0ddc9525 100644 --- a/src/analyze_csv.py +++ b/src/ssvc/csv_analyzer.py @@ -3,7 +3,7 @@ This module provides a script for analyzing an SSVC tree csv file. ```shell -usage: analyze_csv.py [-h] [--outcol OUTCOL] [--permutation] csvfile +usage: csv_analyzer.py [-h] [--outcol OUTCOL] [--permutation] csvfile Analyze an SSVC tree csv file @@ -27,7 +27,7 @@ ``` Analyze the csv file: ```shell - $ python analyze_csv.py test.csv + $ python csv_analyzer.py test.csv Feature Importance after Dropping Each Feature in test.csv feature feature_importance @@ -41,13 +41,13 @@ """ import argparse +import re import sys import pandas as pd -import re -from sklearn.tree import DecisionTreeClassifier import sklearn.inspection from sklearn.base import clone +from sklearn.tree import DecisionTreeClassifier def _col_norm(c: str) -> str: diff --git a/src/ssvc/decision_points/__init__.py b/src/ssvc/decision_points/__init__.py new file mode 100644 index 00000000..7018304c --- /dev/null +++ b/src/ssvc/decision_points/__init__.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +""" +The ssvc.decision_points package provides a set of decision points for use in SSVC decision functions. + +Decision points are the basic building blocks of SSVC decision functions. Individual decision points describe a +single aspect of the input to a decision function. Decision points should have the following characteristics: + +- A name or label +- A description +- A version (a semantic version string) +- A namespace (a short, unique string): For example, "ssvc" or "cvss" to indicate the source of the decision point +- A key (a short, unique string) that can be used to identify the decision point in a shorthand way +- A short enumeration of possible values + +In turn, each value should have the following characteristics: +- A name or label +- A description +- A key (a short, unique string) that can be used to identify the value in a shorthand way + + +file: __init__.py +author: adh +created_at: 9/20/23 12:38 PM +""" + + +def main(): + pass + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/automatable.py b/src/ssvc/decision_points/automatable.py new file mode 100644 index 00000000..4068acfe --- /dev/null +++ b/src/ssvc/decision_points/automatable.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +""" +file: automatable +author: adh +created_at: 9/21/23 10:37 AM +""" +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + + +AUT_NO = SsvcDecisionPointValue( + name="No", + key="N", + description="Attackers cannot reliably automate steps 1-4 of the kill chain for this vulnerability. " + "These steps are (1) reconnaissance, (2) weaponization, (3) delivery, and (4) exploitation.", +) +AUT_YES = SsvcDecisionPointValue( + name="Yes", + key="Y", + description="Attackers can reliably automate steps 1-4 of the kill chain.", +) + + +AUTOMATABLE_1 = SsvcDecisionPoint( + name="Automatable", + description="Can an attacker reliably automate creating exploitation events for this vulnerability?", + key="A", + version="1.0.0", + values=(AUT_NO, AUT_YES), +) + + +def main(): + print(AUTOMATABLE_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/base.py b/src/ssvc/decision_points/base.py new file mode 100644 index 00000000..c148f841 --- /dev/null +++ b/src/ssvc/decision_points/base.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +""" +file: decisionpoints +author: adh +created_at: 9/20/23 10:07 AM +""" + +import logging +from dataclasses import dataclass, field +from typing import ClassVar, Dict, Tuple + +from dataclasses_json import config, dataclass_json + +from ssvc._mixins import _Base, _Keyed, _Namespaced, _Versioned + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + + +class _DecisionPoints: + """ + A collection of SSVC decision points. + """ + + registry: ClassVar[Dict[str, "SsvcDecisionPoint"]] = {} + + def __iter__(self): + return iter(self.registry.values()) + + +REGISTERED_DECISION_POINTS = _DecisionPoints() + + +@dataclass_json +@dataclass(kw_only=True) +class SsvcDecisionPointValue(_Base, _Keyed): + """ + Models a single value option for a decision point. + """ + + pass + + +@dataclass_json +@dataclass(kw_only=True) +class SsvcDecisionPoint(_Base, _Keyed, _Versioned, _Namespaced): + """ + Models a single decision point as a list of values. + """ + + values: Tuple[SsvcDecisionPointValue] + + # this is only for our own use in Python land, exclude it from serialization + _fullname: str = field( + init=False, repr=False, default=None, metadata=config(exclude=lambda x: True) + ) + + def __post_init__(self): + self._fullname = f"{self.namespace} {self.name} v{self.version}" + logging.debug(f"Add {self._fullname} to registry") + REGISTERED_DECISION_POINTS.registry[self._fullname] = self + + def to_table(self): + rows = [] + rows.append(f"{self.description}") + rows.append("") + + headings = ["Value", "Key", "Description"] + + def make_row(items): + return "| " + " | ".join(items) + " |" + + rows.append(make_row(headings)) + rows.append(make_row(["---" for _ in headings])) + + for value in self.values: + rows.append(make_row([value.name, value.key, value.description])) + + return "\n".join(rows) + + +def main(): + dp = SsvcDecisionPoint( + _comment="This is an optional comment that will be included in the object.", + name="Exploitation", + description="Is there an exploit available?", + key="E", + version="1.0.0", + values=( + SsvcDecisionPointValue( + name="None", key="N", description="No exploit available" + ), + SsvcDecisionPointValue( + name="PoC", + key="P", + description="Proof of concept exploit available", + ), + SsvcDecisionPointValue( + name="Active", key="A", description="Active exploitation observed" + ), + ), + ) + print(dp.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/critical_software.py b/src/ssvc/decision_points/critical_software.py new file mode 100644 index 00000000..2bab998d --- /dev/null +++ b/src/ssvc/decision_points/critical_software.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +""" +file: eo_critical +author: adh +created_at: 9/21/23 2:03 PM +""" +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +YES = SsvcDecisionPointValue( + name="Yes", + key="Y", + description="System meets a critical software definition.", +) + +NO = SsvcDecisionPointValue( + name="No", + key="N", + description="System does not meet a critical software definition.", +) + +CRITICAL_SOFTWARE_1 = SsvcDecisionPoint( + name="Critical Software", + description="Denotes whether a system meets a critical software definition.", + namespace="SSVC", + key="CS", + version="1.0.0", + values=( + NO, + YES, + ), +) + + +def main(): + print(CRITICAL_SOFTWARE_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/__init__.py b/src/ssvc/decision_points/cvss/__init__.py new file mode 100644 index 00000000..afae4312 --- /dev/null +++ b/src/ssvc/decision_points/cvss/__init__.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +""" +file: __init__.py +author: adh +created_at: 9/20/23 12:39 PM +""" + + +def main(): + pass + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/access_complexity.py b/src/ssvc/decision_points/cvss/access_complexity.py new file mode 100644 index 00000000..08cfb1dc --- /dev/null +++ b/src/ssvc/decision_points/cvss/access_complexity.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +""" +Models the CVSS Access Complexity metric as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_HIGH_2 = SsvcDecisionPointValue( + name="High", key="H", description="Specialized access conditions exist." +) + +_MEDIUM = SsvcDecisionPointValue( + name="Medium", + key="M", + description="The access conditions are somewhat specialized.", +) + +_LOW_2 = SsvcDecisionPointValue( + name="Low", + key="L", + description="Specialized access conditions or extenuating circumstances do not exist.", +) + +_HIGH = SsvcDecisionPointValue( + name="High", + key="H", + description="Specialized access conditions exist; for example: the system is exploitable during specific windows of time (a race condition), the system is exploitable under specific circumstances (nondefault configurations), or the system is exploitable with victim interaction (vulnerability exploitable only if user opens e-mail)", +) + +_LOW = SsvcDecisionPointValue( + name="Low", + key="L", + description="Specialized access conditions or extenuating circumstances do not exist; the system is always exploitable.", +) + + +ACCESS_COMPLEXITY_1 = CvssDecisionPoint( + name="Access Complexity", + description="This metric measures the complexity of the attack required to exploit the vulnerability once an attacker has gained access to the target system.", + key="AC", + version="1.0.0", + values=( + _LOW, + _HIGH, + ), +) +""" +Defines LOW and HIGH values for CVSS Access Complexity. +""" + +ACCESS_COMPLEXITY_2 = CvssDecisionPoint( + name="Access Complexity", + description="This metric measures the complexity of the attack required to exploit the vulnerability once an attacker has gained access to the target system.", + key="AC", + version="2.0.0", + values=( + _LOW_2, + _MEDIUM, + _HIGH_2, + ), +) +""" +Updates LOW and HIGH definitions for CVSS Access Complexity. Adds MEDIUM value. +""" + + +def main(): + print(ACCESS_COMPLEXITY_1.to_json(indent=2)) + print(ACCESS_COMPLEXITY_2.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/access_vector.py b/src/ssvc/decision_points/cvss/access_vector.py new file mode 100644 index 00000000..4209f9df --- /dev/null +++ b/src/ssvc/decision_points/cvss/access_vector.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +""" +Models the CVSS Access Vector metric as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_NETWORK = SsvcDecisionPointValue( + name="Network", + key="N", + description="A vulnerability exploitable with network access means the vulnerable software is bound to the network stack and the attacker does not require local network access or local access. Such a vulnerability is often termed 'remotely exploitable'. An example of a network attack is an RPC buffer overflow.", +) + +_ADJACENT = SsvcDecisionPointValue( + name="Adjacent Network", + key="A", + description="A vulnerability exploitable with adjacent network access requires the attacker to have access to either the broadcast or collision domain of the vulnerable software. Examples of local networks include local IP subnet, Bluetooth, IEEE 802.11, and local Ethernet segment.", +) + +_LOCAL_2 = SsvcDecisionPointValue( + name="Local", + key="L", + description="A vulnerability exploitable with only local access requires the attacker to have either physical access to the vulnerable system or a local (shell) account. Examples of locally exploitable vulnerabilities are peripheral attacks such as Firewire/USB DMA attacks, and local privilege escalations (e.g., sudo).", +) + +_REMOTE = SsvcDecisionPointValue( + name="Remote", + key="R", + description="The vulnerability is exploitable remotely.", +) + +_LOCAL = SsvcDecisionPointValue( + name="Local", + key="L", + description="The vulnerability is only exploitable locally (i.e., it requires physical access or authenticated login to the target system)", +) + +ACCESS_VECTOR_1 = CvssDecisionPoint( + name="Access Vector", + description="This metric measures whether or not the vulnerability is exploitable locally or remotely.", + key="AV", + version="1.0.0", + values=( + _LOCAL, + _REMOTE, + ), +) +""" +Defines LOCAL and REMOTE values for CVSS Access Vector. +""" + + +ACCESS_VECTOR_2 = CvssDecisionPoint( + name="Access Vector", + description="This metric reflects the context by which vulnerability exploitation is possible.", + key="AV", + version="2.0.0", + values=( + _LOCAL_2, + _ADJACENT, + _NETWORK, + ), +) +""" +Updates LOCAL definition for CVSS Access Vector. Adds ADJACENT and NETWORK values. Removes REMOTE value. +""" + + +def main(): + print(ACCESS_VECTOR_1.to_json(indent=2)) + print(ACCESS_VECTOR_2.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/attack_complexity.py b/src/ssvc/decision_points/cvss/attack_complexity.py new file mode 100644 index 00000000..81d69268 --- /dev/null +++ b/src/ssvc/decision_points/cvss/attack_complexity.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +""" +Models the CVSS Attack Complexity metric as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_HIGH = SsvcDecisionPointValue( + name="High", + key="H", + description="A successful attack depends on conditions beyond the attacker's control.", +) + +_LOW = SsvcDecisionPointValue( + name="Low", + key="L", + description="Specialized access conditions or extenuating circumstances do not exist. An attacker can expect repeatable success against the vulnerable component.", +) + +ATTACK_COMPLEXITY_1 = CvssDecisionPoint( + name="Attack Complexity", + description="This metric describes the conditions beyond the attacker's control that must exist in order to exploit the vulnerability.", + key="AC", + version="1.0.0", + values=( + _LOW, + _HIGH, + ), +) +""" +Defines LOW and HIGH values for CVSS Attack Complexity. +""" + + +def main(): + print(ATTACK_COMPLEXITY_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/attack_vector.py b/src/ssvc/decision_points/cvss/attack_vector.py new file mode 100644 index 00000000..dca9a801 --- /dev/null +++ b/src/ssvc/decision_points/cvss/attack_vector.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +""" +Models the CVSS Attack Vector metric as an SSVC decision point. +""" +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_NETWORK = SsvcDecisionPointValue( + name="Network", + key="N", + description="A vulnerability exploitable with network access means the vulnerable component is bound to the network stack and the attacker's path is through OSI layer 3 (the network layer). Such a vulnerability is often termed 'remotely exploitable' and can be thought of as an attack being exploitable one or more network hops away (e.g. across layer 3 boundaries from routers).", +) + +_ADJACENT = SsvcDecisionPointValue( + name="Adjacent", + key="A", + description="A vulnerability exploitable with adjacent network access means the vulnerable component is bound to the network stack, however the attack is limited to the same shared physical (e.g. Bluetooth, IEEE 802.11), or logical (e.g. local IP subnet) network, and cannot be performed across an OSI layer 3 boundary (e.g. a router).", +) + +_LOCAL = SsvcDecisionPointValue( + name="Local", + key="L", + description="A vulnerability exploitable with Local access means that the vulnerable component is not bound to the network stack, and the attacker's path is via read/write/execute capabilities. In some cases, the attacker may be logged in locally in order to exploit the vulnerability, otherwise, she may rely on User Interaction to execute a malicious file.", +) + +_PHYSICAL = SsvcDecisionPointValue( + name="Physical", + key="P", + description="A vulnerability exploitable with Physical access requires the attacker to physically touch or manipulate the vulnerable component. Physical interaction may be brief (e.g. evil maid attack [1]) or persistent. An example of such an attack is a cold boot attack which allows an attacker to access to disk encryption keys after gaining physical access to the system, or peripheral attacks such as Firewire/USB Direct Memory Access attacks.", +) + +ATTACK_VECTOR_1 = CvssDecisionPoint( + name="Attack Vector", + description="This metric reflects the context by which vulnerability exploitation is possible. ", + key="AV", + version="1.0.0", + values=( + _PHYSICAL, + _LOCAL, + _ADJACENT, + _NETWORK, + ), +) +""" +Defines PHYSICAL, LOCAL, ADJACENT, and NETWORK values for CVSS Attack Vector. +""" + + +def main(): + print(ATTACK_VECTOR_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/authentication.py b/src/ssvc/decision_points/cvss/authentication.py new file mode 100644 index 00000000..3f269ad5 --- /dev/null +++ b/src/ssvc/decision_points/cvss/authentication.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +""" +Models the CVSS Authentication metric as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_AUTH_NONE = SsvcDecisionPointValue( + name="None", + key="N", + description="Authentication is not required to exploit the vulnerability.", +) + +_SINGLE = SsvcDecisionPointValue( + name="Single", + key="S", + description="The vulnerability requires an attacker to be logged into the system (such as at a command line or via a desktop session or web interface).", +) + +_MULTIPLE = SsvcDecisionPointValue( + name="Multiple", + key="M", + description="Exploiting the vulnerability requires that the attacker authenticate two or more times, even if the same credentials are used each time.", +) + +_REQUIRED = SsvcDecisionPointValue( + name="Required", + key="R", + description="Authentication is required to access and exploit the vulnerability.", +) + +_NOT_REQUIRED = SsvcDecisionPointValue( + name="Not Required", + key="N", + description="Authentication is not required to access or exploit the vulnerability.", +) + +AUTHENTICATION_1 = CvssDecisionPoint( + name="Authentication", + description="This metric measures whether or not an attacker needs to be authenticated to the target system in order to exploit the vulnerability.", + key="Au", + version="1.0.0", + values=( + _NOT_REQUIRED, + _REQUIRED, + ), +) +""" +Includes NOT_REQUIRED and REQUIRED values for CVSS Authentication. +""" + +AUTHENTICATION_2 = CvssDecisionPoint( + name="Authentication", + description="This metric measures the number of times an attacker must authenticate to a target in order to exploit a vulnerability. This metric does not gauge the strength or complexity of the authentication process, only that an attacker is required to provide credentials before an exploit may occur. The possible values for this metric are listed in Table 3. The fewer authentication instances that are required, the higher the vulnerability score.", + key="Au", + version="2.0.0", + values=( + _MULTIPLE, + _SINGLE, + _AUTH_NONE, + ), +) +""" +Includes MULTIPLE, SINGLE, and AUTH_NONE values for CVSS Authentication. +""" + + +def main(): + print(AUTHENTICATION_1.to_json(indent=2)) + print(AUTHENTICATION_2.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/availability_impact.py b/src/ssvc/decision_points/cvss/availability_impact.py new file mode 100644 index 00000000..7f7f7441 --- /dev/null +++ b/src/ssvc/decision_points/cvss/availability_impact.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +""" +Models the CVSS Availability Impact metric as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_HIGH = SsvcDecisionPointValue( + name="High", + key="H", + description="There is total loss of availability, resulting in the attacker being able to fully deny access to resources in the impacted component; this loss is either sustained (while the attacker continues to deliver the attack) or persistent (the condition persists even after the attack has completed).", +) + +_LOW = SsvcDecisionPointValue( + name="Low", + key="L", + description="There is reduced performance or interruptions in resource availability.", +) + +_NONE_2 = SsvcDecisionPointValue( + name="None", + key="N", + description="There is no impact to the availability of the system.", +) + +_COMPLETE = SsvcDecisionPointValue( + name="Complete", + key="C", + description="Total shutdown of the affected resource. The attacker can render the resource completely unavailable.", +) + +_PARTIAL = SsvcDecisionPointValue( + name="Partial", + key="P", + description="Considerable lag in or interruptions in resource availability. For example, a network-based flood attack that reduces available bandwidth to a web server farm to such an extent that only a small number of connections successfully complete.", +) + +_NONE_1 = SsvcDecisionPointValue( + name="None", key="N", description="No impact on availability." +) + +AVAILABILITY_IMPACT_1 = CvssDecisionPoint( + name="Availability Impact", + description="This metric measures the impact on availability a successful exploit of the vulnerability will have on the target system.", + key="A", + version="1.0.0", + values=( + _NONE_1, + _PARTIAL, + _COMPLETE, + ), +) +""" +Defines None, Partial, and Complete values for CVSS Availability Impact. +""" + +AVAILABILITY_IMPACT_2 = CvssDecisionPoint( + name="Availability Impact", + description="This metric measures the impact to availability of a successfully exploited vulnerability.", + key="A", + version="2.0.0", + values=( + _NONE_2, + _LOW, + _HIGH, + ), +) +""" +Updates None. Removes Partial and Complete. Adds Low and High values for CVSS Availability Impact. +""" + + +def main(): + print(AVAILABILITY_IMPACT_1.to_json(indent=2)) + print(AVAILABILITY_IMPACT_2.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/availability_requirement.py b/src/ssvc/decision_points/cvss/availability_requirement.py new file mode 100644 index 00000000..8d2b2e30 --- /dev/null +++ b/src/ssvc/decision_points/cvss/availability_requirement.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +""" +Models the CVSS Availability Requirement metric as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_NOT_DEFINED = SsvcDecisionPointValue( + name="Not Defined", + key="ND", + description="Assigning this value to the metric will not influence the score. It is a signal to the equation to skip this metric.", +) + +_HIGH = SsvcDecisionPointValue( + name="High", + key="H", + description="Loss of availability is likely to have a catastrophic adverse effect on the organization or individuals associated with the organization (e.g., employees, customers).", +) + +_MEDIUM = SsvcDecisionPointValue( + name="Medium", + key="M", + description="Loss of availability is likely to have a serious adverse effect on the organization or individuals associated with the organization (e.g., employees, customers).", +) + +_LOW = SsvcDecisionPointValue( + name="Low", + key="L", + description="Loss of availability is likely to have only a limited adverse effect on the organization or individuals associated with the organization (e.g., employees, customers).", +) + + +AVAILABILITY_REQUIREMENT_1 = CvssDecisionPoint( + name="Availability Requirement", + description="This metric measures the impact to the availability of a successfully exploited vulnerability.", + key="CR", + version="1.0.0", + values=( + _LOW, + _MEDIUM, + _HIGH, + _NOT_DEFINED, + ), +) +""" +Defines Low, Medium, High, and Not Defined values for CVSS Availability Requirement. +""" + + +def main(): + print(AVAILABILITY_REQUIREMENT_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/base.py b/src/ssvc/decision_points/cvss/base.py new file mode 100644 index 00000000..a993e7e1 --- /dev/null +++ b/src/ssvc/decision_points/cvss/base.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +""" +Provides a base class for modeling CVSS vector metrics as SSVC decision points. +""" + + +from dataclasses import dataclass + +from dataclasses_json import dataclass_json + +from ssvc.decision_points.base import SsvcDecisionPoint + + +@dataclass_json +@dataclass(kw_only=True) +class CvssDecisionPoint(SsvcDecisionPoint): + """ + Models a single CVSS decision point as a list of values. + """ + + namespace: str = "cvss" diff --git a/src/ssvc/decision_points/cvss/collateral_damage_potential.py b/src/ssvc/decision_points/cvss/collateral_damage_potential.py new file mode 100644 index 00000000..01c7594d --- /dev/null +++ b/src/ssvc/decision_points/cvss/collateral_damage_potential.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +""" +Models the CVSS Collateral Damage Potential metric as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_NOT_DEFINED = SsvcDecisionPointValue( + name="Not Defined", + key="ND", + description="Assigning this value to the metric will not influence the score. It is a signal to the equation to skip this metric.", +) + +_MEDIUM_HIGH = SsvcDecisionPointValue( + name="Medium-High", + key="MH", + description="A successful exploit of this vulnerability may result in significant physical or property damage or loss.", +) + +_LOW_MEDIUM = SsvcDecisionPointValue( + name="Low-Medium", + key="LM", + description="A successful exploit of this vulnerability may result in moderate physical or property damage or loss.", +) + +_CDP_NONE_2 = SsvcDecisionPointValue( + name="None", + key="N", + description="There is no potential for loss of life, physical assets, productivity or revenue.", +) + +_HIGH = SsvcDecisionPointValue( + name="High", + key="H", + description="A successful exploit of this vulnerability may result in catastrophic physical or property damage and loss. The range of effect may be over a wide area.", +) + +_MEDIUM = SsvcDecisionPointValue( + name="Medium", + key="M", + description="A successful exploit of this vulnerability may result in significant physical or property damage or loss.", +) + +_LOW = SsvcDecisionPointValue( + name="Low", + key="L", + description="A successful exploit of this vulnerability may result in light physical or property damage or loss. The system itself may be damaged or destroyed.", +) + +_CDP_NONE = SsvcDecisionPointValue( + name="None", + key="N", + description="There is no potential for physical or property damage.", +) + + +COLLATERAL_DAMAGE_POTENTIAL_1 = CvssDecisionPoint( + name="Collateral Damage Potential", + description="This metric measures the potential for a loss in physical equipment, property damage or loss of life or limb.", + key="CDP", + version="1.0.0", + values=( + _CDP_NONE, + _LOW, + _MEDIUM, + _HIGH, + ), +) +""" +Defines None, Low, Medium, and High values for CVSS Collateral Damage Potential. +""" + +COLLATERAL_DAMAGE_POTENTIAL_2 = CvssDecisionPoint( + name="Collateral Damage Potential", + description="This metric measures the potential for loss of life or physical assets.", + key="CDP", + version="2.0.0", + values=( + _CDP_NONE_2, + _LOW_MEDIUM, + _MEDIUM_HIGH, + _HIGH, + _NOT_DEFINED, + ), +) +""" +Updates None description. Adds Low-Medium, Medium-High, and Not Defined value. +""" + + +def main(): + print(COLLATERAL_DAMAGE_POTENTIAL_1.to_json(indent=2)) + print(COLLATERAL_DAMAGE_POTENTIAL_2.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/confidentiality_impact.py b/src/ssvc/decision_points/cvss/confidentiality_impact.py new file mode 100644 index 00000000..1172cccc --- /dev/null +++ b/src/ssvc/decision_points/cvss/confidentiality_impact.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +""" +Models the CVSS Confidentiality Impact metric as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_HIGH = SsvcDecisionPointValue( + name="High", + key="H", + description="There is total loss of confidentiality, resulting in all resources within the impacted component being divulged to the attacker. Alternatively, access to only some restricted information is obtained, but the disclosed information presents a direct, serious impact. For example, an attacker steals the administrator's password, or private encryption keys of a web server.", +) + +_LOW = SsvcDecisionPointValue( + name="Low", + key="L", + description="There is some loss of confidentiality. Access to some restricted information is obtained, but the attacker does not have control over what information is obtained, or the amount or kind of loss is constrained. The information disclosure does not cause a direct, serious loss to the impacted component.", +) + +_CI_NONE_2 = SsvcDecisionPointValue( + name="None", + key="N", + description="There is no loss of confidentiality within the impacted component.", +) + +_COMPLETE = SsvcDecisionPointValue( + name="Complete", + key="C", + description="A total compromise of critical system information. A complete loss of system protection resulting in all critical system files being revealed. The attacker has sovereign control to read all of the system's data (memory, files, etc).", +) + +_PARTIAL = SsvcDecisionPointValue( + name="Partial", + key="P", + description="There is considerable informational disclosure. Access to critical system files is possible. There is a loss of important information, but the attacker doesn't have control over what is obtainable or the scope of the loss is constrained.", +) + +_CI_NONE = SsvcDecisionPointValue( + name="None", + key="N", + description="No impact on confidentiality.", +) + +CONFIDENTIALITY_IMPACT_1 = CvssDecisionPoint( + name="Confidentiality Impact", + description="This metric measures the impact on confidentiality of a successful exploit of the vulnerability on the target system.", + key="C", + version="1.0.0", + values=( + _CI_NONE, + _PARTIAL, + _COMPLETE, + ), +) +""" +Defines None, Partial, and Complete values for CVSS Confidentiality Impact. +""" + +CONFIDENTIALITY_IMPACT_2 = CvssDecisionPoint( + name="Confidentiality Impact", + description="This metric measures the impact to the confidentiality of the information resources managed by a software component due to a successfully exploited vulnerability.", + key="C", + version="2.0.0", + values=( + _CI_NONE_2, + _LOW, + _HIGH, + ), +) +""" +Updates None. Removes Partial and Complete. Adds Low and High values for CVSS Confidentiality Impact. +""" + + +def main(): + print(CONFIDENTIALITY_IMPACT_1.to_json(indent=2)) + print(CONFIDENTIALITY_IMPACT_2.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/confidentiality_requirement.py b/src/ssvc/decision_points/cvss/confidentiality_requirement.py new file mode 100644 index 00000000..26e878fc --- /dev/null +++ b/src/ssvc/decision_points/cvss/confidentiality_requirement.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +""" +Models the CVSS Confidentiality Requirement metric as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_NOT_DEFINED = SsvcDecisionPointValue( + name="Not Defined", + key="ND", + description="Assigning this value to the metric will not influence the score. It is a signal to the equation to skip this metric.", +) + +_HIGH = SsvcDecisionPointValue( + name="High", + key="H", + description="Loss of confidentiality is likely to have a catastrophic adverse effect on the organization or individuals associated with the organization (e.g., employees, customers).", +) + +_MEDIUM = SsvcDecisionPointValue( + name="Medium", + key="M", + description="Loss of confidentiality is likely to have a serious adverse effect on the organization or individuals associated with the organization (e.g., employees, customers).", +) + +_LOW = SsvcDecisionPointValue( + name="Low", + key="L", + description="Loss of confidentiality is likely to have only a limited adverse effect on the organization or individuals associated with the organization (e.g., employees, customers).", +) + +CONFIDENTIALITY_REQUIREMENT_1 = CvssDecisionPoint( + name="Confidentiality Requirement", + description="This metric measures the impact to the confidentiality of a successfully exploited vulnerability.", + key="CR", + version="1.0.0", + values=( + _LOW, + _MEDIUM, + _HIGH, + _NOT_DEFINED, + ), +) +""" +Defines Low, Medium, High, and Not Defined values for CVSS Confidentiality Requirement. +""" + + +def main(): + print(CONFIDENTIALITY_REQUIREMENT_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/exploitability.py b/src/ssvc/decision_points/cvss/exploitability.py new file mode 100644 index 00000000..0bf3c661 --- /dev/null +++ b/src/ssvc/decision_points/cvss/exploitability.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +""" +Model the CVSS Exploitability and Exploit Code Maturity metrics as SSVC decision points. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_NOT_DEFINED_2 = SsvcDecisionPointValue( + name="Not Defined", + key="ND", + description="Assigning this value to the metric will not influence the score. It is a signal to a scoring equation to skip this metric.", +) + +_NOT_DEFINED_1 = SsvcDecisionPointValue( + name="Not Defined", key="ND", description="Not Defined" +) + +_HIGH_2 = SsvcDecisionPointValue( + name="High", + key="H", + description="Functional autonomous code exists, or no exploit is required (manual trigger) and details are widely available. Exploit code works in every situation, or is actively being delivered via an autonomous agent (such as a worm or virus). Network-connected systems are likely to encounter scanning or exploitation attempts. Exploit development has reached the level of reliable, widely-available, easy-to-use automated tools.", +) + +_FUNCTIONAL_2 = SsvcDecisionPointValue( + name="Functional", + key="F", + description="Functional exploit code is available. The code works in most situations where the vulnerability exists.", +) + +_PROOF_OF_CONCEPT_2 = SsvcDecisionPointValue( + name="Proof-of-Concept", + key="POC", + description="Proof-of-concept exploit code is available, or an attack demonstration is not practical for most systems. The code or technique is not functional in all situations and may require substantial modification by a skilled attacker.", +) + +_UNPROVEN_2 = SsvcDecisionPointValue( + name="Unproven", + key="U", + description="No exploit code is available, or an exploit is theoretical.", +) + +_HIGH = SsvcDecisionPointValue( + name="High", + key="H", + description="Either the vulnerability is exploitable by functional mobile autonomous code or no exploit is required (manual trigger) and the details for the manual technique are widely available. The code works in every situation where the vulnerability is exploitable and/or is actively being delivered via a mobile autonomous agent (a worm or virus).", +) + +_FUNCTIONAL = SsvcDecisionPointValue( + name="Functional", + key="F", + description="Functional exploit code is available. The code works in most situations where the vulnerability is exploitable.", +) + +_PROOF_OF_CONCEPT = SsvcDecisionPointValue( + name="Proof of Concept", + key="P", + description="Proof of concept exploit code or an attack demonstration that is not practically applicable to deployed systems is available. The code or technique is not functional in all situations and may require substantial hand tuning by a skilled attacker for use against deployed systems.", +) + +_UNPROVEN = SsvcDecisionPointValue( + name="Unproven", + key="U", + description="No exploit code is yet available or an exploit method is entirely theoretical.", +) + +EXPLOITABILITY_1 = CvssDecisionPoint( + name="Exploitability", + description="This metric measures the current state of exploit technique or code availability and suggests a likelihood of exploitation.", + key="E", + version="1.0.0", + values=( + _UNPROVEN, + _PROOF_OF_CONCEPT, + _FUNCTIONAL, + _HIGH, + ), +) +""" +Defines Unproven, Proof of Concept, Functional, and High values for CVSS Exploitability. +""" + +EXPLOITABILITY_1_1 = CvssDecisionPoint( + name="Exploitability", + description="This metric measures the current state of exploit technique or code availability and suggests a likelihood of exploitation.", + key="E", + version="1.1.0", + values=( + _UNPROVEN, + _PROOF_OF_CONCEPT, + _FUNCTIONAL, + _HIGH, + _NOT_DEFINED_1, + ), +) +""" +Introduced Not Defined value. +""" + + +# TODO is it correct to inherit the version from Exploitability? +# TODO Or is it better for Exploit Code Maturity to reset to 1.0.0? + +EXPLOIT_CODE_MATURITY_1_1_1 = CvssDecisionPoint( + name="Exploit Code Maturity", + description="measures the likelihood of the vulnerability being attacked, and is typically based on the current state of exploit techniques, exploit code availability, or active, 'in-the-wild' exploitation", + key="E", + version="1.1.1", + values=( + _UNPROVEN_2, + _PROOF_OF_CONCEPT_2, + _FUNCTIONAL_2, + _HIGH_2, + _NOT_DEFINED_2, + ), +) +""" +Name changed to Exploit Code Maturity. Updated descriptions of values. +""" + + +def main(): + print(EXPLOITABILITY_1.to_json(indent=2)) + print(EXPLOITABILITY_1_1.to_json(indent=2)) + print(EXPLOIT_CODE_MATURITY_1_1_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/impact_bias.py b/src/ssvc/decision_points/cvss/impact_bias.py new file mode 100644 index 00000000..7ab5a993 --- /dev/null +++ b/src/ssvc/decision_points/cvss/impact_bias.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +""" +Model the CVSS Impact Bias as an SSVC decision point. +""" +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_AVAILABILITY = SsvcDecisionPointValue( + name="Availability", + key="A", + description="Availability Impact is assigned greater weight than Confidentiality Impact or Integrity Impact.", +) + +_INTEGRITY = SsvcDecisionPointValue( + name="Integrity", + key="I", + description="Integrity Impact is assigned greater weight than Confidentiality Impact or Availability Impact.", +) + +_CONFIDENTIALITY = SsvcDecisionPointValue( + name="Confidentiality", + key="C", + description="Confidentiality impact is assigned greater weight than Integrity Impact or Availability Impact.", +) + +_NORMAL = SsvcDecisionPointValue( + name="Normal", + key="N", + description="Confidentiality Impact, Integrity Impact, and Availability Impact are all assigned the same weight.", +) + +IMPACT_BIAS_1 = CvssDecisionPoint( + name="Impact Bias", + description="This metric measures the impact bias of the vulnerability.", + key="IB", + version="1.0.0", + values=( + _NORMAL, + _CONFIDENTIALITY, + _INTEGRITY, + _AVAILABILITY, + ), +) +""" +Defines Normal, Confidentiality, Integrity, and Availability values for CVSS Impact Bias. +""" + + +def main(): + print(IMPACT_BIAS_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/integrity_impact.py b/src/ssvc/decision_points/cvss/integrity_impact.py new file mode 100644 index 00000000..33586f09 --- /dev/null +++ b/src/ssvc/decision_points/cvss/integrity_impact.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +""" +Models the CVSS Integrity Impact metric as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue + +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_II_HIGH = SsvcDecisionPointValue( + name="High", + key="H", + description="There is a total loss of integrity, or a complete loss of protection. For example, the attacker is able to modify any/all files protected by the impacted component. Alternatively, only some files can be modified, but malicious modification would present a direct, serious consequence to the impacted component.", +) + +_II_LOW = SsvcDecisionPointValue( + name="Low", + key="L", + description="Modification of data is possible, but the attacker does not have control over the consequence of a modification, or the amount of modification is constrained. The data modification does not have a direct, serious impact on the impacted component.", +) + +_II_NONE_2 = SsvcDecisionPointValue( + name="None", + key="N", + description="There is no impact to the integrity of the system.", +) +_COMPLETE = SsvcDecisionPointValue( + name="Complete", + key="C", + description="A total compromise of system integrity. There is a complete loss of system protection resulting in the entire system being compromised. The attacker has sovereign control to modify any system files.", +) + +_PARTIAL = SsvcDecisionPointValue( + name="Partial", + key="P", + description="Considerable breach in integrity. Modification of critical system files or information is possible, but the attacker does not have control over what can be modified, or the scope of what the attacker can affect is constrained. For example, key system or program files may be overwritten or modified, but at random or in a limited context or scope.", +) + +_II_NONE = SsvcDecisionPointValue( + name="None", key="N", description="No impact on integrity." +) + +INTEGRITY_IMPACT_1 = CvssDecisionPoint( + name="Integrity Impact", + description="This metric measures the impact on integrity a successful exploit of the vulnerability will have on the target system.", + key="I", + version="1.0.0", + values=( + _II_NONE, + _PARTIAL, + _COMPLETE, + ), +) +""" +Defines None, Partial, and Complete values for CVSS Integrity Impact. +""" + +INTEGRITY_IMPACT_2 = CvssDecisionPoint( + name="Integrity Impact", + description="This metric measures the impact to integrity of a successfully exploited vulnerability.", + key="I", + version="2.0.0", + values=( + _II_NONE_2, + _II_LOW, + _II_HIGH, + ), +) +""" +Updates None. Removes Partial and Complete. Adds Low and High values for CVSS Integrity Impact. +""" + + +def main(): + print(INTEGRITY_IMPACT_1.to_json(indent=2)) + print(INTEGRITY_IMPACT_2.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/integrity_requirement.py b/src/ssvc/decision_points/cvss/integrity_requirement.py new file mode 100644 index 00000000..bed51253 --- /dev/null +++ b/src/ssvc/decision_points/cvss/integrity_requirement.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +""" +Models the CVSS Integrity Requirement metric as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_NOT_DEFINED = SsvcDecisionPointValue( + name="Not Defined", + key="ND", + description="Assigning this value to the metric will not influence the score. It is a signal to the equation to skip this metric.", +) + +_HIGH = SsvcDecisionPointValue( + name="High", + key="H", + description="Loss of integrity is likely to have a catastrophic adverse effect on the organization or individuals associated with the organization (e.g., employees, customers).", +) + +_MEDIUM = SsvcDecisionPointValue( + name="Medium", + key="M", + description="Loss of integrity is likely to have a serious adverse effect on the organization or individuals associated with the organization (e.g., employees, customers).", +) + +_LOW = SsvcDecisionPointValue( + name="Low", + key="L", + description="Loss of integrity is likely to have only a limited adverse effect on the organization or individuals associated with the organization (e.g., employees, customers).", +) + +INTEGRITY_REQUIREMENT_1 = CvssDecisionPoint( + name="Integrity Requirement", + description="This metric measures the impact to the integrity of a successfully exploited vulnerability.", + key="CR", + version="1.0.0", + values=( + _LOW, + _MEDIUM, + _HIGH, + _NOT_DEFINED, + ), +) +""" +Defines Low, Medium, High, and Not Defined values for CVSS Integrity Requirement. +""" + + +def main(): + print(INTEGRITY_REQUIREMENT_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/privileges_required.py b/src/ssvc/decision_points/cvss/privileges_required.py new file mode 100644 index 00000000..8c71f61b --- /dev/null +++ b/src/ssvc/decision_points/cvss/privileges_required.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +""" +Models the CVSS Privileges Required metric as an SSVC decision point. +""" +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_HIGH = SsvcDecisionPointValue( + name="High", + key="H", + description="The attacker is authorized with (i.e. requires) privileges that provide significant (e.g. administrative) control over the vulnerable component that could affect component-wide settings and files.", +) + +_LOW = SsvcDecisionPointValue( + name="Low", + key="L", + description="The attacker is authorized with (i.e. requires) privileges that provide basic user capabilities that could normally affect only settings and files owned by a user. Alternatively, an attacker with Low privileges may have the ability to cause an impact only to non-sensitive resources.", +) + +_PR_NONE = SsvcDecisionPointValue( + name="None", + key="N", + description="The attacker is unauthorized prior to attack, and therefore does not require any access to settings or files to carry out an attack.", +) + +PRIVILEGES_REQUIRED_1 = CvssDecisionPoint( + name="Privileges Required", + description="This metric describes the level of privileges an attacker must possess before successfully exploiting the vulnerability.", + key="PR", + version="1.0.0", + values=( + _PR_NONE, + _LOW, + _HIGH, + ), +) +""" +Defines None, Low, and High values for CVSS Privileges Required. +""" + + +def main(): + print(PRIVILEGES_REQUIRED_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/remediation_level.py b/src/ssvc/decision_points/cvss/remediation_level.py new file mode 100644 index 00000000..ad3e6b0c --- /dev/null +++ b/src/ssvc/decision_points/cvss/remediation_level.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +""" +Models the CVSS Remediation Level metric as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_NOT_DEFINED = SsvcDecisionPointValue( + name="Not Defined", + key="ND", + description="Assigning this value to the metric will not influence the score. It is a signal to the equation to skip this metric.", +) + +_UNAVAILABLE = SsvcDecisionPointValue( + name="Unavailable", + key="U", + description="There is either no solution available or it is impossible to apply.", +) + +_WORKAROUND = SsvcDecisionPointValue( + name="Workaround", + key="W", + description="There is an unofficial, non-vendor solution available. In some cases, users of the affected technology will create a patch of their own or provide steps to work around or otherwise mitigate against the vulnerability. When it is generally accepted that these unofficial fixes are adequate in plugging the hole for the mean time and no official remediation is available, this value can be set.", +) + +_TEMPORARY_FIX = SsvcDecisionPointValue( + name="Temporary Fix", + key="TF", + description="There is an official but temporary fix available. This includes instances where the vendor issues a temporary hotfix, tool or official workaround.", +) + +_OFFICIAL_FIX = SsvcDecisionPointValue( + name="Official Fix", + key="OF", + description="A complete vendor solution is available. Either the vendor has issued the final, official patch which eliminates the vulnerability or an upgrade that is not vulnerable is available.", +) + +REMEDIATION_LEVEL_1 = CvssDecisionPoint( + name="Remediation Level", + description="This metric measures the remediation status of a vulnerability.", + key="RL", + version="1.0.0", + values=( + _OFFICIAL_FIX, + _TEMPORARY_FIX, + _WORKAROUND, + _UNAVAILABLE, + ), +) +""" +Defines Official Fix, Temporary Fix, Workaround, and Unavailable values for CVSS Remediation Level. +""" + +REMEDIATION_LEVEL_1_1 = CvssDecisionPoint( + name="Remediation Level", + description="This metric measures the remediation status of a vulnerability.", + key="RL", + version="1.1.0", + values=( + _OFFICIAL_FIX, + _TEMPORARY_FIX, + _WORKAROUND, + _UNAVAILABLE, + _NOT_DEFINED, + ), +) +""" +Adds Not Defined to the CVSS Remediation Level decision point. +""" + + +def main(): + print(REMEDIATION_LEVEL_1.to_json(indent=2)) + print(REMEDIATION_LEVEL_1_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/report_confidence.py b/src/ssvc/decision_points/cvss/report_confidence.py new file mode 100644 index 00000000..9c4c0a35 --- /dev/null +++ b/src/ssvc/decision_points/cvss/report_confidence.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +""" +Models the CVSS Report Confidence metric as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_NOT_DEFINED = SsvcDecisionPointValue( + name="Not Defined", + key="ND", + description="Assigning this value to the metric will not influence the score. It is a signal to the equation to skip this metric.", +) + +_CONFIRMED_2 = SsvcDecisionPointValue( + name="Confirmed", + key="C", + description="Detailed reports exist, or functional reproduction is possible (functional exploits may provide this). Source code is available to independently verify the assertions of the research, or the author or vendor of the affected code has confirmed the presence of the vulnerability.", +) + +_REASONABLE = SsvcDecisionPointValue( + name="Reasonable", + key="R", + description="Significant details are published, but researchers either do not have full confidence in the root cause, or do not have access to source code to fully confirm all of the interactions that may lead to the result. Reasonable confidence exists, however, that the bug is reproducible and at least one impact is able to be verified (proof-of-concept exploits may provide this). An example is a detailed write-up of research into a vulnerability with an explanation (possibly obfuscated or 'left as an exercise to the reader') that gives assurances on how to reproduce the results.", +) + +_UNKNOWN = SsvcDecisionPointValue( + name="Unknown", + key="U", + description="There are reports of impacts that indicate a vulnerability is present. The reports indicate that the cause of the vulnerability is unknown, or reports may differ on the cause or impacts of the vulnerability. Reporters are uncertain of the true nature of the vulnerability, and there is little confidence in the validity of the reports or whether a static Base score can be applied given the differences described. An example is a bug report which notes that an intermittent but non-reproducible crash occurs, with evidence of memory corruption suggesting that denial of service, or possible more serious impacts, may result.", +) + +_CONFIRMED = SsvcDecisionPointValue( + name="Confirmed", + key="C", + description="Vendor or author of the affected technology has acknowledged that the vulnerability exists. This value may also be set when existence of a vulnerability is confirmed with absolute confidence through some other event, such as publication of functional proof of concept exploit code or widespread exploitation.", +) + +_UNCORROBORATED = SsvcDecisionPointValue( + name="Uncorroborated", + key="UR", + description="Multiple non-official sources; possibily including independent security companies or research organizations. At this point there may be conflicting technical details or some other lingering ambiguity.", +) + +_UNCONFIRMED = SsvcDecisionPointValue( + name="Unconfirmed", + key="UC", + description="A single unconfirmed source or possibly several conflicting reports. There is little confidence in the validity of the report. For example, a rumor that surfaces from the hacker underground.", +) + +REPORT_CONFIDENCE_1 = CvssDecisionPoint( + name="Report Confidence", + description="This metric measures the degree of confidence in the existence of the vulnerability and the credibility of the known technical details.", + key="RC", + version="1.0.0", + values=( + _UNCONFIRMED, + _UNCORROBORATED, + _CONFIRMED, + ), +) +""" +Defines Unconfirmed, Uncorroborated, and Confirmed values for CVSS Report Confidence. +""" + +REPORT_CONFIDENCE_1_1 = CvssDecisionPoint( + name="Report Confidence", + description="This metric measures the degree of confidence in the existence of the vulnerability and the credibility of the known technical details.", + key="RC", + version="1.1.0", + values=( + _UNCONFIRMED, + _UNCORROBORATED, + _CONFIRMED, + _NOT_DEFINED, + ), +) +""" +Adds Not Defined to the CVSS Report Confidence decision point. +""" + +REPORT_CONFIDENCE_2 = CvssDecisionPoint( + name="Report Confidence", + description="This metric measures the degree of confidence in the existence of the vulnerability and the credibility of the known technical details.", + key="RC", + version="2.0.0", + values=( + _UNKNOWN, + _REASONABLE, + _CONFIRMED_2, + _NOT_DEFINED, + ), +) +""" +Replaces Unconfirmed and Uncorroborated with Unknown and Reasonable. Updated Confirmed. Retains Not Defined. +""" + + +def main(): + print(REPORT_CONFIDENCE_1.to_json(indent=2)) + print(REPORT_CONFIDENCE_1_1.to_json(indent=2)) + print(REPORT_CONFIDENCE_2.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/scope.py b/src/ssvc/decision_points/cvss/scope.py new file mode 100644 index 00000000..5da298b8 --- /dev/null +++ b/src/ssvc/decision_points/cvss/scope.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +""" +Models CVSS Scope as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_CHANGED = SsvcDecisionPointValue( + name="Changed", + key="C", + description="An exploited vulnerability can affect resources beyond the authorization privileges intended by the vulnerable component. In this case the vulnerable component and the impacted component are different.", +) + +_UNCHANGED = SsvcDecisionPointValue( + name="Unchanged", + key="U", + description="An exploited vulnerability can only affect resources managed by the same authority. In this case the vulnerable component and the impacted component are the same.", +) + +SCOPE_1 = CvssDecisionPoint( + name="Scope", + description="the ability for a vulnerability in one software component to impact resources beyond its means, or privileges", + key="S", + version="1.0.0", + values=( + _UNCHANGED, + _CHANGED, + ), +) +""" +Defines Changed and Unchanged values for CVSS Scope. +""" + + +def main(): + print(SCOPE_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/target_distribution.py b/src/ssvc/decision_points/cvss/target_distribution.py new file mode 100644 index 00000000..ba0da97d --- /dev/null +++ b/src/ssvc/decision_points/cvss/target_distribution.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +""" +Models CVSS Target Distribution as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_NOT_DEFINED = SsvcDecisionPointValue( + name="Not Defined", + key="ND", + description="This value is not defined in any specification, but is used in the CVSS v3.0 vector string when a value is required by the specification, but cannot be provided. This is a special case value and should be used sparingly.", +) + +_HIGH = SsvcDecisionPointValue( + name="High", + key="H", + description="Targets exist inside the environment on a considerable scale. Between 50% - 100% of the total environment is considered at risk.", +) + +_MEDIUM = SsvcDecisionPointValue( + name="Medium", + key="M", + description="Targets exist inside the environment, but on a medium scale. Between 16% - 49% of the total environment is at risk.", +) + +_LOW = SsvcDecisionPointValue( + name="Low", + key="L", + description="Targets exist inside the environment, but on a small scale. Between 1% - 15% of the total environment is at risk.", +) + +_TD_NONE = SsvcDecisionPointValue( + name="None", + key="N", + description="No target systems exist, or targets are so highly specialized that they only exist in a laboratory setting. Effectively 0% of the environment is at risk.", +) + +TARGET_DISTRIBUTION_1 = CvssDecisionPoint( + name="Target Distribution", + description="This metric measures the relative size of the field of target systems susceptible to the vulnerability. It is meant as an environment-specific indicator in order to approximate the percentage of systems within the environment that could be affected by the vulnerability.", + key="TD", + version="1.0.0", + values=( + _TD_NONE, + _LOW, + _MEDIUM, + _HIGH, + ), +) +""" +Defines None, Low, Medium, and High values for CVSS Target Distribution. +""" + +TARGET_DISTRIBUTION_1_1 = CvssDecisionPoint( + name="Target Distribution", + description="This metric measures the relative size of the field of target systems susceptible to the vulnerability. It is meant as an environment-specific indicator in order to approximate the percentage of systems within the environment that could be affected by the vulnerability.", + key="TD", + version="1.1.0", + values=( + _TD_NONE, + _LOW, + _MEDIUM, + _HIGH, + _NOT_DEFINED, + ), +) +""" +Introduces Not Defined value. +""" + + +def main(): + print(TARGET_DISTRIBUTION_1.to_json(indent=2)) + print(TARGET_DISTRIBUTION_1_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/cvss/user_interaction.py b/src/ssvc/decision_points/cvss/user_interaction.py new file mode 100644 index 00000000..97000018 --- /dev/null +++ b/src/ssvc/decision_points/cvss/user_interaction.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +""" +Models CVSS User Interaction as an SSVC decision point. +""" + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.base import CvssDecisionPoint + +_REQUIRED = SsvcDecisionPointValue( + name="Required", + key="R", + description="Successful exploitation of this vulnerability requires a user to take some action before the vulnerability can be exploited.", +) + +_UI_NONE = SsvcDecisionPointValue( + name="None", + key="N", + description="The vulnerable system can be exploited without interaction from any user.", +) + + +USER_INTERACTION_1 = CvssDecisionPoint( + name="User Interaction", + description="This metric captures the requirement for a user, other than the attacker, to participate in the successful compromise of the vulnerable component.", + key="UI", + version="1.0.0", + values=( + _UI_NONE, + _REQUIRED, + ), +) +""" +Defines None and Required values for CVSS User Interaction. +""" + + +def main(): + print(USER_INTERACTION_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/exploitation.py b/src/ssvc/decision_points/exploitation.py new file mode 100644 index 00000000..050ea51e --- /dev/null +++ b/src/ssvc/decision_points/exploitation.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +""" +file: exploitation +author: adh +created_at: 9/20/23 11:41 AM +""" +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +ACTIVE = SsvcDecisionPointValue( + name="Active", + key="A", + description="Shared, observable, reliable evidence that the exploit is being" + " used in the wild by real attackers; there is credible public reporting.", +) + +POC = SsvcDecisionPointValue( + name="PoC", + key="P", + description="One of the following cases is true: (1) private evidence of exploitation is attested but not shared; " + "(2) widespread hearsay attests to exploitation; (3) typical public PoC in places such as Metasploit" + " or ExploitDB; or (4) the vulnerability has a well-known method of exploitation.", +) + +EXP_NONE = SsvcDecisionPointValue( + name="None", + key="N", + description="There is no evidence of active exploitation and no public proof of concept (PoC) of how to exploit the vulnerability.", +) + + +def _strip_spaces(s): + return " ".join([x.strip() for x in s.splitlines()]) + + +EXPLOITATION_1 = SsvcDecisionPoint( + name="Exploitation", + description="The present state of exploitation of the vulnerability.", + key="E", + version="1.0.0", + values=( + EXP_NONE, + POC, + ACTIVE, + ), +) + + +def main(): + print(EXPLOITATION_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/high_value_asset.py b/src/ssvc/decision_points/high_value_asset.py new file mode 100644 index 00000000..381b00c7 --- /dev/null +++ b/src/ssvc/decision_points/high_value_asset.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +""" +Models a high value asset as a decision point. +""" +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +YES = SsvcDecisionPointValue( + name="Yes", + key="Y", + description="System meets a high value asset definition.", +) + +NO = SsvcDecisionPointValue( + name="No", + key="N", + description="System does not meet a high value asset definition.", +) + +HIGH_VALUE_ASSET_1 = SsvcDecisionPoint( + name="High Value Asset", + description="Denotes whether a system meets a high value asset definition.", + namespace="SSVC", + key="HVA", + version="1.0.0", + values=( + NO, + YES, + ), +) + + +def main(): + print(HIGH_VALUE_ASSET_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/human_impact.py b/src/ssvc/decision_points/human_impact.py new file mode 100644 index 00000000..355f524a --- /dev/null +++ b/src/ssvc/decision_points/human_impact.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +""" +file: human_impact +author: adh +created_at: 9/21/23 10:49 AM +""" +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +VERY_HIGH = SsvcDecisionPointValue( + name="Very High", + key="VH", + description="Safety=Catastrophic OR Mission=Mission Failure", +) + +HIGH = SsvcDecisionPointValue( + name="High", + key="H", + description="Safety=Hazardous, Mission=None/Degraded/Crippled/MEF Failure OR Safety=Major, Mission=MEF Failure", +) + +MEDIUM = SsvcDecisionPointValue( + name="Medium", + key="M", + description="Safety=None/Minor, Mission=MEF Failure OR Safety=Major, Mission=None/Degraded/Crippled", +) + +LOW = SsvcDecisionPointValue( + name="Low", + key="L", + description="Safety=None/Minor, Mission=None/Degraded/Crippled", +) + +HUMAN_IMPACT_1 = SsvcDecisionPoint( + name="Human Impact", + description="Human Impact", + key="HI", + version="1.0.0", + values=( + LOW, + MEDIUM, + HIGH, + VERY_HIGH, + ), +) + + +def main(): + print(HUMAN_IMPACT_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/mission_impact.py b/src/ssvc/decision_points/mission_impact.py new file mode 100644 index 00000000..d0f29808 --- /dev/null +++ b/src/ssvc/decision_points/mission_impact.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +""" +file: mission_impact +author: adh +created_at: 9/21/23 10:20 AM +""" +from copy import deepcopy + +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + + +MISSION_FAILURE = SsvcDecisionPointValue( + name="Mission Failure", + key="MF", + description="Multiple or all mission essential functions fail; ability to recover those functions degraded; organization’s ability to deliver its overall mission fails", +) + +MEF_FAILURE = SsvcDecisionPointValue( + name="MEF Failure", + key="MEF", + description="Any one mission essential function fails for period of time longer than acceptable; overall mission of the organization degraded but can still be accomplished for a time", +) + +MEF_CRIPPLED = SsvcDecisionPointValue( + name="MEF Support Crippled", + key="MSC", + description="Activities that directly support essential functions are crippled; essential functions continue for a time", +) + + +MI_NED = SsvcDecisionPointValue( + name="Non-Essential Degraded", + key="NED", + description="Degradation of non-essential functions; chronic degradation would eventually harm essential functions", +) + +MI_NONE = SsvcDecisionPointValue( + name="None", key="N", description="Little to no impact" +) + +# combine MI_NONE and MI_NED into a single value +DEGRADED = SsvcDecisionPointValue( + name="Degraded", + key="D", + description="Little to no impact up to degradation of non-essential functions; chronic degradation would eventually harm essential functions", +) + + +MISSION_IMPACT_1 = SsvcDecisionPoint( + name="Mission Impact", + description="Impact on Mission Essential Functions of the Organization", + key="MI", + version="1.0.0", + values=( + MI_NONE, + MI_NED, + MEF_CRIPPLED, + MEF_FAILURE, + MISSION_FAILURE, + ), +) + +# SSVC v2.1 combined None and Non-Essential Degraded into a single value +MISSION_IMPACT_2 = deepcopy(MISSION_IMPACT_1) +MISSION_IMPACT_2.version = "2.0.0" +MISSION_IMPACT_2.values = (DEGRADED, MEF_CRIPPLED, MEF_FAILURE, MISSION_FAILURE) + + +def main(): + print(MISSION_IMPACT_1.to_json(indent=2)) + print(MISSION_IMPACT_2.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/public_safety_impact.py b/src/ssvc/decision_points/public_safety_impact.py new file mode 100644 index 00000000..6a1b6104 --- /dev/null +++ b/src/ssvc/decision_points/public_safety_impact.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +""" +file: public_safety_impact +author: adh +created_at: 9/21/23 10:43 AM +""" +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +SIGNIFICANT = SsvcDecisionPointValue( + name="Significant", + description="Safety impact of Major, Hazardous, or Catastrophic.", + key="S", +) + +MINIMAL = SsvcDecisionPointValue( + name="Minimal", description="Safety impact of None or Minor.", key="M" +) + +PUBLIC_SAFETY_IMPACT_1 = SsvcDecisionPoint( + name="Public Safety Impact", + description="A coarse-grained representation of impact to public safety.", + key="PSI", + version="1.0.0", + values=( + MINIMAL, + SIGNIFICANT, + ), +) + + +def main(): + print(PUBLIC_SAFETY_IMPACT_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/public_value_added.py b/src/ssvc/decision_points/public_value_added.py new file mode 100644 index 00000000..31385d57 --- /dev/null +++ b/src/ssvc/decision_points/public_value_added.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +""" +file: public_value_added +author: adh +created_at: 9/21/23 11:27 AM +""" + +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +LIMITED = SsvcDecisionPointValue( + name="Limited", + key="L", + description="Minimal value added to the existing public information because existing information is already high quality and in multiple outlets.", +) + +AMPLIATIVE = SsvcDecisionPointValue( + name="Ampliative", + key="A", + description="Amplifies and/or augments the existing public information about the vulnerability, for example, adds additional detail, addresses or corrects errors in other public information, draws further attention to the vulnerability, etc.", +) + +PRECEDENCE = SsvcDecisionPointValue( + name="Precedence", + key="P", + description="The publication would be the first publicly available, or be coincident with the first publicly available.", +) + +PUBLIC_VALUE_ADDED_1 = SsvcDecisionPoint( + name="Public Value Added", + description="How much value would a publication from the coordinator benefit the broader community?", + key="PVA", + version="1.0.0", + values=(PRECEDENCE, AMPLIATIVE, LIMITED), +) + + +def main(): + print(PUBLIC_VALUE_ADDED_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/report_credibility.py b/src/ssvc/decision_points/report_credibility.py new file mode 100644 index 00000000..38722be5 --- /dev/null +++ b/src/ssvc/decision_points/report_credibility.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +""" +file: report_credibility +author: adh +created_at: 9/21/23 11:24 AM +""" + +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +NOT_CREDIBLE = SsvcDecisionPointValue( + name="Not Credible", + key="NC", + description="The report is not credible.", +) + +CREDIBLE = SsvcDecisionPointValue( + name="Credible", + key="C", + description="The report is credible.", +) + +REPORT_CREDIBILITY_1 = SsvcDecisionPoint( + name="Report Credibility", + description="Is the report credible?", + key="RC", + version="1.0.0", + values=( + CREDIBLE, + NOT_CREDIBLE, + ), +) + + +def main(): + print(REPORT_CREDIBILITY_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/report_public.py b/src/ssvc/decision_points/report_public.py new file mode 100644 index 00000000..4141ae89 --- /dev/null +++ b/src/ssvc/decision_points/report_public.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +""" +file: report_public +author: adh +created_at: 9/21/23 11:15 AM +""" +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +YES = SsvcDecisionPointValue( + name="Yes", + key="Y", + description="A public report of the vulnerability exists.", +) + +NO = SsvcDecisionPointValue( + name="No", + key="N", + description="No public report of the vulnerability exists.", +) + +REPORT_PUBLIC_1 = SsvcDecisionPoint( + name="Report Public", + description="Is a viable report of the details of the vulnerability already publicly available?", + key="RP", + version="1.0.0", + values=( + NO, + YES, + ), +) + + +def main(): + print(REPORT_PUBLIC_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/safety_impact.py b/src/ssvc/decision_points/safety_impact.py new file mode 100644 index 00000000..42466d05 --- /dev/null +++ b/src/ssvc/decision_points/safety_impact.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +""" +file: safety_impact +author: adh +created_at: 9/21/23 10:05 AM +""" +from copy import deepcopy + +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +CATASTROPHIC = SsvcDecisionPointValue( + name="Catastrophic", + key="C", + description="Any one or more of these conditions hold. " + "Physical harm: Multiple immediate fatalities (Emergency response probably cannot save the victims.) " + "Operator resiliency: Operator incapacitated (includes fatality or otherwise incapacitated). " + "System resiliency: Total loss of whole cyber-physical system, of which the software is a part. " + "Environment: Extreme externalities (immediate public health threat, environmental damage leading to small ecosystem collapse, etc.) imposed on other parties. " + "Financial: Social systems (elections, financial grid, etc.) supported by the software collapse. " + "Psychological: N/A.", +) + +HAZARDOUS = SsvcDecisionPointValue( + name="Hazardous", + key="H", + description="Any one or more of these conditions hold. " + "Physical harm: Serious or fatal injuries, where fatalities are plausibly preventable via emergency services or other measures. " + "Operator resiliency: Actions that would keep the system in a safe state are beyond system operator capabilities, resulting in adverse conditions; OR great physical distress to system operators such that they cannot be expected to operate the system properly. " + "System resiliency: Parts of the cyber-physical system break; system’s ability to recover lost functionality remains intact. " + "Environment: Serious externalities (threat to life as well as property, widespread environmental damage, measurable public health risks, etc.) imposed on other parties. " + "Financial: Socio-technical system (elections, financial grid, etc.) of which the affected component is a part is actively destabilized and enters unsafe state. " + "Psychological: N/A.", +) + +MAJOR = SsvcDecisionPointValue( + name="Major", + key="J", + description="Any one or more of these conditions hold. " + "Physical harm: Physical distress and injuries for users (not operators) of the system. " + "Operator resiliency: Requires action by system operator to maintain safe system state as a result of exploitation of the " + "vulnerability where operator actions would be within their capabilities but the actions require their full attention and effort; OR significant distraction or discomfort to operators; OR causes significant occupational safety hazard. " + "System resiliency: System safety margin effectively eliminated but no actual harm; OR failure of system functional capabilities that support safe operation. " + "Environment: Major externalities (property damage, environmental damage, etc.) imposed on other parties. " + "Financial: Financial losses that likely lead to bankruptcy of multiple persons. " + "Psychological: Widespread emotional or psychological harm, sufficient to be cause for counselling or therapy, to populations of people.", +) + +MINOR = SsvcDecisionPointValue( + name="Minor", + key="M", + description="Any one or more of these conditions hold. " + "Physical harm: Physical discomfort for users (not operators) of the system. " + "Operator resiliency: Requires action by system operator to maintain safe system state as a result of exploitation of the " + "vulnerability where operator actions would be well within expected operator abilities; OR causes a minor occupational safety hazard. " + "System resiliency: Small reduction in built-in system safety margins; OR small reduction in system functional capabilities that support safe operation. " + "Environment Minor externalities (property damage, environmental damage, etc.) imposed on other parties. " + "Financial Financial losses, which are not readily absorbable, to multiple persons. " + "Psychological: Emotional or psychological harm, sufficient to be cause for counselling or therapy, to multiple persons.", +) + +SAF_NONE = SsvcDecisionPointValue( + name="None", + key="N", + description="The effect is below the threshold for all aspects described in Minor.", +) + +SAFETY_IMPACT_1 = SsvcDecisionPoint( + name="Safety Impact", + description="The safety impact of the vulnerability.", + key="SI", + version="1.0.0", + values=( + SAF_NONE, + MINOR, + MAJOR, + HAZARDOUS, + CATASTROPHIC, + ), +) + + +def main(): + print(SAFETY_IMPACT_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/supplier_cardinality.py b/src/ssvc/decision_points/supplier_cardinality.py new file mode 100644 index 00000000..ebde9d27 --- /dev/null +++ b/src/ssvc/decision_points/supplier_cardinality.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +""" +file: supplier_cardinality +author: adh +created_at: 9/21/23 11:20 AM +""" +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +MULTIPLE = SsvcDecisionPointValue( + name="Multiple", + key="M", + description="There are multiple suppliers of the vulnerable component.", +) + +ONE = SsvcDecisionPointValue( + name="One", + key="O", + description="There is only one supplier of the vulnerable component.", +) + +SUPPLIER_CARDINALITY_1 = SsvcDecisionPoint( + name="Supplier Cardinality", + description="How many suppliers are responsible for the vulnerable component and its remediation or mitigation plan?", + key="SC", + version="1.0.0", + values=( + ONE, + MULTIPLE, + ), +) + + +def main(): + print(SUPPLIER_CARDINALITY_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/supplier_contacted.py b/src/ssvc/decision_points/supplier_contacted.py new file mode 100644 index 00000000..eff08419 --- /dev/null +++ b/src/ssvc/decision_points/supplier_contacted.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +""" +file: supplier_contacted +author: adh +created_at: 9/21/23 11:17 AM +""" +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +YES = SsvcDecisionPointValue( + name="Yes", + key="Y", + description="The supplier has been contacted.", +) + +NO = SsvcDecisionPointValue( + name="No", + key="N", + description="The supplier has not been contacted.", +) + +SUPPLIER_CONTACTED_1 = SsvcDecisionPoint( + name="Supplier Contacted", + description="Has the reporter made a good-faith effort to contact the supplier of the vulnerable component using a quality contact method?", + key="SC", + version="1.0.0", + values=( + NO, + YES, + ), +) + + +def main(): + print(SUPPLIER_CONTACTED_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/supplier_engagement.py b/src/ssvc/decision_points/supplier_engagement.py new file mode 100644 index 00000000..69380931 --- /dev/null +++ b/src/ssvc/decision_points/supplier_engagement.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +""" +file: supplier_engagement +author: adh +created_at: 9/21/23 11:22 AM +""" + +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +UNRESPONSIVE = SsvcDecisionPointValue( + name="Unresponsive", + key="U", + description="The supplier is not responding to the reporter’s contact effort and not actively participating in the coordination effort.", +) + +ACTIVE = SsvcDecisionPointValue( + name="Active", + key="A", + description="The supplier is responding to the reporter’s contact effort and actively participating in the coordination effort.", +) + +SUPPLIER_ENGAGEMENT_1 = SsvcDecisionPoint( + name="Supplier Engagement", + description="Is the supplier responding to the reporter’s contact effort and actively participating in the coordination effort?", + key="SE", + version="1.0.0", + values=( + ACTIVE, + UNRESPONSIVE, + ), +) + + +def main(): + print(SUPPLIER_ENGAGEMENT_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/supplier_involvement.py b/src/ssvc/decision_points/supplier_involvement.py new file mode 100644 index 00000000..afc3ce07 --- /dev/null +++ b/src/ssvc/decision_points/supplier_involvement.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +""" +file: supplier_involvement +author: adh +created_at: 9/21/23 11:28 AM +""" + +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +UNCOOPERATIVE = SsvcDecisionPointValue( + name="Uncooperative/Unresponsive", + key="UU", + description="The supplier has not responded, declined to generate a remediation, or no longer exists.", +) + +COOPERATIVE = SsvcDecisionPointValue( + name="Cooperative", + key="C", + description="The supplier is actively generating a patch or fix; they may or may not have provided a mitigation or work-around in the mean time.", +) + +FIX_READY = SsvcDecisionPointValue( + name="Fix Ready", + key="FR", + description="The supplier has provided a patch or fix.", +) + +SUPPLIER_INVOLVEMENT_1 = SsvcDecisionPoint( + name="Supplier Involvement", + description="What is the state of the supplier’s work on addressing the vulnerability?", + key="SI", + version="1.0.0", + values=( + FIX_READY, + COOPERATIVE, + UNCOOPERATIVE, + ), +) + + +def main(): + print(SUPPLIER_INVOLVEMENT_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/system_exposure.py b/src/ssvc/decision_points/system_exposure.py new file mode 100644 index 00000000..c5a1978b --- /dev/null +++ b/src/ssvc/decision_points/system_exposure.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +""" +file: exposure +author: adh +created_at: 9/21/23 10:16 AM +""" +from copy import deepcopy +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +EXP_UNAVOIDABLE = SsvcDecisionPointValue( + name="Unavoidable", + key="U", + description="Internet or another widely accessible network where access cannot plausibly be restricted or " + "controlled (e.g., DNS servers, web servers, VOIP servers, email servers)", +) + +EXP_CONTROLLED = SsvcDecisionPointValue( + name="Controlled", + key="C", + description="Networked service with some access restrictions or mitigations already in place (whether locally or on the network). " + "A successful mitigation must reliably interrupt the adversary’s attack, which requires the attack is detectable " + "both reliably and quickly enough to respond. Controlled covers the situation in which a vulnerability can be " + "exploited through chaining it with other vulnerabilities. The assumption is that the number of steps in the " + "attack path is relatively low; if the path is long enough that it is implausible for an adversary to reliably " + "execute it, then exposure should be small.", +) + +EXP_SMALL = SsvcDecisionPointValue( + name="Small", + key="S", + description="Local service or program; highly controlled network", +) + + +SYSTEM_EXPOSURE_1 = SsvcDecisionPoint( + name="System Exposure", + description="The Accessible Attack Surface of the Affected System or Service", + key="EXP", + version="1.0.0", + values=( + EXP_SMALL, + EXP_CONTROLLED, + EXP_UNAVOIDABLE, + ), +) + +# EXP_OPEN is just a rename of EXP_UNAVOIDABLE +EXP_OPEN = deepcopy(EXP_UNAVOIDABLE) +EXP_OPEN.name = "Open" +EXP_OPEN.key = "O" + +SYSTEM_EXPOSURE_1_0_1 = SsvcDecisionPoint( + name="System Exposure", + description="The Accessible Attack Surface of the Affected System or Service", + key="EXP", + version="1.0.1", + values=( + EXP_SMALL, + EXP_CONTROLLED, + EXP_OPEN, + ), +) + + +def main(): + print(SYSTEM_EXPOSURE_1.to_json(indent=2)) + print(SYSTEM_EXPOSURE_1_0_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/technical_impact.py b/src/ssvc/decision_points/technical_impact.py new file mode 100644 index 00000000..da042f62 --- /dev/null +++ b/src/ssvc/decision_points/technical_impact.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +""" +file: technical_impact +author: adh +created_at: 9/21/23 9:49 AM +""" +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +TOTAL = SsvcDecisionPointValue( + name="Total", + key="T", + description="The exploit gives the adversary total control over the behavior of the software, or it gives total disclosure of all information on the system that contains the vulnerability.", +) + +PARTIAL = SsvcDecisionPointValue( + name="Partial", + key="P", + description="The exploit gives the adversary limited control over, or information exposure about, the behavior of the software that contains the vulnerability. Or the exploit gives the adversary an importantly low stochastic opportunity for total control.", +) + +TECHNICAL_IMPACT_1 = SsvcDecisionPoint( + name="Technical Impact", + description="The technical impact of the vulnerability.", + key="TI", + version="1.0.0", + values=( + PARTIAL, + TOTAL, + ), +) + + +def main(): + print(TECHNICAL_IMPACT_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/utility.py b/src/ssvc/decision_points/utility.py new file mode 100644 index 00000000..eeb3c591 --- /dev/null +++ b/src/ssvc/decision_points/utility.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +""" +file: utility +author: adh +created_at: 9/21/23 9:55 AM +""" +from copy import deepcopy + +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +SUPER_EFFECTIVE_2 = SsvcDecisionPointValue( + name="Super Effective", + key="S", + description="Yes to automatable and concentrated value", +) + +EFFICIENT_2 = SsvcDecisionPointValue( + name="Efficient", + key="E", + description="Yes to automatable and diffuse value OR No to automatable and concentrated value", +) + +LABORIOUS_2 = SsvcDecisionPointValue( + name="Laborious", key="L", description="No to automatable and diffuse value" +) + +SUPER_EFFECTIVE = SsvcDecisionPointValue( + name="Super Effective", + key="S", + description="Rapid virulence and concentrated value", +) + +EFFICIENT = SsvcDecisionPointValue( + name="Efficient", + key="E", + description="Rapid virulence and diffuse value OR Slow virulence and concentrated value", +) + +LABORIOUS = SsvcDecisionPointValue( + name="Laborious", key="L", description="Slow virulence and diffuse value" +) + +UTILITY_1 = SsvcDecisionPoint( + name="Utility", + description="The Usefulness of the Exploit to the Adversary", + key="U", + version="1.0.0", + values=( + LABORIOUS, + EFFICIENT, + SUPER_EFFECTIVE, + ), +) + +UTILITY_1_0_1 = SsvcDecisionPoint( + name="Utility", + description="The Usefulness of the Exploit to the Adversary", + key="U", + version="1.0.1", + values=( + LABORIOUS_2, + EFFICIENT_2, + SUPER_EFFECTIVE_2, + ), +) + + +def main(): + print(UTILITY_1.to_json(indent=2)) + print(UTILITY_1_0_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/value_density.py b/src/ssvc/decision_points/value_density.py new file mode 100644 index 00000000..eac48a13 --- /dev/null +++ b/src/ssvc/decision_points/value_density.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +""" +file: value_density +author: adh +created_at: 9/21/23 10:01 AM +""" +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +CONCENTRATED = SsvcDecisionPointValue( + name="Concentrated", + key="C", + description="The system that contains the vulnerable component is rich in resources. Heuristically, such systems are often the direct responsibility of “system operators” rather than users.", +) + +DIFFUSE = SsvcDecisionPointValue( + name="Diffuse", + key="D", + description="The system that contains the vulnerable component has limited resources. That is, the resources that the adversary will gain control over with a single exploitation event are relatively small.", +) + +VALUE_DENSITY_1 = SsvcDecisionPoint( + name="Value Density", + description="The concentration of value in the target", + key="VD", + version="1.0.0", + values=( + DIFFUSE, + CONCENTRATED, + ), +) + + +def main(): + print(VALUE_DENSITY_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/decision_points/virulence.py b/src/ssvc/decision_points/virulence.py new file mode 100644 index 00000000..289263b0 --- /dev/null +++ b/src/ssvc/decision_points/virulence.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +""" +file: virulence +author: adh +created_at: 9/21/23 9:58 AM +""" +from ssvc.decision_points.base import SsvcDecisionPoint, SsvcDecisionPointValue + +RAPID = SsvcDecisionPointValue( + name="Rapid", + key="R", + description="Steps 1-4 of the of the kill chain can be reliably automated. If the vulnerability allows remote code execution or command injection, the default response should be rapid.", +) + +SLOW = SsvcDecisionPointValue( + name="Slow", + key="S", + description="Steps 1-4 of the kill chain cannot be reliably automated for this vulnerability for some reason. These steps are reconnaissance, weaponization, delivery, and exploitation. Example reasons for why a step may not be reliably automatable include (1) the vulnerable component is not searchable or enumerable on the network, (2) weaponization may require human direction for each target, (3) delivery may require channels that widely deployed network security configurations block, and (3) exploitation may be frustrated by adequate exploit-prevention techniques enabled by default; ASLR is an example of an exploit-prevention tool.", +) + +VIRULENCE_1 = SsvcDecisionPoint( + name="Virulence", + description="The speed at which the vulnerability can be exploited.", + key="V", + version="1.0.0", + values=( + SLOW, + RAPID, + ), +) + + +def main(): + print(VIRULENCE_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/doc_helpers/__init__.py b/src/ssvc/doc_helpers/__init__.py new file mode 100644 index 00000000..dd6c1701 --- /dev/null +++ b/src/ssvc/doc_helpers/__init__.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +""" +file: __init__.py +author: adh +created_at: 10/4/23 2:12 PM +""" + + +def main(): + pass + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/doc_helpers/tools.py b/src/ssvc/doc_helpers/tools.py new file mode 100644 index 00000000..8ba73af6 --- /dev/null +++ b/src/ssvc/doc_helpers/tools.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +""" +file: tools +author: adh +created_at: 9/21/23 3:20 PM +""" +from ssvc.decision_points.base import SsvcDecisionPoint +from ssvc.dp_groups.base import SsvcDecisionPointGroup, get_all_decision_points_from + +_DISCLAIMER = "This file is auto-generated. Do not edit." + + +def _html_comment(s: str) -> str: + return f"\n" + + +def write_file(fname: str, blob: str) -> None: + with open(fname, "w") as f: + print(f"Writing {fname}") + f.write(blob) + f.write("\n") + + +def dump_dp(dp: SsvcDecisionPoint, path: str = None) -> None: + dp._comment = _DISCLAIMER + json_blob = dp.to_json(indent=2) + + if path is None: + # act like we're in a json list + lines = [f" {line}" for line in json_blob.split("\n")] + print("\n".join(lines) + ",") + else: + basename = dp.name.strip().lower().replace(" ", "_") + basename += f"_{dp.version.replace('.', '_')}" + + json_fname = f"{path}/{basename}.json" + write_file(json_fname, json_blob) + + table_fname = f"{path}/{basename}.md" + md_str = _html_comment(_DISCLAIMER) + md_str += dp.to_table() + write_file(table_fname, md_str) + + +def group_to_jsonfiles(group: SsvcDecisionPointGroup, path: str = None) -> None: + for dp in group.decision_points: + dump_dp(dp, path) + + +def main(): + from ssvc.dp_groups.v1 import SSVCv1 + from ssvc.dp_groups.v2 import SSVCv2 + from ssvc.dp_groups.v2_1 import SSVCv2_1 + from ssvc.dp_groups.cvss.v1 import CVSSv1 + from ssvc.dp_groups.cvss.v2 import CVSSv2 + from ssvc.dp_groups.cvss.v3 import CVSSv3 + + # extract all decision points from the groups + dps = get_all_decision_points_from( + [SSVCv1, SSVCv2, SSVCv2_1, CVSSv1, CVSSv2, CVSSv3] + ) + print("[") + for dp in dps: + dump_dp(dp, None) + print("]") + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/dp_groups/__init__.py b/src/ssvc/dp_groups/__init__.py new file mode 100644 index 00000000..a3a2a181 --- /dev/null +++ b/src/ssvc/dp_groups/__init__.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +""" +file: __init__.py +author: adh +created_at: 9/20/23 4:47 PM +""" + + +def main(): + pass + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/dp_groups/base.py b/src/ssvc/dp_groups/base.py new file mode 100644 index 00000000..706bec3c --- /dev/null +++ b/src/ssvc/dp_groups/base.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +""" +file: base +author: adh +created_at: 9/20/23 4:47 PM +""" +from dataclasses import dataclass +from typing import Tuple + +from dataclasses_json import dataclass_json + +from ssvc._mixins import _Base, _Versioned +from ssvc.decision_points.base import SsvcDecisionPoint + + +@dataclass_json +@dataclass(kw_only=True) +class SsvcDecisionPointGroup(_Base, _Versioned): + """ + Models a group of decision points. + """ + + decision_points: Tuple[SsvcDecisionPoint] + + +def get_all_decision_points_from( + glist: list[SsvcDecisionPointGroup], +) -> Tuple[SsvcDecisionPoint]: + """ + Given a list of SsvcDecisionPointGroup objects, return a list of all + the unique SsvcDecisionPoint objects contained in those groups. + + Args: + groups (list): A list of SsvcDecisionPointGroup objects. + + Returns: + list: A list of SsvcDecisionPoint objects. + """ + dps = [] + seen = set() + + for group in glist: + for dp in group.decision_points: + if dp in dps: + # skip duplicates + continue + key = (dp.name, dp.version) + if key in seen: + # skip duplicates + continue + # keep non-duplicates + dps.append(dp) + seen.add(key) + + return tuple(dps) + + +def main(): + pass + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/dp_groups/coordinator_publication.py b/src/ssvc/dp_groups/coordinator_publication.py new file mode 100644 index 00000000..63d3a34a --- /dev/null +++ b/src/ssvc/dp_groups/coordinator_publication.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +""" +file: coordinator_publication +author: adh +created_at: 9/21/23 11:40 AM +""" +from ssvc.decision_points.exploitation import EXPLOITATION_1 +from ssvc.decision_points.public_value_added import PUBLIC_VALUE_ADDED_1 +from ssvc.decision_points.supplier_involvement import SUPPLIER_INVOLVEMENT_1 +from ssvc.dp_groups.base import SsvcDecisionPointGroup + + +COORDINATOR_PUBLICATION_1 = SsvcDecisionPointGroup( + name="Coordinator Publication", + description="The decision points used by the coordinator during publication.", + version="1.0.0", + decision_points=( + SUPPLIER_INVOLVEMENT_1, + EXPLOITATION_1, + PUBLIC_VALUE_ADDED_1, + ), +) +""" +Added in SSVC v2, the Coordinator Publication v1.0.0 decision points are used by the coordinator during the publication process. + +It includes decision points: + +- Supplier Involvement v1.0.0 +- Exploitation v1.0.0 +- Public Value Added v1.0.0 +""" + + +def main(): + print(COORDINATOR_PUBLICATION_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/dp_groups/coordinator_triage.py b/src/ssvc/dp_groups/coordinator_triage.py new file mode 100644 index 00000000..6d6f352b --- /dev/null +++ b/src/ssvc/dp_groups/coordinator_triage.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +""" +file: coordinator_triage +author: adh +created_at: 9/21/23 11:40 AM +""" +from ssvc.decision_points.automatable import AUTOMATABLE_1 +from ssvc.decision_points.public_safety_impact import PUBLIC_SAFETY_IMPACT_1 +from ssvc.decision_points.report_credibility import REPORT_CREDIBILITY_1 +from ssvc.decision_points.report_public import REPORT_PUBLIC_1 +from ssvc.decision_points.safety_impact import SAFETY_IMPACT_1 +from ssvc.decision_points.supplier_cardinality import SUPPLIER_CARDINALITY_1 +from ssvc.decision_points.supplier_contacted import SUPPLIER_CONTACTED_1 +from ssvc.decision_points.supplier_engagement import SUPPLIER_ENGAGEMENT_1 +from ssvc.decision_points.utility import UTILITY_1_0_1 +from ssvc.decision_points.value_density import VALUE_DENSITY_1 +from ssvc.dp_groups.base import SsvcDecisionPointGroup + + +COORDINATOR_TRIAGE_1 = SsvcDecisionPointGroup( + name="Coordinator Triage", + description="The decision points used by the coordinator during triage.", + version="1.0.0", + decision_points=( + REPORT_PUBLIC_1, + SUPPLIER_CONTACTED_1, + REPORT_CREDIBILITY_1, + SUPPLIER_CARDINALITY_1, + SUPPLIER_ENGAGEMENT_1, + UTILITY_1_0_1, + AUTOMATABLE_1, + VALUE_DENSITY_1, + PUBLIC_SAFETY_IMPACT_1, + SAFETY_IMPACT_1, + ), +) +""" +Added in SSVC v2, the Coordinator Triage v1.0.0 decision points are used by the coordinator during the intake and triage process. + +It includes decision points: + +- Report Public v1.0.0 +- Supplier Contacted v1.0.0 +- Report Credibility v1.0.0 +- Supplier Cardinality v1.0.0 +- Supplier Engagement v1.0.0 +- Utility v1.0.1, which depends on + - Value Density v1.0.0 + - Automatable v1.0.0 +- Public Safety Impact v1.0.0. which depends on + - Safety Impact v1.0.0 +""" + + +def main(): + print(COORDINATOR_TRIAGE_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/dp_groups/cvss/__init__.py b/src/ssvc/dp_groups/cvss/__init__.py new file mode 100644 index 00000000..41ce5469 --- /dev/null +++ b/src/ssvc/dp_groups/cvss/__init__.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +""" +file: __init__.py +author: adh +created_at: 9/20/23 4:54 PM +""" + + +def main(): + pass + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/dp_groups/cvss/v1.py b/src/ssvc/dp_groups/cvss/v1.py new file mode 100644 index 00000000..cc4daab8 --- /dev/null +++ b/src/ssvc/dp_groups/cvss/v1.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +""" +file: v1 +author: adh +created_at: 9/20/23 12:39 PM +""" + +from ssvc.decision_points.cvss.access_complexity import ( + ACCESS_COMPLEXITY_1, +) +from ssvc.decision_points.cvss.access_vector import ACCESS_VECTOR_1 +from ssvc.decision_points.cvss.authentication import AUTHENTICATION_1 +from ssvc.decision_points.cvss.availability_impact import ( + AVAILABILITY_IMPACT_1, +) +from ssvc.decision_points.cvss.collateral_damage_potential import ( + COLLATERAL_DAMAGE_POTENTIAL_1, +) +from ssvc.decision_points.cvss.confidentiality_impact import ( + CONFIDENTIALITY_IMPACT_1, +) +from ssvc.decision_points.cvss.exploitability import ( + EXPLOITABILITY_1, +) +from ssvc.decision_points.cvss.impact_bias import IMPACT_BIAS_1 +from ssvc.decision_points.cvss.integrity_impact import ( + INTEGRITY_IMPACT_1, +) +from ssvc.decision_points.cvss.remediation_level import ( + REMEDIATION_LEVEL_1, +) +from ssvc.decision_points.cvss.report_confidence import ( + REPORT_CONFIDENCE_1, +) +from ssvc.decision_points.cvss.target_distribution import ( + TARGET_DISTRIBUTION_1, +) +from ssvc.dp_groups.base import SsvcDecisionPointGroup + +# Instantiate the CVSS v1 decision point group +CVSSv1 = SsvcDecisionPointGroup( + name="CVSS", + version="1.0", + description="CVSS v1 decision points", + decision_points=( + ACCESS_VECTOR_1, + ACCESS_COMPLEXITY_1, + AUTHENTICATION_1, + CONFIDENTIALITY_IMPACT_1, + INTEGRITY_IMPACT_1, + AVAILABILITY_IMPACT_1, + IMPACT_BIAS_1, + EXPLOITABILITY_1, + REMEDIATION_LEVEL_1, + REPORT_CONFIDENCE_1, + COLLATERAL_DAMAGE_POTENTIAL_1, + TARGET_DISTRIBUTION_1, + ), +) + + +def main(): + print(CVSSv1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/dp_groups/cvss/v2.py b/src/ssvc/dp_groups/cvss/v2.py new file mode 100644 index 00000000..4c9d7f5b --- /dev/null +++ b/src/ssvc/dp_groups/cvss/v2.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +""" +file: v2 +author: adh +created_at: 9/20/23 12:54 PM +""" + +from ssvc.decision_points.cvss.access_complexity import ( + ACCESS_COMPLEXITY_2, +) +from ssvc.decision_points.cvss.access_vector import ACCESS_VECTOR_2 +from ssvc.decision_points.cvss.authentication import AUTHENTICATION_2 +from ssvc.decision_points.cvss.availability_impact import ( + AVAILABILITY_IMPACT_1, +) +from ssvc.decision_points.cvss.availability_requirement import ( + AVAILABILITY_REQUIREMENT_1, +) +from ssvc.decision_points.cvss.collateral_damage_potential import ( + COLLATERAL_DAMAGE_POTENTIAL_2, +) +from ssvc.decision_points.cvss.confidentiality_impact import ( + CONFIDENTIALITY_IMPACT_1, +) +from ssvc.decision_points.cvss.confidentiality_requirement import ( + CONFIDENTIALITY_REQUIREMENT_1, +) +from ssvc.decision_points.cvss.exploitability import ( + EXPLOITABILITY_1_1, +) +from ssvc.decision_points.cvss.integrity_impact import ( + INTEGRITY_IMPACT_1, +) +from ssvc.decision_points.cvss.integrity_requirement import ( + INTEGRITY_REQUIREMENT_1, +) +from ssvc.decision_points.cvss.remediation_level import ( + REMEDIATION_LEVEL_1_1, +) +from ssvc.decision_points.cvss.report_confidence import ( + REPORT_CONFIDENCE_1_1, +) +from ssvc.decision_points.cvss.target_distribution import ( + TARGET_DISTRIBUTION_1_1, +) +from ssvc.dp_groups.base import SsvcDecisionPointGroup + +CVSSv2 = SsvcDecisionPointGroup( + name="CVSS Version 2", + description="The Common Vulnerability Scoring System (CVSS) is a free and open industry standard for assessing the severity of computer system security vulnerabilities. CVSS attempts to assign severity scores to vulnerabilities, allowing responders to prioritize responses and resources according to threat. Scores are calculated based on a formula that depends on several metrics that approximate ease of exploit and the impact of exploit. Scores range from 0 to 10, with 10 being the most severe.", + version="2.0", + decision_points=( + ACCESS_VECTOR_2, + ACCESS_COMPLEXITY_2, + AUTHENTICATION_2, + CONFIDENTIALITY_IMPACT_1, + INTEGRITY_IMPACT_1, + AVAILABILITY_IMPACT_1, + EXPLOITABILITY_1_1, + REMEDIATION_LEVEL_1_1, + REPORT_CONFIDENCE_1_1, + COLLATERAL_DAMAGE_POTENTIAL_2, + TARGET_DISTRIBUTION_1_1, + CONFIDENTIALITY_REQUIREMENT_1, + INTEGRITY_REQUIREMENT_1, + AVAILABILITY_REQUIREMENT_1, + ), +) + + +def main(): + print(CVSSv2.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/dp_groups/cvss/v3.py b/src/ssvc/dp_groups/cvss/v3.py new file mode 100644 index 00000000..19393aa8 --- /dev/null +++ b/src/ssvc/dp_groups/cvss/v3.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +""" +file: v3 +author: adh +created_at: 9/20/23 2:30 PM +""" +from copy import deepcopy + +from ssvc.decision_points.base import SsvcDecisionPointValue +from ssvc.decision_points.cvss.attack_complexity import ( + ATTACK_COMPLEXITY_1, +) +from ssvc.decision_points.cvss.attack_vector import ATTACK_VECTOR_1 +from ssvc.decision_points.cvss.availability_impact import ( + AVAILABILITY_IMPACT_2, +) +from ssvc.decision_points.cvss.availability_requirement import ( + AVAILABILITY_REQUIREMENT_1, +) +from ssvc.decision_points.cvss.confidentiality_impact import ( + CONFIDENTIALITY_IMPACT_2, +) +from ssvc.decision_points.cvss.confidentiality_requirement import ( + CONFIDENTIALITY_REQUIREMENT_1, +) +from ssvc.decision_points.cvss.exploitability import ( + EXPLOIT_CODE_MATURITY_1_1_1, +) +from ssvc.decision_points.cvss.integrity_impact import ( + INTEGRITY_IMPACT_2, +) +from ssvc.decision_points.cvss.integrity_requirement import ( + INTEGRITY_REQUIREMENT_1, +) +from ssvc.decision_points.cvss.privileges_required import ( + PRIVILEGES_REQUIRED_1, +) +from ssvc.decision_points.cvss.remediation_level import ( + REMEDIATION_LEVEL_1_1, +) +from ssvc.decision_points.cvss.report_confidence import ( + REPORT_CONFIDENCE_2, +) +from ssvc.decision_points.cvss.scope import SCOPE_1 as SCOPE +from ssvc.decision_points.cvss.user_interaction import ( + USER_INTERACTION_1, +) +from ssvc.dp_groups.base import SsvcDecisionPointGroup + + +def _modify(obj): + """ + Prepends "Modified " to the name and "M" to the key of the given object. Also adds a value of "Not Defined" to the + values list. + :param obj: the object to modify + :return: the object + """ + o = deepcopy(obj) + o.name = "Modified " + o.name + o.key = "M" + o.key + nd = SsvcDecisionPointValue( + name="Not Defined", key="ND", description="Ignore this value" + ) + values = list(o.values) + # if there is no value named "Not Defined" value, add it + names = [v.name for v in values] + if nd.name not in names: + values.append(nd) + o.values = tuple(values) + return o + + +MODIFIED_ATTACK_VECTOR = _modify(ATTACK_VECTOR_1) +MODIFIED_ATTACK_COMPLEXITY = _modify(ATTACK_COMPLEXITY_1) +MODIFIED_PRIVILEGES_REQUIRED = _modify(PRIVILEGES_REQUIRED_1) +MODIFIED_USER_INTERACTION = _modify(USER_INTERACTION_1) +MODIFIED_SCOPE = _modify(SCOPE) +MODIFIED_CONFIDENTIALITY_IMPACT = _modify(CONFIDENTIALITY_IMPACT_2) +MODIFIED_INTEGRITY_IMPACT = _modify(INTEGRITY_IMPACT_2) +MODIFIED_AVAILABILITY_IMPACT = _modify(AVAILABILITY_IMPACT_2) + + +CVSSv3 = SsvcDecisionPointGroup( + name="CVSS Version 3", + description="The Common Vulnerability Scoring System (CVSS) is a free and open industry standard for assessing the severity of computer system security vulnerabilities. CVSS attempts to assign severity scores to vulnerabilities, allowing responders to prioritize responses and resources according to threat. Scores are calculated based on a formula that depends on several metrics that approximate ease of exploit and the impact of exploit. Scores range from 0 to 10, with 10 being the most severe.", + version="3.0", + decision_points=( + ATTACK_VECTOR_1, + ATTACK_COMPLEXITY_1, + PRIVILEGES_REQUIRED_1, + USER_INTERACTION_1, + SCOPE, + CONFIDENTIALITY_IMPACT_2, + INTEGRITY_IMPACT_2, + AVAILABILITY_IMPACT_2, + EXPLOIT_CODE_MATURITY_1_1_1, + REMEDIATION_LEVEL_1_1, + REPORT_CONFIDENCE_2, + CONFIDENTIALITY_REQUIREMENT_1, + INTEGRITY_REQUIREMENT_1, + AVAILABILITY_REQUIREMENT_1, + MODIFIED_ATTACK_VECTOR, + MODIFIED_ATTACK_COMPLEXITY, + MODIFIED_PRIVILEGES_REQUIRED, + MODIFIED_USER_INTERACTION, + MODIFIED_SCOPE, + MODIFIED_CONFIDENTIALITY_IMPACT, + MODIFIED_INTEGRITY_IMPACT, + MODIFIED_AVAILABILITY_IMPACT, + ), +) + + +def main(): + print(CVSSv3.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/dp_groups/deployer.py b/src/ssvc/dp_groups/deployer.py new file mode 100644 index 00000000..e375510e --- /dev/null +++ b/src/ssvc/dp_groups/deployer.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +""" +file: deployer +author: adh +created_at: 9/21/23 11:40 AM +""" + +from ssvc.decision_points.automatable import AUTOMATABLE_1 +from ssvc.decision_points.exploitation import EXPLOITATION_1 +from ssvc.decision_points.human_impact import HUMAN_IMPACT_1 +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, + SYSTEM_EXPOSURE_1_0_1, +) +from ssvc.decision_points.utility import UTILITY_1_0_1 +from ssvc.decision_points.value_density import VALUE_DENSITY_1 +from ssvc.dp_groups.base import SsvcDecisionPointGroup + +PATCH_APPLIER_1 = SsvcDecisionPointGroup( + name="SSVC Patch Applier", + description="The decision points used by the patch applier.", + version="1.0.0", + decision_points=[ + EXPLOITATION_1, + SYSTEM_EXPOSURE_1, + MISSION_IMPACT_1, + SAFETY_IMPACT_1, + ], +) +""" +In SSVC v1, Patch Applier v1 represents the decision points used by the patch applier. +It includes decision points: + +- Exploitation v1.0.0 +- System Exposure v1.0.0 +- Mission Impact v1.0.0 +- Safety Impact v1.0.0. +""" + + +# alias for forward compatibility +DEPLOYER_1 = PATCH_APPLIER_1 + +# SSVC v2 +DEPLOYER_2 = SsvcDecisionPointGroup( + name="SSVC Deployer", + description="The decision points used by the deployer.", + version="2.0.0", + decision_points=[ + EXPLOITATION_1, + SYSTEM_EXPOSURE_1_0_1, + MISSION_IMPACT_1, + SAFETY_IMPACT_1, + UTILITY_1_0_1, + AUTOMATABLE_1, + VALUE_DENSITY_1, + HUMAN_IMPACT_1, + ], +) +""" +Deployer v2.0.0 is renamed from Patch Applier v1.0.0. +It includes decision points: + +- Exploitation v1.0.0 +- System Exposure v1.0.1 +- Human Impact v1.0.0 (consolidate Mission Impact v1.0.0 and Safety Impact v1.0.0) + - Safety Impact v1.0.0 + - Mission Impact v1.0.0 +- Utility v1.0.1 (consolidate Automatable v1.0.0 and Value Density v1.0.0) + - Automatable v1.0.0 + - Value Density v1.0.0 + +Changes from Patch Applier v1.0.0: +- System Exposure v1.0.0 -> v1.0.1 +- Utility v1.0.1 is added, which depends on Automatable v1.0.0 and Value Density v1.0.0 +- Human Impact v1.0.0 is added, which depends on Mission Impact v1.0.0 and Safety Impact v1.0.0 +""" + +DEPLOYER_3 = SsvcDecisionPointGroup( + name="SSVC Deployer", + description="The decision points used by the deployer.", + version="3.0.0", + decision_points=( + EXPLOITATION_1, + SYSTEM_EXPOSURE_1_0_1, + MISSION_IMPACT_2, + SAFETY_IMPACT_1, + AUTOMATABLE_1, + HUMAN_IMPACT_1, + ), +) +""" +In SSVC 2.1, Deployer 3.0.0 includes decision points: + +- Exploitation 1.0.0 +- System Exposure 1.0.1 +- Automatable 1.0.0 +- Human Impact 1.0.0 + - Safety Impact 1.0.0 + - Mission Impact 2.0.0 + +Changes from v2.0.0: +- removes Utility v1.0.1 in favor of Automatable v1.0.0. +- Mission Impact v1.0.0 -> v2.0.0 +""" + + +def main(): + print(PATCH_APPLIER_1.to_json(indent=2)) + print(DEPLOYER_2.to_json(indent=2)) + print(DEPLOYER_3.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/dp_groups/supplier.py b/src/ssvc/dp_groups/supplier.py new file mode 100644 index 00000000..126ec913 --- /dev/null +++ b/src/ssvc/dp_groups/supplier.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +""" +file: supplier +author: adh +created_at: 9/21/23 11:41 AM +""" + +from ssvc.decision_points.automatable import AUTOMATABLE_1 +from ssvc.decision_points.exploitation import EXPLOITATION_1 +from ssvc.decision_points.safety_impact import SAFETY_IMPACT_1 +from ssvc.decision_points.technical_impact import TECHNICAL_IMPACT_1 +from ssvc.decision_points.utility import UTILITY_1, UTILITY_1_0_1 +from ssvc.decision_points.value_density import VALUE_DENSITY_1 +from ssvc.decision_points.virulence import VIRULENCE_1 +from ssvc.dp_groups.base import SsvcDecisionPointGroup + +PATCH_DEVELOPER_1 = SsvcDecisionPointGroup( + name="SSVC Patch Developer", + description="The decision points used by the patch developer.", + version="1.0.0", + decision_points=( + EXPLOITATION_1, + UTILITY_1, + TECHNICAL_IMPACT_1, + VIRULENCE_1, + VALUE_DENSITY_1, + SAFETY_IMPACT_1, + ), +) +""" +In SSVC v1, Patch Developer v1 represents the decision points used by the patch developer. + +It includes decision points: + +- Exploitation v1.0.0 +- Utility v1.0.0 + - Virulence v1.0.0 + - Value Density v1.0.0 +- Technical Impact v1.0.0 +- Safety Impact v1.0.0 +""" + +# alias for forward compatibility +SUPPLIER_1 = PATCH_DEVELOPER_1 + +# SSVC v2 renamed to SSVC Supplier +SUPPLIER_2 = SsvcDecisionPointGroup( + name="SSVC Supplier", + description="The decision points used by the supplier.", + version="2.0.0", + decision_points=[ + EXPLOITATION_1, + UTILITY_1_0_1, + TECHNICAL_IMPACT_1, + AUTOMATABLE_1, + VALUE_DENSITY_1, + SAFETY_IMPACT_1, + ], +) +""" +In SSVC v2, Supplier v2 represents the decision points used by the supplier. +It includes decision points: + +- Exploitation v1.0.0 +- Utility v1.0.1 + - Automatable v1.0.0 + - Value Density v1.0.0 +- Technical Impact v1.0.0 +- Public Safety Impact v1.0.0 + - Safety Impact v1.0.0 + +Changes from Patch Developer v1: + +- Name change from Patch Developer v1 -> Supplier v2 +- Utility v1.0.0 -> v1.0.1 +- Virulence v1.0.0 replaced by Automatable v1.0.0 +- Public Safety Impact v1.0.0 added, which subsumes Safety Impact v1.0.0 +""" + + +def main(): + print(PATCH_DEVELOPER_1.to_json(indent=2)) + print(SUPPLIER_2.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/dp_groups/v1.py b/src/ssvc/dp_groups/v1.py new file mode 100644 index 00000000..049b34db --- /dev/null +++ b/src/ssvc/dp_groups/v1.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +""" +file: v1 +author: adh +created_at: 9/21/23 9:52 AM +""" +from ssvc.dp_groups.base import SsvcDecisionPointGroup, get_all_decision_points_from + +# convenience imports +from ssvc.dp_groups.deployer import PATCH_APPLIER_1 # noqa +from ssvc.dp_groups.supplier import PATCH_DEVELOPER_1 # noqa + +GROUPS = [PATCH_APPLIER_1, PATCH_DEVELOPER_1] + +SSVCv1 = SsvcDecisionPointGroup( + name="SSVCv1", + description="The first version of the SSVC.", + version="1.0.0", + decision_points=get_all_decision_points_from(GROUPS), +) + + +def main(): + print(SSVCv1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/dp_groups/v2.py b/src/ssvc/dp_groups/v2.py new file mode 100644 index 00000000..636217e0 --- /dev/null +++ b/src/ssvc/dp_groups/v2.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +""" +file: v2 +author: adh +created_at: 9/21/23 10:31 AM +""" + +from ssvc.dp_groups.base import SsvcDecisionPointGroup, get_all_decision_points_from + +# convenience imports +from ssvc.dp_groups.coordinator_publication import COORDINATOR_PUBLICATION_1 # noqa +from ssvc.dp_groups.coordinator_triage import COORDINATOR_TRIAGE_1 # noqa +from ssvc.dp_groups.deployer import DEPLOYER_2 # noqa +from ssvc.dp_groups.supplier import SUPPLIER_2 # noqa + +GROUPS = [COORDINATOR_PUBLICATION_1, COORDINATOR_TRIAGE_1, DEPLOYER_2, SUPPLIER_2] + + +SSVCv2 = SsvcDecisionPointGroup( + name="SSVCv2", + description="The second version of the SSVC.", + version="2.0.0", + decision_points=get_all_decision_points_from(GROUPS), +) + + +def main(): + print(SSVCv2.to_json(indent=2)) + print(SUPPLIER_2.to_json(indent=2)) + print(DEPLOYER_2.to_json(indent=2)) + print(COORDINATOR_TRIAGE_1.to_json(indent=2)) + print(COORDINATOR_PUBLICATION_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/dp_groups/v2_1.py b/src/ssvc/dp_groups/v2_1.py new file mode 100644 index 00000000..f2409de5 --- /dev/null +++ b/src/ssvc/dp_groups/v2_1.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +""" +file: v2_1 +author: adh +created_at: 9/21/23 11:45 AM +""" + +from ssvc.dp_groups.base import SsvcDecisionPointGroup, get_all_decision_points_from + +# convenience imports +from ssvc.dp_groups.coordinator_publication import COORDINATOR_PUBLICATION_1 +from ssvc.dp_groups.coordinator_triage import COORDINATOR_TRIAGE_1 +from ssvc.dp_groups.deployer import DEPLOYER_3 +from ssvc.dp_groups.supplier import SUPPLIER_2 + +GROUPS = [COORDINATOR_PUBLICATION_1, COORDINATOR_TRIAGE_1, DEPLOYER_3, SUPPLIER_2] + + +SSVCv2_1 = SsvcDecisionPointGroup( + name="SSVCv2.1", + description="The second version of the SSVC.", + version="2.1.0", + decision_points=get_all_decision_points_from(GROUPS), +) + + +def main(): + for group in GROUPS: + print(group.to_json(indent=2)) + print() + print(SSVCv2_1.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/test/__init__.py b/src/test/__init__.py index e69de29b..5c4aa405 100644 --- a/src/test/__init__.py +++ b/src/test/__init__.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +""" +file: __init__.py +author: adh +created_at: 9/27/23 3:56 PM +""" + + +def main(): + pass + + +if __name__ == "__main__": + main() diff --git a/src/test/test_analyze_csv.py b/src/test/test_analyze_csv.py index 115c460d..742ee25f 100644 --- a/src/test/test_analyze_csv.py +++ b/src/test/test_analyze_csv.py @@ -1,10 +1,8 @@ -import contextlib -import io import unittest import pandas as pd -import analyze_csv as acsv +from ssvc import csv_analyzer as acsv class MyTestCase(unittest.TestCase): diff --git a/src/test/test_dp_base.py b/src/test/test_dp_base.py new file mode 100644 index 00000000..cb1ecaac --- /dev/null +++ b/src/test/test_dp_base.py @@ -0,0 +1,63 @@ +import unittest +import ssvc.decision_points.base as base + + +class MyTestCase(unittest.TestCase): + def setUp(self) -> None: + self.value = base.SsvcDecisionPointValue( + name="foo", key="bar", description="baz" + ) + + self.dp = base.SsvcDecisionPoint( + name="foo", + key="bar", + description="baz", + version="1.0.0", + namespace="ns", + values=(self.value,), + ) + + def test_ssvc_value(self): + obj = self.value + # should have name, key, description + self.assertEqual(obj.name, "foo") + self.assertEqual(obj.key, "bar") + self.assertEqual(obj.description, "baz") + + # should not have namespace, version + self.assertFalse(hasattr(obj, "namespace")) + self.assertFalse(hasattr(obj, "version")) + + def test_ssvc_decision_point(self): + obj = self.dp + # should have name, key, description, values, version, namespace + self.assertEqual(obj.name, "foo") + self.assertEqual(obj.key, "bar") + self.assertEqual(obj.description, "baz") + self.assertEqual(obj.version, "1.0.0") + self.assertEqual(obj.namespace, "ns") + self.assertEqual(len(obj.values), 1) + + def test_ssvc_value_json_roundtrip(self): + obj = self.value + + json = obj.to_json() + self.assertIsInstance(json, str) + self.assertGreater(len(json), 0) + + obj2 = base.SsvcDecisionPointValue.from_json(json) + self.assertEqual(obj, obj2) + + def test_ssvc_decision_point_json_roundtrip(self): + obj = self.dp + + json = obj.to_json() + self.assertIsInstance(json, str) + self.assertGreater(len(json), 0) + + obj2 = base.SsvcDecisionPoint.from_json(json) + self.assertEqual(obj, obj2) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/test/test_mixins.py b/src/test/test_mixins.py new file mode 100644 index 00000000..53e7c517 --- /dev/null +++ b/src/test/test_mixins.py @@ -0,0 +1,155 @@ +import unittest +from dataclasses import dataclass + +from dataclasses_json import dataclass_json + +from ssvc._mixins import _Base, _Keyed, _Versioned, _Namespaced + + +class TestMixins(unittest.TestCase): + def setUp(self) -> None: + self.obj = _Base(name="foo", description="baz") + + def test_ssvc_base_create(self): + obj = _Base(name="foo", description="baz") + self.assertEqual(obj.name, "foo") + self.assertEqual(obj.description, "baz") + + # empty + self.assertRaises(TypeError, _Base) + # no name + self.assertRaises(TypeError, _Base, description="baz") + # no description + self.assertRaises(TypeError, _Base, name="foo") + + def test_json_roundtrip(self): + obj = self.obj + json = obj.to_json() + # is it a string? + self.assertIsInstance(json, str) + # does it look right? + self.assertEqual(json, '{"name": "foo", "description": "baz"}') + + # modify the raw json string + json = json.replace("foo", "quux") + self.assertEqual(json, '{"name": "quux", "description": "baz"}') + + # does it load? + obj2 = _Base.from_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) + + self.assertIsInstance(d, dict) + self.assertEqual(d["name"], "foo") + self.assertEqual(d["description"], "baz") + + # modify the dict + d["name"] = "quux" + + # does it load? + obj2 = _Base(**d) + self.assertEqual(obj2.name, "quux") + self.assertEqual(obj2.description, "baz") + + def test_namespaced_create(self): + obj = _Namespaced() + self.assertEqual(obj.namespace, "ssvc") + + obj = _Namespaced(namespace="quux") + self.assertEqual(obj.namespace, "quux") + + def test_versioned_create(self): + obj = _Versioned() + self.assertEqual(obj.version, "0.0.0") + + obj = _Versioned(version="1.2.3") + self.assertEqual(obj.version, "1.2.3") + + def test_keyed_create(self): + obj = _Keyed(key="foo") + self.assertEqual(obj.key, "foo") + + self.assertRaises(TypeError, _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}, + ] + 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 + ] + + import itertools + + max_len = len(mixins) + for i in range(max_len): + for combo in itertools.combinations(mixins, i + 1): + classes = [x["class"] for x in combo] + 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): + pass + + # make sure it breaks if we leave out a required arg + for k in args.keys(): + args_copy = args.copy() + del args_copy[k] + + if k in keys_with_defaults: + # expect success + obj = Foo(name="foo", description="baz", **args_copy) + # make sure the key is defaulted + self.assertEqual(getattr(Foo, k), getattr(obj, k)) + else: + self.assertRaises( + TypeError, Foo, name="foo", description="baz", **args_copy + ) + + # instantiate the object + obj = Foo(name="foo", description="baz", **args) + self.assertEqual(obj.name, "foo") + self.assertEqual(obj.description, "baz") + # make sure the args are set + for k, v in args.items(): + self.assertEqual(getattr(obj, k), v) + + # test json roundtrip + json = obj.to_json() + # is it a string? + self.assertIsInstance(json, str) + # does it look right? + self.assertIn('"name": "foo"', json) + self.assertIn('"description": "baz"', json) + for k, v in args.items(): + 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) + self.assertEqual(obj2.name, "quux") + self.assertEqual(obj2.description, "fizz") + # make sure the args are set + for k, v in args.items(): + self.assertEqual(getattr(obj2, k), v) + # make sure unchanged attributes match from the original object + for k in args.keys(): + self.assertEqual(getattr(obj2, k), getattr(obj, k)) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/test/test_schema.py b/src/test/test_schema.py new file mode 100644 index 00000000..3869b871 --- /dev/null +++ b/src/test/test_schema.py @@ -0,0 +1,45 @@ +import json +import unittest + +import jsonschema + +import ssvc.decision_points # noqa F401 +from ssvc.decision_points.base import REGISTERED_DECISION_POINTS + + +class MyTestCase(unittest.TestCase): + def test_validation(self): + schema = json.load(open("../../data/schema/Decision_Point.schema.json")) + import logging + + logger = logging.getLogger() + logger.setLevel(logging.DEBUG) + hdlr = logging.StreamHandler() + logger.addHandler(hdlr) + + # importing these causes the decision points to register themselves + from ssvc.dp_groups.v1 import SSVCv1 # noqa + from ssvc.dp_groups.v2 import SSVCv2 # noqa + from ssvc.dp_groups.v2_1 import SSVCv2_1 # noqa + from ssvc.dp_groups.cvss.v1 import CVSSv1 # noqa + from ssvc.dp_groups.cvss.v2 import CVSSv2 # noqa + from ssvc.dp_groups.cvss.v3 import CVSSv3 # noqa + + for dp in REGISTERED_DECISION_POINTS: + exp = None + as_json = dp.to_json() + loaded = json.loads(as_json) + + try: + jsonschema.validate(loaded, schema) + except jsonschema.exceptions.ValidationError as e: + exp = e + + self.assertIsNone(exp, f"Validation failed for {dp.name} {dp.version}") + logger.debug( + f"Validation passed for ({dp.namespace}) {dp.name} v{dp.version}" + ) + + +if __name__ == "__main__": + unittest.main()