nvd_cvss/v3/
mod.rs

1//!
2//! Common Vulnerability Scoring System version 3.1
3//! ===============================================
4//!
5//!
6//! CVSS Version 3.1 Release
7//! ------------------------
8//!
9//! This page updates with each release of the CVSS standard. It is currently CVSS version 3.1, released in June 2019. If you wish to use a specific version of the Specification Document, use:
10//!
11//! *   [https://www.first.org/cvss/v3.1/specification-document](https://www.first.org/cvss/v3.1/specification-document) for CVSS version 3.1
12//! *   [https://www.first.org/cvss/v3.0/specification-document](https://www.first.org/cvss/v3.0/specification-document) for CVSS version 3.0
13//!
14//! * * *
15//!
16
17use std::fmt::{Display, Formatter};
18use std::str::FromStr;
19
20use serde::{Deserialize, Serialize};
21
22use crate::error::{CVSSError, Result};
23use crate::metric::{Metric, MetricLevelType};
24use crate::severity::SeverityType;
25use crate::v3::attack_complexity::AttackComplexityType;
26use crate::v3::attack_vector::AttackVectorType;
27use crate::v3::impact_metrics::{
28  AvailabilityImpactType, ConfidentialityImpactType, Impact, IntegrityImpactType,
29};
30use crate::v3::privileges_required::PrivilegesRequiredType;
31use crate::v3::scope::ScopeType;
32use crate::v3::user_interaction::UserInteractionType;
33use crate::version::Version;
34
35pub mod attack_complexity;
36pub mod attack_vector;
37pub mod impact_metrics;
38pub mod privileges_required;
39pub mod scope;
40pub mod user_interaction;
41
42/// 2.1. Exploitability Metrics
43///
44/// As mentioned, the Exploitability metrics reflect the characteristics of the thing that is vulnerable, which we refer to formally as the vulnerable component. Therefore, each of the Exploitability metrics listed below should be scored relative to the vulnerable component, and reflect the properties of the vulnerability that lead to a successful attack.
45///
46#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
47#[serde(rename_all = "camelCase")]
48pub struct ExploitAbility {
49  /// [`AttackVectorType`] 访问途径(AV)
50  pub attack_vector: AttackVectorType,
51  /// [`AttackComplexityType`] 攻击复杂度(AC)
52  pub attack_complexity: AttackComplexityType,
53  /// [`PrivilegesRequiredType`] 所需权限(PR)
54  pub privileges_required: PrivilegesRequiredType,
55  /// [`UserInteractionType`] 用户交互(UI)
56  pub user_interaction: UserInteractionType,
57}
58
59impl ExploitAbility {
60  /// 8.22 × 𝐴𝑡𝑡𝑎𝑐𝑘𝑉𝑒𝑐𝑡𝑜𝑟 × 𝐴𝑡𝑡𝑎𝑐𝑘𝐶𝑜𝑚𝑝𝑙𝑒𝑥𝑖𝑡𝑦 × 𝑃𝑟𝑖𝑣𝑖𝑙𝑒𝑔𝑒𝑅𝑒𝑞𝑢𝑖𝑟𝑒𝑑 × 𝑈𝑠𝑒𝑟𝐼𝑛𝑡𝑒𝑟𝑎𝑐𝑡𝑖𝑜𝑛
61  pub fn score(&self, scope_is_changed: bool) -> f32 {
62    8.22
63      * self.attack_vector.score()
64      * self.attack_complexity.score()
65      * self.user_interaction.score()
66      * self.privileges_required.scoped_score(scope_is_changed)
67  }
68}
69
70///
71/// The Common Vulnerability Scoring System (CVSS) captures the principal technical characteristics of software, hardware and firmware vulnerabilities. Its outputs include numerical scores indicating the severity of a vulnerability relative to other vulnerabilities.
72///
73/// CVSS is composed of three metric groups: Base, Temporal, and Environmental. The Base Score reflects the severity of a vulnerability according to its intrinsic characteristics which are constant over time and assumes the reasonable worst case impact across different deployed environments. The Temporal Metrics adjust the Base severity of a vulnerability based on factors that change over time, such as the availability of knowledge_base code. The Environmental Metrics adjust the Base and Temporal severities to a specific computing environment. They consider factors such as the presence of mitigations in that environment.
74///
75/// Base Scores are usually produced by the organization maintaining the vulnerable product, or a third party scoring on their behalf. It is typical for only the Base Metrics to be published as these do not change over time and are common to all environments. Consumers of CVSS should supplement the Base Score with Temporal and Environmental Scores specific to their use of the vulnerable product to produce a severity more accurate for their organizational environment. Consumers may use CVSS information as input to an organizational vulnerability management process that also considers factors that are not part of CVSS in order to rank the threats to their technology infrastructure and make informed remediation decisions. Such factors may include: number of customers on a product line, monetary losses due to a breach, life or property threatened, or public sentiment on highly publicized vulnerabilities. These are outside the scope of CVSS.
76///
77/// The benefits of CVSS include the provision of a standardized vendor and platform agnostic vulnerability scoring methodology. It is an open framework, providing transparency to the individual characteristics and methodology used to derive a score.
78///
79#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
80#[serde(rename_all = "camelCase")]
81pub struct CVSS {
82  /// Version 版本: 3.0 和 3.1
83  pub version: Version,
84  /// 向量: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H"
85  pub vector_string: String,
86  #[serde(flatten)]
87  pub exploit_ability: ExploitAbility,
88  /// [`ScopeType`] 影响范围(S)
89  pub scope: ScopeType,
90  #[serde(flatten)]
91  pub impact: Impact,
92  /// 基础评分
93  pub base_score: f32,
94  /// [`SeverityType`] 基础评级
95  pub base_severity: SeverityType,
96}
97
98impl CVSS {
99  /// https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator
100  /// 7.1. Base Metrics Equations
101  /// The Base Score formula depends on sub-formulas for Impact Sub-Score (ISS), Impact, and Exploitability, all of which are defined below:
102  ///
103  /// | ISS = | 1 - \[ (1 - Confidentiality) × (1 - Integrity) × (1 - Availability) \] |
104  /// | --- | --- |
105  /// | Impact = |  |
106  /// | If Scope is Unchanged | 6.42 × ISS |
107  /// | If Scope is Changed | 7.52 × (ISS - 0.029) - 3.25 × (ISS - 0.02)15 |
108  /// | Exploitability = | 8.22 × AttackVector × AttackComplexity × |
109  /// |  | PrivilegesRequired × UserInteraction |
110  /// | BaseScore = |  |
111  /// | If Impact \\<= 0 | 0, _else_ |
112  /// | If Scope is Unchanged | Roundup (Minimum \[(Impact + Exploitability), 10\]) |
113  /// | If Scope is Changed | Roundup (Minimum \[1.08 × (Impact + Exploitability), 10\]) |[](#body)
114  ///
115  fn base_score(&self) -> f32 {
116    let exploit_ability_score = self.exploitability_score();
117    let impact_score_scope = self.impact_score();
118    // > BaseScore
119    // If (Impact sub score <= 0)     0 else,
120    // Scope Unchanged                 𝑅𝑜𝑢𝑛𝑑𝑢𝑝(𝑀𝑖𝑛𝑖𝑚𝑢𝑚[(𝐼𝑚𝑝𝑎𝑐𝑡 + 𝐸𝑥𝑝𝑙𝑜𝑖𝑡𝑎𝑏𝑖𝑙𝑖𝑡𝑦), 10])
121    if impact_score_scope <= 0.0 {
122      0.0
123    } else if !self.scope.is_changed() {
124      roundup((impact_score_scope + exploit_ability_score).min(10.0))
125    } else {
126      roundup((1.08 * (impact_score_scope + exploit_ability_score)).min(10.0))
127    }
128  }
129  pub fn exploitability_score(&self) -> f32 {
130    self.exploit_ability.score(self.scope.is_changed())
131  }
132  /// Scope Unchanged 6.42 × 𝐼𝑆𝐶Base
133  /// Scope Changed 7.52 × [𝐼𝑆𝐶𝐵𝑎𝑠𝑒 − 0.029] − 3.25 × [𝐼𝑆𝐶𝐵𝑎𝑠𝑒 − 0.02]15
134  pub fn impact_score(&self) -> f32 {
135    let impact_sub_score_base = self.impact.impact_sub_score_base();
136
137    if !self.scope.is_changed() {
138      self.scope.score() * impact_sub_score_base
139    } else {
140      (self.scope.score() * (impact_sub_score_base - 0.029).abs())
141        - (3.25 * (impact_sub_score_base - 0.02).abs().powf(15.0))
142    }
143  }
144  pub fn builder(
145    version: Version,
146    exploit_ability: ExploitAbility,
147    scope: ScopeType,
148    impact: Impact,
149  ) -> CVSSBuilder {
150    CVSSBuilder::new(version, exploit_ability, scope, impact)
151  }
152}
153
154impl Display for CVSS {
155  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
156    write!(
157      f,
158      "CVSS:{}/{}/{}/{}/{}/{}/{}/{}/{}",
159      self.version,
160      self.exploit_ability.attack_vector,
161      self.exploit_ability.attack_complexity,
162      self.exploit_ability.privileges_required,
163      self.exploit_ability.user_interaction,
164      self.scope,
165      self.impact.confidentiality_impact,
166      self.impact.integrity_impact,
167      self.impact.availability_impact
168    )
169  }
170}
171
172impl FromStr for CVSS {
173  type Err = CVSSError;
174  fn from_str(vector_string: &str) -> Result<Self> {
175    let (version, vectors) = match vector_string.split_once('/') {
176      None => {
177        return Err(CVSSError::InvalidPrefix {
178          value: vector_string.to_string(),
179        });
180      }
181      Some((v, vector)) => {
182        let version = Version::from_str(v).unwrap_or_default();
183        (version, vector)
184      }
185    };
186    if matches!(version, Version::None) {
187      return Err(CVSSError::InvalidCVSSVersion {
188        value: version.to_string(),
189        expected: "3.0 or 3.1".to_string(),
190      });
191    }
192    let mut vector = vectors.split('/');
193    // "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H"
194    let error = CVSSError::InvalidCVSS {
195      key: "CVSS:3.1".to_string(),
196      value: vector_string.to_string(),
197      expected: "".to_string(),
198    };
199    let exploit_ability = ExploitAbility {
200      attack_vector: AttackVectorType::from_str(vector.next().ok_or(&error)?)?,
201      attack_complexity: AttackComplexityType::from_str(vector.next().ok_or(&error)?)?,
202      privileges_required: PrivilegesRequiredType::from_str(vector.next().ok_or(&error)?)?,
203      user_interaction: UserInteractionType::from_str(vector.next().ok_or(&error)?)?,
204    };
205    let scope = ScopeType::from_str(vector.next().ok_or(&error)?)?;
206    let impact = Impact {
207      confidentiality_impact: ConfidentialityImpactType::from_str(vector.next().ok_or(&error)?)?,
208      integrity_impact: IntegrityImpactType::from_str(vector.next().ok_or(&error)?)?,
209      availability_impact: AvailabilityImpactType::from_str(vector.next().ok_or(&error)?)?,
210    };
211    let mut cvss = CVSS {
212      version,
213      vector_string: vector_string.to_string(),
214      exploit_ability,
215      scope,
216      impact,
217      base_score: 0.0,
218      base_severity: SeverityType::None,
219    };
220    cvss.base_score = cvss.base_score();
221    cvss.base_severity = SeverityType::from(cvss.base_score);
222    cvss.vector_string = cvss.to_string();
223    Ok(cvss)
224  }
225}
226
227pub struct CVSSBuilder {
228  /// Version 版本: 3.0 和 3.1
229  pub version: Version,
230  pub exploit_ability: ExploitAbility,
231  /// [`ScopeType`] 影响范围(S)
232  pub scope: ScopeType,
233  pub impact: Impact,
234}
235
236/// CVSS Builder
237impl CVSSBuilder {
238  pub fn new(
239    version: Version,
240    exploit_ability: ExploitAbility,
241    scope: ScopeType,
242    impact: Impact,
243  ) -> Self {
244    Self {
245      version,
246      exploit_ability,
247      scope,
248      impact,
249    }
250  }
251  pub fn build(self) -> CVSS {
252    let Self {
253      version,
254      exploit_ability,
255      scope,
256      impact,
257    } = self;
258    let mut cvss = CVSS {
259      version,
260      vector_string: "".to_string(),
261      exploit_ability,
262      scope,
263      impact,
264      base_score: 0.0,
265      base_severity: SeverityType::None,
266    };
267    cvss.vector_string = cvss.to_string();
268    cvss.base_score = cvss.base_score();
269    cvss.base_severity = SeverityType::from(cvss.base_score);
270    cvss
271  }
272}
273
274/// cvss v3
275///
276/// The CVSSv3 <https://www.first.org/cvss/specification-document> scoring data.
277#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
278#[serde(rename_all = "camelCase", deny_unknown_fields)]
279pub struct ImpactMetricV3 {
280  #[serde(default)]
281  pub source: Option<String>,
282  #[serde(default)]
283  pub r#type: MetricLevelType,
284  #[serde(alias = "cvssData")]
285  pub cvss_v3: CVSS,
286  /// 漏洞的可利用 评分
287  pub exploitability_score: f32,
288  /// 影响评分
289  pub impact_score: f32,
290}
291
292impl FromStr for ImpactMetricV3 {
293  type Err = CVSSError;
294
295  fn from_str(s: &str) -> Result<Self> {
296    match CVSS::from_str(s) {
297      Ok(c) => {
298        let exploit_ability_score = c.exploitability_score();
299        let impact_score = c.impact_score();
300        Ok(Self {
301          source: None,
302          r#type: Default::default(),
303          cvss_v3: c,
304          exploitability_score: exploit_ability_score,
305          impact_score,
306        })
307      }
308      Err(err) => Err(err),
309    }
310  }
311}
312
313/// Roundup保留小数点后一位,小数点后第二位大于零则进一。 例如, Roundup(4.02) = 4.1; 或者 Roundup(4.00) = 4.0
314///
315/// Where “Round up” is defined as the smallest number,
316/// specified to one decimal place, that is equal to or higher than its input. For example,
317/// Round up (4.02) is 4.1; and Round up (4.00) is 4.0.
318///
319/// 1.  `function Roundup (input):`
320/// 2.  `    int_input = round_to_nearest_integer (input * 100000)`
321/// 3.  `    if (int_input % 10000) == 0:`
322/// 4.  `        return int_input / 100000.0`
323/// 5.  `    else:`
324/// 6.  `        return (floor(int_input / 10000) + 1) / 10.0`
325fn roundup(input: f32) -> f32 {
326  let int_input = (input * 100_000.0) as u32;
327  if int_input % 10000 == 0 {
328    (int_input as f32) / 100_000.0
329  } else {
330    let score_floor = ((int_input as f32) / 10_000.0).floor();
331    (score_floor + 1.0) / 10.0
332  }
333}
334
335#[cfg(test)]
336mod tests {
337  use crate::v3::roundup;
338
339  #[test]
340  fn roundup_test() {
341    assert_eq!(roundup(4.00), 4.0);
342    assert_eq!(roundup(4.02), 4.1);
343    assert_eq!(roundup(0.8619848), 0.9);
344    assert_eq!(roundup(0.9006104), 1.0)
345  }
346}