diff --git a/.gitignore b/.gitignore index 96a61d5e..6189a8e6 100644 --- a/.gitignore +++ b/.gitignore @@ -128,3 +128,4 @@ dmypy.json # Pyre type checker .pyre/ ssvc2-applier-wip.xlsx +_version.py diff --git a/data/json/decision_points/automatable_2_0_0.json b/data/json/decision_points/automatable_2_0_0.json new file mode 100644 index 00000000..9a0369b2 --- /dev/null +++ b/data/json/decision_points/automatable_2_0_0.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "2.0.0", + "key": "A", + "name": "Automatable", + "description": "Can an attacker reliably automate creating exploitation events for this vulnerability?", + "values": [ + { + "key": "N", + "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": "Y", + "name": "Yes", + "description": "Attackers can reliably automate steps 1-4 of the kill chain." + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/exploitation_1_0_0.json b/data/json/decision_points/exploitation_1_0_0.json new file mode 100644 index 00000000..9f287310 --- /dev/null +++ b/data/json/decision_points/exploitation_1_0_0.json @@ -0,0 +1,24 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "E", + "name": "Exploitation", + "description": "The present state of exploitation of the vulnerability.", + "values": [ + { + "key": "N", + "name": "None", + "description": "There is no evidence of active exploitation and no public proof of concept (PoC) of how to exploit the vulnerability." + }, + { + "key": "P", + "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": "A", + "name": "Active", + "description": "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/data/json/decision_points/human_impact_1_0_0.json b/data/json/decision_points/human_impact_1_0_0.json new file mode 100644 index 00000000..a251f16a --- /dev/null +++ b/data/json/decision_points/human_impact_1_0_0.json @@ -0,0 +1,29 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "HI", + "name": "Human Impact", + "description": "Human Impact is a combination of Safety and Mission impacts.", + "values": [ + { + "key": "L", + "name": "Low", + "description": "Safety=None/Minor, Mission=None/Degraded/Crippled" + }, + { + "key": "M", + "name": "Medium", + "description": "Safety=None/Minor, Mission=MEF Failure OR Safety=Major, Mission=None/Degraded/Crippled" + }, + { + "key": "H", + "name": "High", + "description": "Safety=Hazardous, Mission=None/Degraded/Crippled/MEF Failure OR Safety=Major, Mission=MEF Failure" + }, + { + "key": "VH", + "name": "Very High", + "description": "Safety=Catastrophic OR Mission=Mission Failure" + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/mission_impact_1_0_0.json b/data/json/decision_points/mission_impact_1_0_0.json new file mode 100644 index 00000000..456db1bd --- /dev/null +++ b/data/json/decision_points/mission_impact_1_0_0.json @@ -0,0 +1,34 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "MI", + "name": "Mission Impact", + "description": "Impact on Mission Essential Functions of the Organization", + "values": [ + { + "key": "N", + "name": "None", + "description": "Little to no impact" + }, + { + "key": "NED", + "name": "Non-Essential Degraded", + "description": "Degradation of non-essential functions; chronic degradation would eventually harm essential functions" + }, + { + "key": "MSC", + "name": "MEF Support Crippled", + "description": "Activities that directly support essential functions are crippled; essential functions continue for a time" + }, + { + "key": "MEF", + "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": "MF", + "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" + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/mission_impact_2_0_0.json b/data/json/decision_points/mission_impact_2_0_0.json new file mode 100644 index 00000000..9d096ce0 --- /dev/null +++ b/data/json/decision_points/mission_impact_2_0_0.json @@ -0,0 +1,29 @@ +{ + "namespace": "ssvc", + "version": "2.0.0", + "key": "MI", + "name": "Mission Impact", + "description": "Impact on Mission Essential Functions of the Organization", + "values": [ + { + "key": "D", + "name": "Degraded", + "description": "Little to no impact up to degradation of non-essential functions; chronic degradation would eventually harm essential functions" + }, + { + "key": "MSC", + "name": "MEF Support Crippled", + "description": "Activities that directly support essential functions are crippled; essential functions continue for a time" + }, + { + "key": "MEF", + "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": "MF", + "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" + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/public_safety_impact_1_0_0.json b/data/json/decision_points/public_safety_impact_1_0_0.json new file mode 100644 index 00000000..84f727fa --- /dev/null +++ b/data/json/decision_points/public_safety_impact_1_0_0.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "PSI", + "name": "Public Safety Impact", + "description": "A coarse-grained representation of impact to public safety.", + "values": [ + { + "key": "M", + "name": "Minimal", + "description": "Safety impact of None or Minor." + }, + { + "key": "S", + "name": "Significant", + "description": "Safety impact of Major, Hazardous, or Catastrophic." + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/public_value_added_1_0_0.json b/data/json/decision_points/public_value_added_1_0_0.json new file mode 100644 index 00000000..bfa46554 --- /dev/null +++ b/data/json/decision_points/public_value_added_1_0_0.json @@ -0,0 +1,24 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "PVA", + "name": "Public Value Added", + "description": "How much value would a publication from the coordinator benefit the broader community?", + "values": [ + { + "key": "P", + "name": "Precedence", + "description": "The publication would be the first publicly available, or be coincident with the first publicly available." + }, + { + "key": "A", + "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": "L", + "name": "Limited", + "description": "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/data/json/decision_points/report_credibility_1_0_0.json b/data/json/decision_points/report_credibility_1_0_0.json new file mode 100644 index 00000000..68ce1143 --- /dev/null +++ b/data/json/decision_points/report_credibility_1_0_0.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "RC", + "name": "Report Credibility", + "description": "Is the report credible?", + "values": [ + { + "key": "C", + "name": "Credible", + "description": "The report is credible." + }, + { + "key": "NC", + "name": "Not Credible", + "description": "The report is not credible." + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/report_public_1_0_0.json b/data/json/decision_points/report_public_1_0_0.json new file mode 100644 index 00000000..522473dd --- /dev/null +++ b/data/json/decision_points/report_public_1_0_0.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "RP", + "name": "Report Public", + "description": "Is a viable report of the details of the vulnerability already publicly available?", + "values": [ + { + "key": "N", + "name": "No", + "description": "No public report of the vulnerability exists." + }, + { + "key": "Y", + "name": "Yes", + "description": "A public report of the vulnerability exists." + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/safety_impact_1_0_0.json b/data/json/decision_points/safety_impact_1_0_0.json new file mode 100644 index 00000000..3ca63485 --- /dev/null +++ b/data/json/decision_points/safety_impact_1_0_0.json @@ -0,0 +1,34 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "SI", + "name": "Safety Impact", + "description": "The safety impact of the vulnerability.", + "values": [ + { + "key": "N", + "name": "None", + "description": "The effect is below the threshold for all aspects described in Minor." + }, + { + "key": "M", + "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": "J", + "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": "H", + "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": "C", + "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." + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/supplier_cardinality_1_0_0.json b/data/json/decision_points/supplier_cardinality_1_0_0.json new file mode 100644 index 00000000..36088dcc --- /dev/null +++ b/data/json/decision_points/supplier_cardinality_1_0_0.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "SC", + "name": "Supplier Cardinality", + "description": "How many suppliers are responsible for the vulnerable component and its remediation or mitigation plan?", + "values": [ + { + "key": "O", + "name": "One", + "description": "There is only one supplier of the vulnerable component." + }, + { + "key": "M", + "name": "Multiple", + "description": "There are multiple suppliers of the vulnerable component." + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/supplier_contacted_1_0_0.json b/data/json/decision_points/supplier_contacted_1_0_0.json new file mode 100644 index 00000000..526ef3e0 --- /dev/null +++ b/data/json/decision_points/supplier_contacted_1_0_0.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "SC", + "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?", + "values": [ + { + "key": "N", + "name": "No", + "description": "The supplier has not been contacted." + }, + { + "key": "Y", + "name": "Yes", + "description": "The supplier has been contacted." + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/supplier_engagement_1_0_0.json b/data/json/decision_points/supplier_engagement_1_0_0.json new file mode 100644 index 00000000..cce9d92a --- /dev/null +++ b/data/json/decision_points/supplier_engagement_1_0_0.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "SE", + "name": "Supplier Engagement", + "description": "Is the supplier responding to the reporter\u2019s contact effort and actively participating in the coordination effort?", + "values": [ + { + "key": "A", + "name": "Active", + "description": "The supplier is responding to the reporter\u2019s contact effort and actively participating in the coordination effort." + }, + { + "key": "U", + "name": "Unresponsive", + "description": "The supplier is not responding to the reporter\u2019s contact effort and not actively participating in the coordination effort." + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/supplier_involvement_1_0_0.json b/data/json/decision_points/supplier_involvement_1_0_0.json new file mode 100644 index 00000000..0adcf48d --- /dev/null +++ b/data/json/decision_points/supplier_involvement_1_0_0.json @@ -0,0 +1,24 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "SI", + "name": "Supplier Involvement", + "description": "What is the state of the supplier\u2019s work on addressing the vulnerability?", + "values": [ + { + "key": "FR", + "name": "Fix Ready", + "description": "The supplier has provided a patch or fix." + }, + { + "key": "C", + "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": "UU", + "name": "Uncooperative/Unresponsive", + "description": "The supplier has not responded, declined to generate a remediation, or no longer exists." + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/system_exposure_1_0_0.json b/data/json/decision_points/system_exposure_1_0_0.json new file mode 100644 index 00000000..60b5dc75 --- /dev/null +++ b/data/json/decision_points/system_exposure_1_0_0.json @@ -0,0 +1,24 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "EXP", + "name": "System Exposure", + "description": "The Accessible Attack Surface of the Affected System or Service", + "values": [ + { + "key": "S", + "name": "Small", + "description": "Local service or program; highly controlled network" + }, + { + "key": "C", + "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": "U", + "name": "Unavoidable", + "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)" + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/system_exposure_1_0_1.json b/data/json/decision_points/system_exposure_1_0_1.json new file mode 100644 index 00000000..f287944d --- /dev/null +++ b/data/json/decision_points/system_exposure_1_0_1.json @@ -0,0 +1,24 @@ +{ + "namespace": "ssvc", + "version": "1.0.1", + "key": "EXP", + "name": "System Exposure", + "description": "The Accessible Attack Surface of the Affected System or Service", + "values": [ + { + "key": "S", + "name": "Small", + "description": "Local service or program; highly controlled network" + }, + { + "key": "C", + "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": "O", + "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)" + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/technical_impact_1_0_0.json b/data/json/decision_points/technical_impact_1_0_0.json new file mode 100644 index 00000000..a844a82b --- /dev/null +++ b/data/json/decision_points/technical_impact_1_0_0.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "TI", + "name": "Technical Impact", + "description": "The technical impact of the vulnerability.", + "values": [ + { + "key": "P", + "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": "T", + "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." + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/utility_1_0_0.json b/data/json/decision_points/utility_1_0_0.json new file mode 100644 index 00000000..16ebc6c4 --- /dev/null +++ b/data/json/decision_points/utility_1_0_0.json @@ -0,0 +1,24 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "U", + "name": "Utility", + "description": "The Usefulness of the Exploit to the Adversary", + "values": [ + { + "key": "L", + "name": "Laborious", + "description": "Slow virulence and diffuse value" + }, + { + "key": "E", + "name": "Efficient", + "description": "Rapid virulence and diffuse value OR Slow virulence and concentrated value" + }, + { + "key": "S", + "name": "Super Effective", + "description": "Rapid virulence and concentrated value" + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/utility_1_0_1.json b/data/json/decision_points/utility_1_0_1.json new file mode 100644 index 00000000..c0e2509e --- /dev/null +++ b/data/json/decision_points/utility_1_0_1.json @@ -0,0 +1,24 @@ +{ + "namespace": "ssvc", + "version": "1.0.1", + "key": "U", + "name": "Utility", + "description": "The Usefulness of the Exploit to the Adversary", + "values": [ + { + "key": "L", + "name": "Laborious", + "description": "No to automatable and diffuse value" + }, + { + "key": "E", + "name": "Efficient", + "description": "Yes to automatable and diffuse value OR No to automatable and concentrated value" + }, + { + "key": "S", + "name": "Super Effective", + "description": "Yes to automatable and concentrated value" + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/value_density_1_0_0.json b/data/json/decision_points/value_density_1_0_0.json new file mode 100644 index 00000000..2c2db1a4 --- /dev/null +++ b/data/json/decision_points/value_density_1_0_0.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "VD", + "name": "Value Density", + "description": "The concentration of value in the target", + "values": [ + { + "key": "D", + "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": "C", + "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." + } + ] +} \ No newline at end of file diff --git a/data/json/decision_points/virulence_1_0_0.json b/data/json/decision_points/virulence_1_0_0.json new file mode 100644 index 00000000..dfa91097 --- /dev/null +++ b/data/json/decision_points/virulence_1_0_0.json @@ -0,0 +1,19 @@ +{ + "namespace": "ssvc", + "version": "1.0.0", + "key": "V", + "name": "Virulence", + "description": "The speed at which the vulnerability can be exploited.", + "values": [ + { + "key": "S", + "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." + }, + { + "key": "R", + "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." + } + ] +} \ No newline at end of file diff --git a/docs/_generated/decision_points/automatable.md b/docs/_generated/decision_points/automatable.md new file mode 120000 index 00000000..a8229e62 --- /dev/null +++ b/docs/_generated/decision_points/automatable.md @@ -0,0 +1 @@ +automatable_2_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/automatable_2_0_0.md b/docs/_generated/decision_points/automatable_2_0_0.md new file mode 100644 index 00000000..96befcd5 --- /dev/null +++ b/docs/_generated/decision_points/automatable_2_0_0.md @@ -0,0 +1,17 @@ + +!!! note "Automatable v2.0.0" + + === "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 for this vulnerability. These steps are (1) reconnaissance, (2) weaponization, (3) delivery, and (4) exploitation. | + | Yes | Attackers can reliably automate steps 1-4 of the kill chain. | + + === "JSON" + + ```json + {% include "../../../data/json/decision_points/automatable_2_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/exploitation.md b/docs/_generated/decision_points/exploitation.md new file mode 120000 index 00000000..c236bcfd --- /dev/null +++ b/docs/_generated/decision_points/exploitation.md @@ -0,0 +1 @@ +exploitation_1_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/exploitation_1_0_0.md b/docs/_generated/decision_points/exploitation_1_0_0.md new file mode 100644 index 00000000..1b07d383 --- /dev/null +++ b/docs/_generated/decision_points/exploitation_1_0_0.md @@ -0,0 +1,18 @@ + +!!! note "Exploitation v1.0.0" + + === "Text" + + The present state of exploitation of the vulnerability. + + | Value | Definition | + |:-----|:-----------| + | None | There is no evidence of active exploitation and no public proof of concept (PoC) of how to exploit the vulnerability. | + | PoC | 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 | Shared, observable, reliable evidence that the exploit is being used in the wild by real attackers; there is credible public reporting. | + + === "JSON" + + ```json + {% include "../../../data/json/decision_points/exploitation_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/human_impact.md b/docs/_generated/decision_points/human_impact.md new file mode 120000 index 00000000..c42e394c --- /dev/null +++ b/docs/_generated/decision_points/human_impact.md @@ -0,0 +1 @@ +human_impact_1_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/human_impact_1_0_0.md b/docs/_generated/decision_points/human_impact_1_0_0.md new file mode 100644 index 00000000..9aac5149 --- /dev/null +++ b/docs/_generated/decision_points/human_impact_1_0_0.md @@ -0,0 +1,19 @@ + +!!! note "Human Impact v1.0.0" + + === "Text" + + Human Impact is a combination of Safety and Mission impacts. + + | Value | Definition | + |:-----|:-----------| + | Low | Safety=None/Minor, Mission=None/Degraded/Crippled | + | Medium | Safety=None/Minor, Mission=MEF Failure OR Safety=Major, Mission=None/Degraded/Crippled | + | High | Safety=Hazardous, Mission=None/Degraded/Crippled/MEF Failure OR Safety=Major, Mission=MEF Failure | + | Very High | Safety=Catastrophic OR Mission=Mission Failure | + + === "JSON" + + ```json + {% include "../../../data/json/decision_points/human_impact_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/mission_impact.md b/docs/_generated/decision_points/mission_impact.md new file mode 120000 index 00000000..938009ab --- /dev/null +++ b/docs/_generated/decision_points/mission_impact.md @@ -0,0 +1 @@ +mission_impact_2_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/mission_impact_1_0_0.md b/docs/_generated/decision_points/mission_impact_1_0_0.md new file mode 100644 index 00000000..3b8f858a --- /dev/null +++ b/docs/_generated/decision_points/mission_impact_1_0_0.md @@ -0,0 +1,20 @@ + +!!! note "Mission Impact v1.0.0" + + === "Text" + + Impact on Mission Essential Functions of the Organization + + | Value | Definition | + |:-----|:-----------| + | None | Little to no impact | + | Non-Essential Degraded | 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 "../../../data/json/decision_points/mission_impact_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/mission_impact_2_0_0.md b/docs/_generated/decision_points/mission_impact_2_0_0.md new file mode 100644 index 00000000..72aa323f --- /dev/null +++ b/docs/_generated/decision_points/mission_impact_2_0_0.md @@ -0,0 +1,19 @@ + +!!! note "Mission Impact v2.0.0" + + === "Text" + + Impact on Mission Essential Functions of the Organization + + | Value | Definition | + |:-----|:-----------| + | 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 "../../../data/json/decision_points/mission_impact_2_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/public_safety_impact.md b/docs/_generated/decision_points/public_safety_impact.md new file mode 120000 index 00000000..65bbe0a9 --- /dev/null +++ b/docs/_generated/decision_points/public_safety_impact.md @@ -0,0 +1 @@ +public_safety_impact_1_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/public_safety_impact_1_0_0.md b/docs/_generated/decision_points/public_safety_impact_1_0_0.md new file mode 100644 index 00000000..80b78d99 --- /dev/null +++ b/docs/_generated/decision_points/public_safety_impact_1_0_0.md @@ -0,0 +1,17 @@ + +!!! note "Public Safety Impact v1.0.0" + + === "Text" + + A coarse-grained representation of impact to public safety. + + | Value | Definition | + |:-----|:-----------| + | Minimal | Safety impact of None or Minor. | + | Significant | Safety impact of Major, Hazardous, or Catastrophic. | + + === "JSON" + + ```json + {% include "../../../data/json/decision_points/public_safety_impact_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/public_value_added.md b/docs/_generated/decision_points/public_value_added.md new file mode 120000 index 00000000..b185dcb5 --- /dev/null +++ b/docs/_generated/decision_points/public_value_added.md @@ -0,0 +1 @@ +public_value_added_1_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/public_value_added_1_0_0.md b/docs/_generated/decision_points/public_value_added_1_0_0.md new file mode 100644 index 00000000..9f315564 --- /dev/null +++ b/docs/_generated/decision_points/public_value_added_1_0_0.md @@ -0,0 +1,18 @@ + +!!! note "Public Value Added v1.0.0" + + === "Text" + + How much value would a publication from the coordinator benefit the broader community? + + | Value | Definition | + |:-----|:-----------| + | 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 "../../../data/json/decision_points/public_value_added_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/report_credibility.md b/docs/_generated/decision_points/report_credibility.md new file mode 120000 index 00000000..549fae08 --- /dev/null +++ b/docs/_generated/decision_points/report_credibility.md @@ -0,0 +1 @@ +report_credibility_1_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/report_credibility_1_0_0.md b/docs/_generated/decision_points/report_credibility_1_0_0.md new file mode 100644 index 00000000..7494a7b1 --- /dev/null +++ b/docs/_generated/decision_points/report_credibility_1_0_0.md @@ -0,0 +1,17 @@ + +!!! note "Report Credibility v1.0.0" + + === "Text" + + Is the report credible? + + | Value | Definition | + |:-----|:-----------| + | Credible | The report is credible. | + | Not Credible | The report is not credible. | + + === "JSON" + + ```json + {% include "../../../data/json/decision_points/report_credibility_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/report_public.md b/docs/_generated/decision_points/report_public.md new file mode 120000 index 00000000..1bd15fd2 --- /dev/null +++ b/docs/_generated/decision_points/report_public.md @@ -0,0 +1 @@ +report_public_1_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/report_public_1_0_0.md b/docs/_generated/decision_points/report_public_1_0_0.md new file mode 100644 index 00000000..80bee355 --- /dev/null +++ b/docs/_generated/decision_points/report_public_1_0_0.md @@ -0,0 +1,17 @@ + +!!! note "Report Public v1.0.0" + + === "Text" + + Is a viable report of the details of the vulnerability already publicly available? + + | Value | Definition | + |:-----|:-----------| + | No | No public report of the vulnerability exists. | + | Yes | A public report of the vulnerability exists. | + + === "JSON" + + ```json + {% include "../../../data/json/decision_points/report_public_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/safety_impact.md b/docs/_generated/decision_points/safety_impact.md new file mode 120000 index 00000000..55eace7f --- /dev/null +++ b/docs/_generated/decision_points/safety_impact.md @@ -0,0 +1 @@ +safety_impact_1_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/safety_impact_1_0_0.md b/docs/_generated/decision_points/safety_impact_1_0_0.md new file mode 100644 index 00000000..fa7b4c24 --- /dev/null +++ b/docs/_generated/decision_points/safety_impact_1_0_0.md @@ -0,0 +1,20 @@ + +!!! note "Safety Impact v1.0.0" + + === "Text" + + The safety impact of the vulnerability. + + | Value | Definition | + |:-----|:-----------| + | None | The effect is below the threshold for all aspects described in Minor. | + | Minor | 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 | 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 | 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 | 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. | + + === "JSON" + + ```json + {% include "../../../data/json/decision_points/safety_impact_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/supplier_cardinality.md b/docs/_generated/decision_points/supplier_cardinality.md new file mode 120000 index 00000000..518ef0e7 --- /dev/null +++ b/docs/_generated/decision_points/supplier_cardinality.md @@ -0,0 +1 @@ +supplier_cardinality_1_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/supplier_cardinality_1_0_0.md b/docs/_generated/decision_points/supplier_cardinality_1_0_0.md new file mode 100644 index 00000000..9dbdc154 --- /dev/null +++ b/docs/_generated/decision_points/supplier_cardinality_1_0_0.md @@ -0,0 +1,17 @@ + +!!! note "Supplier Cardinality v1.0.0" + + === "Text" + + How many suppliers are responsible for the vulnerable component and its remediation or mitigation plan? + + | Value | Definition | + |:-----|:-----------| + | One | There is only one supplier of the vulnerable component. | + | Multiple | There are multiple suppliers of the vulnerable component. | + + === "JSON" + + ```json + {% include "../../../data/json/decision_points/supplier_cardinality_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/supplier_contacted.md b/docs/_generated/decision_points/supplier_contacted.md new file mode 120000 index 00000000..7a40d514 --- /dev/null +++ b/docs/_generated/decision_points/supplier_contacted.md @@ -0,0 +1 @@ +supplier_contacted_1_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/supplier_contacted_1_0_0.md b/docs/_generated/decision_points/supplier_contacted_1_0_0.md new file mode 100644 index 00000000..aff63ba8 --- /dev/null +++ b/docs/_generated/decision_points/supplier_contacted_1_0_0.md @@ -0,0 +1,17 @@ + +!!! note "Supplier Contacted v1.0.0" + + === "Text" + + Has the reporter made a good-faith effort to contact the supplier of the vulnerable component using a quality contact method? + + | Value | Definition | + |:-----|:-----------| + | No | The supplier has not been contacted. | + | Yes | The supplier has been contacted. | + + === "JSON" + + ```json + {% include "../../../data/json/decision_points/supplier_contacted_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/supplier_engagement.md b/docs/_generated/decision_points/supplier_engagement.md new file mode 120000 index 00000000..7a13d88e --- /dev/null +++ b/docs/_generated/decision_points/supplier_engagement.md @@ -0,0 +1 @@ +supplier_engagement_1_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/supplier_engagement_1_0_0.md b/docs/_generated/decision_points/supplier_engagement_1_0_0.md new file mode 100644 index 00000000..2d3d9d18 --- /dev/null +++ b/docs/_generated/decision_points/supplier_engagement_1_0_0.md @@ -0,0 +1,17 @@ + +!!! note "Supplier Engagement v1.0.0" + + === "Text" + + Is the supplier responding to the reporter’s contact effort and actively participating in the coordination effort? + + | Value | Definition | + |:-----|:-----------| + | 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 not actively participating in the coordination effort. | + + === "JSON" + + ```json + {% include "../../../data/json/decision_points/supplier_engagement_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/supplier_involvement.md b/docs/_generated/decision_points/supplier_involvement.md new file mode 120000 index 00000000..9f97027b --- /dev/null +++ b/docs/_generated/decision_points/supplier_involvement.md @@ -0,0 +1 @@ +supplier_involvement_1_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/supplier_involvement_1_0_0.md b/docs/_generated/decision_points/supplier_involvement_1_0_0.md new file mode 100644 index 00000000..3e7504b2 --- /dev/null +++ b/docs/_generated/decision_points/supplier_involvement_1_0_0.md @@ -0,0 +1,18 @@ + +!!! note "Supplier Involvement v1.0.0" + + === "Text" + + What is the state of the supplier’s work on addressing the vulnerability? + + | Value | Definition | + |:-----|:-----------| + | 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 "../../../data/json/decision_points/supplier_involvement_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/system_exposure.md b/docs/_generated/decision_points/system_exposure.md new file mode 120000 index 00000000..38c20e7b --- /dev/null +++ b/docs/_generated/decision_points/system_exposure.md @@ -0,0 +1 @@ +system_exposure_1_0_1.md \ No newline at end of file diff --git a/docs/_generated/decision_points/system_exposure_1_0_0.md b/docs/_generated/decision_points/system_exposure_1_0_0.md new file mode 100644 index 00000000..4c6e977c --- /dev/null +++ b/docs/_generated/decision_points/system_exposure_1_0_0.md @@ -0,0 +1,18 @@ + +!!! note "System Exposure v1.0.0" + + === "Text" + + The Accessible Attack Surface of the Affected System or Service + + | Value | Definition | + |:-----|:-----------| + | 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. | + | Unavoidable | 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 "../../../data/json/decision_points/system_exposure_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/system_exposure_1_0_1.md b/docs/_generated/decision_points/system_exposure_1_0_1.md new file mode 100644 index 00000000..234fa98a --- /dev/null +++ b/docs/_generated/decision_points/system_exposure_1_0_1.md @@ -0,0 +1,18 @@ + +!!! note "System Exposure v1.0.1" + + === "Text" + + The Accessible Attack Surface of the Affected System or Service + + | Value | Definition | + |:-----|:-----------| + | 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 "../../../data/json/decision_points/system_exposure_1_0_1.json" %} + ``` diff --git a/docs/_generated/decision_points/technical_impact.md b/docs/_generated/decision_points/technical_impact.md new file mode 120000 index 00000000..8418d098 --- /dev/null +++ b/docs/_generated/decision_points/technical_impact.md @@ -0,0 +1 @@ +technical_impact_1_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/technical_impact_1_0_0.md b/docs/_generated/decision_points/technical_impact_1_0_0.md new file mode 100644 index 00000000..13603ec8 --- /dev/null +++ b/docs/_generated/decision_points/technical_impact_1_0_0.md @@ -0,0 +1,17 @@ + +!!! note "Technical Impact v1.0.0" + + === "Text" + + The technical impact of 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. | + | 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 "../../../data/json/decision_points/technical_impact_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/utility.md b/docs/_generated/decision_points/utility.md new file mode 120000 index 00000000..73464668 --- /dev/null +++ b/docs/_generated/decision_points/utility.md @@ -0,0 +1 @@ +utility_1_0_1.md \ No newline at end of file diff --git a/docs/_generated/decision_points/utility_1_0_0.md b/docs/_generated/decision_points/utility_1_0_0.md new file mode 100644 index 00000000..ad2c9515 --- /dev/null +++ b/docs/_generated/decision_points/utility_1_0_0.md @@ -0,0 +1,18 @@ + +!!! note "Utility v1.0.0" + + === "Text" + + The Usefulness of the Exploit to the Adversary + + | Value | Definition | + |:-----|:-----------| + | Laborious | Slow virulence and diffuse value | + | Efficient | Rapid virulence and diffuse value OR Slow virulence and concentrated value | + | Super Effective | Rapid virulence and concentrated value | + + === "JSON" + + ```json + {% include "../../../data/json/decision_points/utility_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/utility_1_0_1.md b/docs/_generated/decision_points/utility_1_0_1.md new file mode 100644 index 00000000..5051791c --- /dev/null +++ b/docs/_generated/decision_points/utility_1_0_1.md @@ -0,0 +1,18 @@ + +!!! note "Utility v1.0.1" + + === "Text" + + The Usefulness of the Exploit to the Adversary + + | Value | Definition | + |:-----|:-----------| + | Laborious | No to automatable and diffuse value | + | Efficient | Yes to automatable and diffuse value OR No to automatable and concentrated value | + | Super Effective | Yes to automatable and concentrated value | + + === "JSON" + + ```json + {% include "../../../data/json/decision_points/utility_1_0_1.json" %} + ``` diff --git a/docs/_generated/decision_points/value_density.md b/docs/_generated/decision_points/value_density.md new file mode 120000 index 00000000..d65e392d --- /dev/null +++ b/docs/_generated/decision_points/value_density.md @@ -0,0 +1 @@ +value_density_1_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/value_density_1_0_0.md b/docs/_generated/decision_points/value_density_1_0_0.md new file mode 100644 index 00000000..c1351297 --- /dev/null +++ b/docs/_generated/decision_points/value_density_1_0_0.md @@ -0,0 +1,17 @@ + +!!! note "Value Density v1.0.0" + + === "Text" + + The concentration of value in the target + + | 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 "../../../data/json/decision_points/value_density_1_0_0.json" %} + ``` diff --git a/docs/_generated/decision_points/virulence.md b/docs/_generated/decision_points/virulence.md new file mode 120000 index 00000000..e14f67c6 --- /dev/null +++ b/docs/_generated/decision_points/virulence.md @@ -0,0 +1 @@ +virulence_1_0_0.md \ No newline at end of file diff --git a/docs/_generated/decision_points/virulence_1_0_0.md b/docs/_generated/decision_points/virulence_1_0_0.md new file mode 100644 index 00000000..63e7497f --- /dev/null +++ b/docs/_generated/decision_points/virulence_1_0_0.md @@ -0,0 +1,17 @@ + +!!! note "Virulence v1.0.0" + + === "Text" + + The speed at which the vulnerability can be exploited. + + | Value | Definition | + |:-----|:-----------| + | Slow | 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. | + | Rapid | 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. | + + === "JSON" + + ```json + {% include "../../../data/json/decision_points/virulence_1_0_0.json" %} + ``` diff --git a/docs/reference/code/doctools.md b/docs/reference/code/doctools.md new file mode 100644 index 00000000..edd3b5e0 --- /dev/null +++ b/docs/reference/code/doctools.md @@ -0,0 +1,4 @@ +# Doctools + +::: ssvc.doctools + diff --git a/docs/reference/decision_points/automatable.md b/docs/reference/decision_points/automatable.md index 334b7f6d..2447a082 100644 --- a/docs/reference/decision_points/automatable.md +++ b/docs/reference/decision_points/automatable.md @@ -1,34 +1,11 @@ -!!! 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. | - - === "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." - } - ], - } - ``` +# Automatable + +{% include-markdown "../../_generated/decision_points/automatable.md" %} + +!!! tip "See also" + Automatable combines with [Value Density](./value_density.md) to inform + [Utility](./utility.md) [*Automatable*](#automatable) captures the answer to the question “Can an attacker reliably automate creating exploitation events for this vulnerability?” @@ -78,3 +55,13 @@ 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. +## Prior Versions + + +{% include-markdown "../../_generated/decision_points/virulence_1_0_0.md" %} + +!!! warning "Virulence is Superseded by Automatable" + + Virulence is superseded by Automatable, which clarified the concept we + we were attempting to capture. + \ No newline at end of file diff --git a/docs/reference/decision_points/exploitation.md b/docs/reference/decision_points/exploitation.md index f20ba87a..1f014488 100644 --- a/docs/reference/decision_points/exploitation.md +++ b/docs/reference/decision_points/exploitation.md @@ -1,15 +1,6 @@ # Exploitation -!!! note "Exploitation" - - Evidence of Active Exploitation of a Vulnerability - - | 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. | - +{% include-markdown "../../_generated/decision_points/exploitation.md" %} 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/human_impact.md b/docs/reference/decision_points/human_impact.md new file mode 100644 index 00000000..3ee4e7c6 --- /dev/null +++ b/docs/reference/decision_points/human_impact.md @@ -0,0 +1,8 @@ +# Human Impact + +{% include-markdown "../../_generated/decision_points/human_impact.md" %} + +!!! tip "See also" + + Human Impact is a combination of [Safety Impact](./safety_impact.md) and + [Mission Impact](./mission_impact.md) \ No newline at end of file diff --git a/docs/reference/decision_points/mission_impact.md b/docs/reference/decision_points/mission_impact.md index 02764ff4..68619f8b 100644 --- a/docs/reference/decision_points/mission_impact.md +++ b/docs/reference/decision_points/mission_impact.md @@ -1,16 +1,11 @@ # Mission Impact -!!! note "Mission Impact" +{% include-markdown "../../_generated/decision_points/mission_impact.md" %} - 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 | +!!! tip "See also" + Mission Impact combines with [Safety Impact](./safety_impact.md) to inform + [Human Impact](./human_impact.md) A **mission essential function (MEF)** is a function “directly related to accomplishing the organization’s mission as set forth in its statutory or executive charter” [@FCD2_2017, page A-1]. Identification and prioritization of mission essential functions enables effective continuity planning or crisis planning. @@ -31,7 +26,7 @@ While the processes, terminology, and audience for these different frameworks di In that sense they all function quite similarly within SSVC. Organizations should use whatever is most appropriate for their stakeholder context, with Mission Essential Function analysis serving as a fully worked example in the SSVC documents. -### Gathering Information About Mission Impact +## Gathering Information About Mission Impact The factors that influence the mission impact level are diverse. This paper does not exhaustively discuss how a stakeholder should answer a question; that is a topic for future work. @@ -42,3 +37,7 @@ It should require the vulnerability management team to interact with more senior As a heuristic, [*Utility*](#utility) might constrain [*Mission Impact*](#mission-impact) if both are not used in the same decision tree. For example, if the [*Utility*](#utility) is [*super effective*](#utility), then [*Mission Impact*](#mission-impact) is at least [*MEF support crippled*](#mission-impact). + +## Prior Versions + +{% include-markdown "../../_generated/decision_points/mission_impact_1_0_0.md" %} diff --git a/docs/reference/decision_points/public_safety_impact.md b/docs/reference/decision_points/public_safety_impact.md new file mode 100644 index 00000000..5b96ec8b --- /dev/null +++ b/docs/reference/decision_points/public_safety_impact.md @@ -0,0 +1,7 @@ +# Public Safety Impact + +{% include-markdown "../../_generated/decision_points/public_safety_impact.md" %} + +!!! tip "See also" + + - [Safety Impact](./safety_impact.md) diff --git a/docs/reference/decision_points/public_value_added.md b/docs/reference/decision_points/public_value_added.md index 7392c64a..1266c626 100644 --- a/docs/reference/decision_points/public_value_added.md +++ b/docs/reference/decision_points/public_value_added.md @@ -1,14 +1,6 @@ -!!! 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. | +# Public Value Added +{% include-markdown "../../_generated/decision_points/public_value_added.md" %} The intent of the definition is that one rarely if ever transitions from limited to ampliative or ampliative to precedence. A vulnerability could transition from precedence to ampliative and ampliative to limited. diff --git a/docs/reference/decision_points/report_credibility.md b/docs/reference/decision_points/report_credibility.md index c1ea7180..38424352 100644 --- a/docs/reference/decision_points/report_credibility.md +++ b/docs/reference/decision_points/report_credibility.md @@ -1,12 +1,6 @@ -!!! note "Report Credibility" - - 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. | +# Report Credibility +{% include-markdown "../../_generated/decision_points/report_credibility.md" %} !!! tip inline "See Also" diff --git a/docs/reference/decision_points/report_public.md b/docs/reference/decision_points/report_public.md index ed8ef39d..5f02a81e 100644 --- a/docs/reference/decision_points/report_public.md +++ b/docs/reference/decision_points/report_public.md @@ -1,9 +1,3 @@ -!!! 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. | +# Report Public +{% include-markdown "../../_generated/decision_points/report_public.md" %} diff --git a/docs/reference/decision_points/safety_impact.md b/docs/reference/decision_points/safety_impact.md index 97b8bcfa..92ed06a5 100644 --- a/docs/reference/decision_points/safety_impact.md +++ b/docs/reference/decision_points/safety_impact.md @@ -1,30 +1,13 @@ # Safety Impact -!!! 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 | +{% include-markdown "../../_generated/decision_points/safety_impact.md" %} +!!! tip "See also" + - Safety Impact combines with [Mission Impact](./mission_impact.md) to + inform [Human Impact](./human_impact.md). + - [Public Safety Impact](./public_safety_impact.md) provides a simplified + version of this decision point. We take an expansive view of safety, in which a safety violation is a violation of what the United States [Centers for Disease Control (CDC)](https://www.cdc.gov/hrqol/wellbeing.htm#three) calls **well-being**. Physical well-being violations are common safety violations, but we also consider economic, social, emotional, and psychological well-being to be important. Weighing fine differences among these categories is probably not possible, so we will not try. Each decision option lists examples of the effects that qualify for that value/answer in the various types of violations of well-being. These examples should not be considered comprehensive or exhaustive, but rather as suggestive. +!!! note "{dp.name} v{dp.version}" + + === "Text" + + {table} + + === "JSON" + + ```json + {{% include "{json_file}" %}} + ``` +""" + + +def to_markdown_table(dp: SsvcDecisionPoint) -> str: + """ + Generate a markdown table for a decision point. + + Args: + dp (SsvcDecisionPoint): The decision point to generate a markdown table for. + + Returns: + str: The markdown table. + """ + rows = [] + # prepend the header + rows.append(f"{dp.description}") + rows.append("") + indent = " " * 8 + rows.append(f"{indent}| Value | Definition |") + rows.append(f"{indent}|:-----|:-----------|") + + # add a row for each value + for value in dp.values: + rows.append(indent + MD_TABLE_ROW_TEMPLATE.format(value=value)) + + return "\n".join(rows) + + +# create a runtime context that ensures that dir exists +class EnsureDirExists: + """ + A runtime context that ensures that a directory exists or creates it otherwise. + + Example: + + with EnsureDirExists(dir): + assert os.path.exists(dir) + """ + + def __init__(self, dir: str): + """ + Create a new EnsureDirExists context. + + Args: + dir (str): The directory to ensure exists. + + Returns: + EnsureDirExists: The new EnsureDirExists context. + """ + self.dir = dir + + def __enter__(self): + os.makedirs(self.dir, exist_ok=True) + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + +def remove_if_exists(file): + try: + os.remove(file) + logger.debug(f"Removed {file}") + except FileNotFoundError: + logger.debug(f"File {file} does not exist, nothing to remove") + + +def dump_decision_point( + jsondir: str, outdir: str, dp: SsvcDecisionPoint, overwrite: bool +) -> dict: + """ + Generate the markdown table, json example, and markdown table file for a decision point. + + Args: + jsondir (str): The directory to write the json example to. + outdir (str): The directory to write the markdown table file to. + dp (SsvcDecisionPoint): The decision point to generate documentation for. + overwrite (bool): Whether to overwrite existing files. + + Returns: + dict: A dictionary with the following keys: + - include_file: The path to the markdown table file. + - symlink: The path to the symlink that points to the markdown table file. + - json_file: The path to the json example file. + """ + # - generate markdown table + # make dp.name safe for use in a filename + basename = _filename_friendly(dp.name) + f"_{_filename_friendly(dp.version)}" + # - generate json example + json_file = dump_json(basename, dp, jsondir, overwrite) + + # - generate markdown table file + r = dump_markdown(basename, dp, json_file, outdir, overwrite) + r["json_file"] = json_file + return r + + +def dump_markdown( + basename: str, dp: SsvcDecisionPoint, json_file: str, outdir: str, overwrite: bool +) -> dict: + """ + Generate the markdown table file for a decision point. + + Args: + basename (str): The basename of the markdown table file. + dp (SsvcDecisionPoint): The decision point to generate documentation for. + json_file (str): The path to the json example file. + outdir (str): The directory to write the markdown table file to. + overwrite (bool): Whether to overwrite existing files. + + Returns: + dict: A dictionary with the following keys: + - include_file: The path to the markdown table file. + - symlink: The path to the symlink that points to the markdown table file. + """ + include_file = f"{outdir}/{basename}.md" + + relative_json_file = os.path.relpath(json_file, outdir) + + if overwrite: + remove_if_exists(include_file) + with EnsureDirExists(outdir): + try: + with open(include_file, "x") as f: + formatted_template = MD_INCLUDE_TEMPLATE.format( + dp=dp, + json_file=relative_json_file, + table=(to_markdown_table(dp)), + ) + f.write(formatted_template) + except FileExistsError: + logger.warning( + f"File {include_file} already exists, use --overwrite to replace" + ) + + # update the symlink + # because we don't want to have to edit each markdown file every time something changes + symlink = f"{outdir}/{_filename_friendly(dp.name)}.md" + remove_if_exists(symlink) + relative_md_file = os.path.relpath(include_file, outdir) + os.symlink(relative_md_file, symlink) + + result = { + "include_file": include_file, + "symlink": symlink, + } + + return result + + +def dump_json( + basename: str, dp: SsvcDecisionPoint, jsondir: str, overwrite: bool +) -> str: + """ + Generate the json example for a decision point. + + Args: + basename (str): The basename of the json example file. + dp (SsvcDecisionPoint): The decision point to generate documentation for. + jsondir (str): The directory to write the json example to. + overwrite (bool): Whether to overwrite existing files. + + Returns: + str: The path to the json example file. + """ + json_file = f"{jsondir}/{basename}.json" + if overwrite: + remove_if_exists(json_file) + with EnsureDirExists(jsondir): + try: + with open(json_file, "x") as f: + f.write(dp.to_json(indent=2)) + except FileExistsError: + logger.warning( + f"File {json_file} already exists, use --overwrite to replace" + ) + return json_file + + +def main(): + # we are going to generate three files for each decision point: + # - a markdown table that can be used in the decision point documentation + # - a json example that can be used in the decision point documentation + # - a markdown file that builds an mkdocs table to switch between the markdown description and the json + # example using markdown-include plugin of mkdocs + + # parse command line args + import argparse + + parser = argparse.ArgumentParser( + description="Generate decision point documentation" + ) + parser.add_argument( + "--overwrite", + action="store_true", + help="overwrite existing files", + default=False, + ) + + parser.add_argument("--outdir", help="output directory", default="./tmp/md_out") + parser.add_argument( + "--jsondir", help="json output directory", default="./tmp/json_out" + ) + args = parser.parse_args() + + overwrite = args.overwrite + outdir = args.outdir + jsondir = args.jsondir + + # for each decision point: + for dp in REGISTERED_DECISION_POINTS: + dump_decision_point(jsondir, outdir, dp, overwrite) + + +if __name__ == "__main__": + logger = logging.getLogger() + logger.setLevel(logging.DEBUG) + if not logger.hasHandlers(): + hdlr = logging.StreamHandler() + logger.addHandler(hdlr) + + main() diff --git a/src/ssvc/dp_groups/base.py b/src/ssvc/dp_groups/base.py index 6213d14b..3c37512b 100644 --- a/src/ssvc/dp_groups/base.py +++ b/src/ssvc/dp_groups/base.py @@ -49,8 +49,8 @@ def __len__(self): def get_all_decision_points_from( - glist: list[SsvcDecisionPointGroup], -) -> Iterable[SsvcDecisionPoint]: + *groups: Iterable[SsvcDecisionPointGroup], +) -> list[SsvcDecisionPoint]: """ Given a list of SsvcDecisionPointGroup objects, return a list of all the unique SsvcDecisionPoint objects contained in those groups. @@ -62,15 +62,22 @@ def get_all_decision_points_from( list: A list of SsvcDecisionPoint objects. """ dps = [] - for group in glist: + seen = set() + + for group in groups: 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) + return list(dps) def main(): diff --git a/src/ssvc/dp_groups/ssvc/__init__.py b/src/ssvc/dp_groups/ssvc/__init__.py new file mode 100644 index 00000000..ebb125a0 --- /dev/null +++ b/src/ssvc/dp_groups/ssvc/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) 2023 Carnegie Mellon University and Contributors. +# - see Contributors.md for a full list of Contributors +# - see ContributionInstructions.md for information on how you can Contribute to this project +# Stakeholder Specific Vulnerability Categorization (SSVC) is +# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed +# with this Software or contact permission@sei.cmu.edu for full terms. +# Created, in part, with funding and support from the United States Government +# (see Acknowledgments file). This program may include and/or can make use of +# certain third party source code, object code, documentation and other files +# (“Third Party Software”). See LICENSE.md for more details. +# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the +# U.S. Patent and Trademark Office by Carnegie Mellon University diff --git a/src/ssvc/dp_groups/ssvc/collections.py b/src/ssvc/dp_groups/ssvc/collections.py new file mode 100644 index 00000000..006d824a --- /dev/null +++ b/src/ssvc/dp_groups/ssvc/collections.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +""" +Provides collections of decision points for each version of the SSVC. +""" +# Copyright (c) 2023 Carnegie Mellon University and Contributors. +# - see Contributors.md for a full list of Contributors +# - see ContributionInstructions.md for information on how you can Contribute to this project +# Stakeholder Specific Vulnerability Categorization (SSVC) is +# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed +# with this Software or contact permission@sei.cmu.edu for full terms. +# Created, in part, with funding and support from the United States Government +# (see Acknowledgments file). This program may include and/or can make use of +# certain third party source code, object code, documentation and other files +# (“Third Party Software”). See LICENSE.md for more details. +# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the +# U.S. Patent and Trademark Office by Carnegie Mellon University + + +from ssvc.dp_groups.base import SsvcDecisionPointGroup, get_all_decision_points_from +from ssvc.dp_groups.ssvc.coordinator_publication import COORDINATOR_PUBLICATION_1 +from ssvc.dp_groups.ssvc.coordinator_triage import COORDINATOR_TRIAGE_1 +from ssvc.dp_groups.ssvc.deployer import DEPLOYER_2, DEPLOYER_3, PATCH_APPLIER_1 +from ssvc.dp_groups.ssvc.supplier import PATCH_DEVELOPER_1, SUPPLIER_2 + + +SSVCv1 = SsvcDecisionPointGroup( + name="SSVCv1", + description="The first version of the SSVC.", + version="1.0.0", + decision_points=get_all_decision_points_from(PATCH_APPLIER_1, PATCH_DEVELOPER_1), +) +SSVCv2 = SsvcDecisionPointGroup( + name="SSVCv2", + description="The second version of the SSVC.", + version="2.0.0", + decision_points=get_all_decision_points_from( + COORDINATOR_PUBLICATION_1, COORDINATOR_TRIAGE_1, DEPLOYER_2, 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( + COORDINATOR_PUBLICATION_1, COORDINATOR_TRIAGE_1, DEPLOYER_3, SUPPLIER_2 + ), +) + + +def main(): + for dpg in [SSVCv1, SSVCv2, SSVCv2_1]: + print(dpg.to_json(indent=2)) + + +if __name__ == "__main__": + main() diff --git a/src/ssvc/dp_groups/coordinator_publication.py b/src/ssvc/dp_groups/ssvc/coordinator_publication.py similarity index 53% rename from src/ssvc/dp_groups/coordinator_publication.py rename to src/ssvc/dp_groups/ssvc/coordinator_publication.py index 63d3a34a..90258406 100644 --- a/src/ssvc/dp_groups/coordinator_publication.py +++ b/src/ssvc/dp_groups/ssvc/coordinator_publication.py @@ -4,6 +4,19 @@ author: adh created_at: 9/21/23 11:40 AM """ +# Copyright (c) 2023 Carnegie Mellon University and Contributors. +# - see Contributors.md for a full list of Contributors +# - see ContributionInstructions.md for information on how you can Contribute to this project +# Stakeholder Specific Vulnerability Categorization (SSVC) is +# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed +# with this Software or contact permission@sei.cmu.edu for full terms. +# Created, in part, with funding and support from the United States Government +# (see Acknowledgments file). This program may include and/or can make use of +# certain third party source code, object code, documentation and other files +# (“Third Party Software”). See LICENSE.md for more details. +# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the +# U.S. Patent and Trademark Office by Carnegie Mellon University + from ssvc.decision_points.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 diff --git a/src/ssvc/dp_groups/coordinator_triage.py b/src/ssvc/dp_groups/ssvc/coordinator_triage.py similarity index 65% rename from src/ssvc/dp_groups/coordinator_triage.py rename to src/ssvc/dp_groups/ssvc/coordinator_triage.py index 6d6f352b..161e50e4 100644 --- a/src/ssvc/dp_groups/coordinator_triage.py +++ b/src/ssvc/dp_groups/ssvc/coordinator_triage.py @@ -4,7 +4,20 @@ author: adh created_at: 9/21/23 11:40 AM """ -from ssvc.decision_points.automatable import AUTOMATABLE_1 +# Copyright (c) 2023 Carnegie Mellon University and Contributors. +# - see Contributors.md for a full list of Contributors +# - see ContributionInstructions.md for information on how you can Contribute to this project +# Stakeholder Specific Vulnerability Categorization (SSVC) is +# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed +# with this Software or contact permission@sei.cmu.edu for full terms. +# Created, in part, with funding and support from the United States Government +# (see Acknowledgments file). This program may include and/or can make use of +# certain third party source code, object code, documentation and other files +# (“Third Party Software”). See LICENSE.md for more details. +# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the +# U.S. Patent and Trademark Office by Carnegie Mellon University + +from ssvc.decision_points.automatable import AUTOMATABLE_2 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 @@ -28,7 +41,7 @@ SUPPLIER_CARDINALITY_1, SUPPLIER_ENGAGEMENT_1, UTILITY_1_0_1, - AUTOMATABLE_1, + AUTOMATABLE_2, VALUE_DENSITY_1, PUBLIC_SAFETY_IMPACT_1, SAFETY_IMPACT_1, diff --git a/src/ssvc/dp_groups/deployer.py b/src/ssvc/dp_groups/ssvc/deployer.py similarity index 75% rename from src/ssvc/dp_groups/deployer.py rename to src/ssvc/dp_groups/ssvc/deployer.py index e375510e..348502bc 100644 --- a/src/ssvc/dp_groups/deployer.py +++ b/src/ssvc/dp_groups/ssvc/deployer.py @@ -5,7 +5,20 @@ created_at: 9/21/23 11:40 AM """ -from ssvc.decision_points.automatable import AUTOMATABLE_1 +# Copyright (c) 2023 Carnegie Mellon University and Contributors. +# - see Contributors.md for a full list of Contributors +# - see ContributionInstructions.md for information on how you can Contribute to this project +# Stakeholder Specific Vulnerability Categorization (SSVC) is +# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed +# with this Software or contact permission@sei.cmu.edu for full terms. +# Created, in part, with funding and support from the United States Government +# (see Acknowledgments file). This program may include and/or can make use of +# certain third party source code, object code, documentation and other files +# (“Third Party Software”). See LICENSE.md for more details. +# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the +# U.S. Patent and Trademark Office by Carnegie Mellon University + +from ssvc.decision_points.automatable import AUTOMATABLE_2 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 @@ -54,7 +67,7 @@ MISSION_IMPACT_1, SAFETY_IMPACT_1, UTILITY_1_0_1, - AUTOMATABLE_1, + AUTOMATABLE_2, VALUE_DENSITY_1, HUMAN_IMPACT_1, ], @@ -87,7 +100,7 @@ SYSTEM_EXPOSURE_1_0_1, MISSION_IMPACT_2, SAFETY_IMPACT_1, - AUTOMATABLE_1, + AUTOMATABLE_2, HUMAN_IMPACT_1, ), ) diff --git a/src/ssvc/dp_groups/supplier.py b/src/ssvc/dp_groups/ssvc/supplier.py similarity index 68% rename from src/ssvc/dp_groups/supplier.py rename to src/ssvc/dp_groups/ssvc/supplier.py index 126ec913..b245d035 100644 --- a/src/ssvc/dp_groups/supplier.py +++ b/src/ssvc/dp_groups/ssvc/supplier.py @@ -5,13 +5,25 @@ created_at: 9/21/23 11:41 AM """ -from ssvc.decision_points.automatable import AUTOMATABLE_1 +# Copyright (c) 2023 Carnegie Mellon University and Contributors. +# - see Contributors.md for a full list of Contributors +# - see ContributionInstructions.md for information on how you can Contribute to this project +# Stakeholder Specific Vulnerability Categorization (SSVC) is +# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed +# with this Software or contact permission@sei.cmu.edu for full terms. +# Created, in part, with funding and support from the United States Government +# (see Acknowledgments file). This program may include and/or can make use of +# certain third party source code, object code, documentation and other files +# (“Third Party Software”). See LICENSE.md for more details. +# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the +# U.S. Patent and Trademark Office by Carnegie Mellon University + +from ssvc.decision_points.automatable import AUTOMATABLE_2, VIRULENCE_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( @@ -52,7 +64,7 @@ EXPLOITATION_1, UTILITY_1_0_1, TECHNICAL_IMPACT_1, - AUTOMATABLE_1, + AUTOMATABLE_2, VALUE_DENSITY_1, SAFETY_IMPACT_1, ], diff --git a/src/ssvc/dp_groups/v1.py b/src/ssvc/dp_groups/v1.py deleted file mode 100644 index 049b34db..00000000 --- a/src/ssvc/dp_groups/v1.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/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 deleted file mode 100644 index 636217e0..00000000 --- a/src/ssvc/dp_groups/v2.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/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 deleted file mode 100644 index f2409de5..00000000 --- a/src/ssvc/dp_groups/v2_1.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/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/ssvc/policy_generator.py b/src/ssvc/policy_generator.py index 01a490aa..310015ac 100644 --- a/src/ssvc/policy_generator.py +++ b/src/ssvc/policy_generator.py @@ -18,6 +18,7 @@ import itertools import logging +import math from typing import List, Tuple import networkx as nx @@ -76,7 +77,19 @@ def __init__( weight = 1.0 / len(list(self.outcomes)) self.outcome_weights = [weight for _ in self.outcomes] else: + # validate the number of outcome weights + if len(outcome_weights) != len(list(self.outcomes)): + raise ValueError( + f"Outcome weights must have {len(list(self.outcomes))} elements, but has {len(outcome_weights)}" + ) + + # validate that the outcome weights sum to 1.0 + total = sum(outcome_weights) + if not math.isclose(total, 1.0): + raise ValueError(f"Outcome weights must sum to 1.0, but sum to {total}") + self.outcome_weights = outcome_weights + logger.debug(f"Outcome weights: {self.outcome_weights}") self.policy: pd.DataFrame = None diff --git a/src/test/test_doctools.py b/src/test/test_doctools.py new file mode 100644 index 00000000..a41095cd --- /dev/null +++ b/src/test/test_doctools.py @@ -0,0 +1,221 @@ +# Copyright (c) 2023 Carnegie Mellon University and Contributors. +# - see Contributors.md for a full list of Contributors +# - see ContributionInstructions.md for information on how you can Contribute to this project +# Stakeholder Specific Vulnerability Categorization (SSVC) is +# licensed under a MIT (SEI)-style license, please see LICENSE.md distributed +# with this Software or contact permission@sei.cmu.edu for full terms. +# Created, in part, with funding and support from the United States Government +# (see Acknowledgments file). This program may include and/or can make use of +# certain third party source code, object code, documentation and other files +# (“Third Party Software”). See LICENSE.md for more details. +# Carnegie Mellon®, CERT® and CERT Coordination Center® are registered in the +# U.S. Patent and Trademark Office by Carnegie Mellon University +import json +import logging +import os +import tempfile +import unittest + +from ssvc.decision_points import SsvcDecisionPoint +from ssvc.doctools import ( + EnsureDirExists, + _filename_friendly, + dump_decision_point, + dump_json, + dump_markdown, + remove_if_exists, + to_markdown_table, +) + +_dp_dict = { + "namespace": "ssvc", + "version": "1.0.0", + "key": "DPT", + "name": "Decision Point Test", + "description": "This is a test decision point.", + "values": [ + {"key": "N", "name": "No", "description": "No means no"}, + {"key": "Y", "name": "Yes", "description": "Yes means yes"}, + ], +} + + +class MyTestCase(unittest.TestCase): + def setUp(self) -> None: + self.dp = SsvcDecisionPoint.from_dict(_dp_dict) + + # create a temp working dir + self.tempdir = tempfile.TemporaryDirectory() + + def tearDown(self) -> None: + # remove the temp working dir + self.tempdir.cleanup() + self.assertFalse(os.path.exists(self.tempdir.name)) + + def test__filename_friendly(self): + # replace spaces with underscores + self.assertEqual("foo_bar", _filename_friendly("foo bar")) + # replace periods with underscores + self.assertEqual("foo_bar", _filename_friendly("foo.bar")) + # lowercase the string + self.assertEqual("foo_bar", _filename_friendly("Foo.Bar")) + + def test_to_markdown_table(self): + dp = self.dp + + table = to_markdown_table(dp) + self.assertIn(dp.description, table) + # self.assertIn(dp.name, table) + # self.assertIn(dp.version, table) + for value in dp.values: + self.assertIn(value.name, table) + self.assertIn(value.description, table) + self.assertIn(value.key, table) + + def test_ensure_dir_exists(self): + path = os.path.join(self.tempdir.name, "foo") + self.assertFalse(os.path.exists(path)) + + with EnsureDirExists(path): + self.assertTrue(os.path.exists(path)) + + def test_remove_if_exists(self): + path = os.path.join(self.tempdir.name, "foo") + self.assertFalse(os.path.exists(path)) + + # should work without error + self.assertIsNone(remove_if_exists(path)) + + # create the file + open(path, "w").close() + self.assertTrue(os.path.exists(path)) + + # should work without error + self.assertIsNone(remove_if_exists(path)) + + # should have removed the file + self.assertFalse(os.path.exists(path)) + + def test_dump_decision_point(self): + jsondir = os.path.join(self.tempdir.name, "json") + outdir = os.path.join(self.tempdir.name, "out") + dp = self.dp + overwrite = False + + self.assertEqual(0, len(os.listdir(self.tempdir.name))) + + # should create the files in the expected places + r = dump_decision_point(jsondir, outdir, dp, overwrite) + self.assertTrue(os.path.exists(r["include_file"])) + self.assertTrue(os.path.exists(r["symlink"])) + self.assertTrue(os.path.exists(r["json_file"])) + + # not checking these thoroughly, just making sure they are there + # because they are tested elsewhere in dump_markdown and dump_json + + def test_dump_markdown(self): + # dump_markdown should create a file, write to it, and then create a generic symlink + basename = "foo" + dp = self.dp + json_file = os.path.join(self.tempdir.name, f"{basename}.json") + outdir = self.tempdir.name + overwrite = False + + # should create the file in the expected place + include_file = os.path.join(outdir, f"{basename}.md") + symlink = os.path.join(outdir, f"{_filename_friendly(dp.name)}.md") + + self.assertFalse(os.path.exists(include_file)) + self.assertFalse(os.path.exists(symlink)) + r = dump_markdown(basename, dp, json_file, outdir, overwrite) + self.assertTrue(os.path.exists(include_file)) + + self.assertEqual(include_file, r["include_file"]) + self.assertEqual(symlink, r["symlink"]) + + # the file contains text based on the dp + with open(include_file, "r") as f: + text = f.read() + + self.assertIn(dp.description, text) + self.assertIn(dp.name, text) + self.assertIn(dp.version, text) + for value in dp.values: + self.assertIn(value.name, text) + self.assertIn(value.description, text) + self.assertIn(value.key, text) + + # should create the symlink in the expected place + self.assertTrue(os.path.exists(symlink), symlink) + # should be a symlink + self.assertTrue(os.path.islink(symlink)) + # should point to the include file + self.assertEqual(os.path.realpath(symlink), os.path.realpath(include_file)) + + # should not overwrite the file + overwrite = False + # capture logger output + with self.assertLogs() as cm: + dump_markdown(basename, dp, json_file, outdir, overwrite) + # logger warns that the file exists + self.assertIn("already exists", cm.output[0]) + + # should overwrite the file + overwrite = True + dp.name = "Different Decision Point" + # capture logger output + with self.assertLogs(level=logging.DEBUG) as cm: + dump_markdown(basename, dp, json_file, outdir, overwrite) + # logger warns that the file was removed + self.assertIn("Removed", cm.output[0]) + + def test_dump_json(self): + basename = "foo" + dp = self.dp + jsondir = self.tempdir.name + overwrite = False + + _jsonfile = os.path.join(jsondir, f"{basename}.json") + self.assertFalse(os.path.exists(_jsonfile)) + + # should create the file in the expected place + json_file = dump_json(basename, dp, jsondir, overwrite) + self.assertEqual(_jsonfile, json_file) + self.assertTrue(os.path.exists(json_file)) + + # file is loadable json + d = json.load(open(json_file)) + for k, v in dp.to_dict().items(): + self.assertEqual(v, d[k]) + + # should not overwrite the file + overwrite = False + # capture logger output + with self.assertLogs() as cm: + json_file = dump_json(basename, dp, jsondir, overwrite) + self.assertEqual(_jsonfile, json_file) + # logger warns that the file exists + self.assertIn("already exists", cm.output[0]) + + # should overwrite the file + overwrite = True + + dp.name = "Different Decision Point" + # capture logger output + with self.assertLogs(level=logging.DEBUG) as cm: + json_file = dump_json(basename, dp, jsondir, overwrite) + + self.assertEqual(_jsonfile, json_file) + # logger warns that the file was removed + self.assertIn("Removed", cm.output[0]) + + # the file was overwritten + d = json.load(open(json_file)) + self.assertEqual(dp.name, d["name"]) + + def test_main(self): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/src/test/test_dp_base.py b/src/test/test_dp_base.py index 969ef03c..4fcd7012 100644 --- a/src/test/test_dp_base.py +++ b/src/test/test_dp_base.py @@ -20,9 +20,14 @@ class MyTestCase(unittest.TestCase): def setUp(self) -> None: self.original_registry = base.REGISTERED_DECISION_POINTS.copy() - self.value = base.SsvcDecisionPointValue( - name="foo", key="bar", description="baz" - ) + # add multiple values + self.values = [] + for i in range(3): + self.values.append( + base.SsvcDecisionPointValue( + name=f"foo{i}", key=f"bar{i}", description=f"baz{i}" + ) + ) self.dp = base.SsvcDecisionPoint( name="foo", @@ -30,7 +35,7 @@ def setUp(self) -> None: description="baz", version="1.0.0", namespace="ns", - values=(self.value,), + values=tuple(self.values), ) def tearDown(self) -> None: @@ -47,10 +52,24 @@ def test_registry(self): description="asdfasdf", version="1.33.1", namespace="asdfasdf", - values=( - self.value, - self.value, - ), + values=tuple(self.values), + ) + + def tearDown(self) -> None: + # restore the original registry + base.REGISTERED_DECISION_POINTS = self.original_registry + + def test_registry(self): + # just by creating the objects, they should be registered + self.assertIn(self.dp, base.REGISTERED_DECISION_POINTS) + + dp2 = base.SsvcDecisionPoint( + name="asdfad", + key="asdfasdf", + description="asdfasdf", + version="1.33.1", + namespace="asdfasdf", + values=self.values, ) dp2._comment = "asdfasdfasdf" @@ -58,15 +77,15 @@ def test_registry(self): self.assertIn(dp2, base.REGISTERED_DECISION_POINTS) 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") + for i, obj in enumerate(self.values): + # should have name, key, description + self.assertEqual(obj.name, f"foo{i}") + self.assertEqual(obj.key, f"bar{i}") + self.assertEqual(obj.description, f"baz{i}") - # should not have namespace, version - self.assertFalse(hasattr(obj, "namespace")) - self.assertFalse(hasattr(obj, "version")) + # should not have namespace, version + self.assertFalse(hasattr(obj, "namespace")) + self.assertFalse(hasattr(obj, "version")) def test_ssvc_decision_point(self): obj = self.dp @@ -76,17 +95,16 @@ def test_ssvc_decision_point(self): self.assertEqual(obj.description, "baz") self.assertEqual(obj.version, "1.0.0") self.assertEqual(obj.namespace, "ns") - self.assertEqual(len(obj.values), 1) + self.assertEqual(len(self.values), len(obj.values)) def test_ssvc_value_json_roundtrip(self): - obj = self.value + for i, obj in enumerate(self.values): + json = obj.to_json() + self.assertIsInstance(json, str) + self.assertGreater(len(json), 0) - json = obj.to_json() - self.assertIsInstance(json, str) - self.assertGreater(len(json), 0) - - obj2 = base.SsvcDecisionPointValue.from_json(json) - self.assertEqual(obj, obj2) + obj2 = base.SsvcDecisionPointValue.from_json(json) + self.assertEqual(obj, obj2) def test_ssvc_decision_point_json_roundtrip(self): obj = self.dp @@ -96,19 +114,10 @@ def test_ssvc_decision_point_json_roundtrip(self): self.assertGreater(len(json), 0) obj2 = base.SsvcDecisionPoint.from_json(json) - self.assertEqual(obj.to_dict(), obj2.to_dict()) - - def test_dp_to_table(self): - obj = self.dp - table = base.dp_to_table(obj) - - self.assertIn(obj.description, table) - self.assertIn("Value", table) - self.assertIn("Key", table) - self.assertIn("Description", table) - self.assertIn(obj.name, table) - self.assertIn(obj.key, table) + # the objects should be equal + self.assertEqual(obj, obj2) + self.assertEqual(obj.to_dict(), obj2.to_dict()) if __name__ == "__main__": diff --git a/src/test/test_schema.py b/src/test/test_schema.py index ea6b2412..cab2a20c 100644 --- a/src/test/test_schema.py +++ b/src/test/test_schema.py @@ -23,10 +23,9 @@ from ssvc.decision_points.critical_software import CRITICAL_SOFTWARE_1 # noqa from ssvc.decision_points.high_value_asset import HIGH_VALUE_ASSET_1 # noqa from ssvc.decision_points.in_kev import IN_KEV_1 +# importing these causes the decision points to register themselves +from ssvc.dp_groups.ssvc.collections import SSVCv1, SSVCv2, SSVCv2_1 # noqa from ssvc.dp_groups.cvss.collections import CVSSv1, CVSSv2, CVSSv3, CVSSv4_BTE # noqa -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 def find_schema(basepath: str) -> str: