Skip to content

Commit

Permalink
cvss
Browse files Browse the repository at this point in the history
  • Loading branch information
cn-kali-team committed Aug 7, 2023
1 parent 1511bf6 commit 13a06f7
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 50 deletions.
20 changes: 20 additions & 0 deletions cve/src/cve.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::node;
use cvss::error::{CVSSError, Result};
use serde::{Deserialize, Serialize};
use std::str::FromStr;

// 单个CVE信息
#[derive(Debug, Serialize, Deserialize, Clone)]
Expand Down Expand Up @@ -55,6 +57,24 @@ pub struct ImpactMetricV3 {
pub impact_score: f32,
}

impl FromStr for ImpactMetricV3 {
type Err = CVSSError;

fn from_str(s: &str) -> Result<Self> {
match cvss::v3::CVSS::from_str(s) {
Ok(c) => {
let exploitability_score = c.exploit_ability_score();
let impact_score = c.impact_score();
Ok(Self {
cvss_v3: c,
exploitability_score,
impact_score,
})
}
Err(err) => Err(err),
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Configurations {
// 版本
Expand Down
68 changes: 64 additions & 4 deletions cvss/src/v3.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::str::FromStr;
// https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator
use crate::error::{CVSSError, Result};
use crate::metric::Metric;
use crate::v3::attack_complexity::AttackComplexityType;
use crate::v3::attack_vector::AttackVectorType;
use crate::v3::impact_metrics::{
Expand All @@ -12,6 +11,7 @@ use crate::v3::severity::SeverityType;
use crate::v3::user_interaction::UserInteractionType;
use crate::version::Version;
use serde::{Deserialize, Serialize};
use std::str::FromStr;

pub mod attack_complexity;
pub mod attack_vector;
Expand Down Expand Up @@ -52,9 +52,69 @@ pub struct CVSS {

impl CVSS {
// https://nvd.nist.gov/vuln-metrics/cvss
fn update_severity(&self) {}
fn update_severity(&mut self) {
self.base_severity = SeverityType::from(self.base_score)
}
// https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator
fn update_score(&mut self) {
self.base_score = 0 as f32;
let exploit_ability_score = self.exploit_ability_score();
let impact_score_scope = self.impact_score();

// > BaseScore
// If (Impact sub score <= 0) 0 else,
// Scope Unchanged 𝑅𝑜𝑢𝑛𝑑𝑢𝑝(𝑀𝑖𝑛𝑖𝑚𝑢𝑚[(𝐼𝑚𝑝𝑎𝑐𝑡 + 𝐸𝑥𝑝𝑙𝑜𝑖𝑡𝑎𝑏𝑖𝑙𝑖𝑡𝑦), 10])
let base_score = if impact_score_scope < 0.0 {
0.0
} else if !self.scope.is_changed() {
self.roundup((impact_score_scope + exploit_ability_score).min(10.0))
} else {
self.roundup((1.08 * (impact_score_scope + exploit_ability_score)).min(10.0))
};
self.base_score = base_score;
}
// Roundup保留小数点后一位,小数点后第二位大于零则进一。 例如, Roundup(4.02) = 4.1; 或者 Roundup(4.00) = 4.0
/// Where “Round up” is defined as the smallest number,
/// specified to one decimal place, that is equal to or higher than its input. For example,
/// Round up (4.02) is 4.1; and Round up (4.00) is 4.0.
fn roundup(&self, base_score: f32) -> f32 {
let score_int = (base_score * 100_000.0) as u32;
if score_int % 10000 == 0 {
(score_int as f32) / 100_000.0
} else {
let score_floor = ((score_int as f32) / 10_000.0).floor();
(score_floor + 1.0) / 10.0
}
}
/// 8.22 × 𝐴𝑡𝑡𝑎𝑐𝑘𝑉𝑒𝑐𝑡𝑜𝑟 × 𝐴𝑡𝑡𝑎𝑐𝑘𝐶𝑜𝑚𝑝𝑙𝑒𝑥𝑖𝑡𝑦 × 𝑃𝑟𝑖𝑣𝑖𝑙𝑒𝑔𝑒𝑅𝑒𝑞𝑢𝑖𝑟𝑒𝑑 × 𝑈𝑠𝑒𝑟𝐼𝑛𝑡𝑒𝑟𝑎𝑐𝑡𝑖𝑜𝑛
pub fn exploit_ability_score(&self) -> f32 {
self.roundup(
8.22
* self.attack_vector.score()
* self.attack_complexity.score()
* self.user_interaction.score()
* self
.privileges_required
.scoped_score(self.scope.is_changed()),
)
}
/// 𝐼𝑆𝐶𝐵𝑎𝑠𝑒 = 1 − [(1 − 𝐼𝑚𝑝𝑎𝑐𝑡𝐶𝑜𝑛𝑓) × (1 − 𝐼𝑚𝑝𝑎𝑐𝑡𝐼𝑛𝑡𝑒𝑔) × (1 − 𝐼𝑚𝑝𝑎𝑐𝑡𝐴𝑣𝑎𝑖𝑙)]
fn impact_sub_score(&self) -> f32 {
let c_score = self.confidentiality_impact.score();
let i_score = self.confidentiality_impact.score();
let a_score = self.availability_impact.score();
1.0 - ((1.0 - c_score) * (1.0 - i_score) * (1.0 - a_score)).abs()
}
/// Scope Unchanged 6.42 × 𝐼𝑆𝐶Base
/// Scope Changed 7.52 × [𝐼𝑆𝐶𝐵𝑎𝑠𝑒 − 0.029] − 3.25 × [𝐼𝑆𝐶𝐵𝑎𝑠𝑒 − 0.02]15
pub fn impact_score(&self) -> f32 {
let impact_sub_score = self.impact_sub_score();
let impact_score = if !self.scope.is_changed() {
self.scope.score() * impact_sub_score
} else {
(self.scope.score() * (impact_sub_score - 0.029).abs())
- (3.25 * (impact_sub_score - 0.02).abs().powf(15.0))
};
self.roundup(impact_score)
}
}
impl FromStr for CVSS {
Expand Down
4 changes: 2 additions & 2 deletions cvss/src/v3/attack_complexity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ impl Metric for AttackComplexityType {

fn score(&self) -> f32 {
match self {
AttackComplexityType::High => 0.72,
AttackComplexityType::Low => 0.44,
AttackComplexityType::High => 0.44,
AttackComplexityType::Low => 0.77,
}
}

Expand Down
4 changes: 2 additions & 2 deletions cvss/src/v3/attack_vector.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::fmt::{Display, Formatter};
use crate::error::{CVSSError, Result};
use crate::metric::Metric;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use crate::metric::Metric;

// AV
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
Expand Down
12 changes: 6 additions & 6 deletions cvss/src/v3/impact_metrics.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
use std::fmt::{Display, Formatter};
use crate::error::{CVSSError, Result};
use crate::metric::Metric;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use crate::metric::Metric;

// CIA 影响指标 原json schema为ciaType
// 机密性影响
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum ConfidentialityImpactType {
High,
Low,
None,
}
// 完整性影响
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum IntegrityImpactType {
High,
Low,
None,
}
// 可用性影响
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum AvailabilityImpactType {
Expand Down Expand Up @@ -124,7 +125,6 @@ impl FromStr for AvailabilityImpactType {
}
}


impl Display for ConfidentialityImpactType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", Self::NAME, self.as_str())
Expand Down Expand Up @@ -201,4 +201,4 @@ impl Metric for AvailabilityImpactType {
AvailabilityImpactType::High => "H",
}
}
}
}
26 changes: 11 additions & 15 deletions cvss/src/v3/privileges_required.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,23 +65,19 @@ impl FromStr for PrivilegesRequiredType {
}

impl PrivilegesRequiredType {
fn scoped_score(&self, scope_change: bool) -> f32 {
match self {
PrivilegesRequiredType::High => {
if scope_change {
0.50
} else {
0.27
}
pub fn scoped_score(&self, scope_change: bool) -> f32 {
if scope_change {
match self {
PrivilegesRequiredType::High => 0.5,
PrivilegesRequiredType::Low => 0.68,
PrivilegesRequiredType::None => 0.85,
}
PrivilegesRequiredType::Low => {
if scope_change {
0.68
} else {
0.62
}
} else {
match self {
PrivilegesRequiredType::High => 0.27,
PrivilegesRequiredType::Low => 0.62,
PrivilegesRequiredType::None => 0.85,
}
PrivilegesRequiredType::None => 0.85,
}
}
}
21 changes: 20 additions & 1 deletion cvss/src/v3/scope.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::error::{CVSSError, Result};
use crate::metric::Metric;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
Expand Down Expand Up @@ -27,7 +28,25 @@ impl ScopeType {
}
impl Display for ScopeType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "S:{}", self.as_str())
write!(f, "{}:{}", Self::NAME, self.as_str())
}
}

impl Metric for ScopeType {
const NAME: &'static str = "S";

fn score(&self) -> f32 {
match self {
ScopeType::Unchanged => 6.42,
ScopeType::Changed => 7.52,
}
}

fn as_str(&self) -> &'static str {
match self {
ScopeType::Unchanged => "U",
ScopeType::Changed => "C",
}
}
}
impl FromStr for ScopeType {
Expand Down
55 changes: 41 additions & 14 deletions cvss/src/v3/severity.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::error::{CVSSError, Result};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::str::FromStr;

// 严重性
Expand All @@ -18,26 +19,52 @@ pub enum SeverityType {
Critical,
}

impl Display for SeverityType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "S:{}", self.as_str())
}
}

impl SeverityType {
fn as_str(&self) -> &'static str {
match self {
SeverityType::None => "None",
SeverityType::Low => "Low",
SeverityType::Medium => "Medium",
SeverityType::High => "High",
SeverityType::Critical => "Critical",
}
}
}

impl From<f32> for SeverityType {
fn from(value: f32) -> Self {
if value < 0.1 {
SeverityType::None
} else if value < 4.0 {
SeverityType::Low
} else if value < 7.0 {
SeverityType::Medium
} else if value < 9.0 {
SeverityType::High
} else {
SeverityType::Critical
}
}
}

impl FromStr for SeverityType {
type Err = CVSSError;

fn from_str(s: &str) -> Result<Self> {
let c = {
let c = s.to_uppercase().chars().next();
c.ok_or(CVSSError::InvalidCVSS {
value: s.to_string(),
scope: "SeverityType from_str".to_string(),
})?
};
match c {
'N' => Ok(Self::None),
'L' => Ok(Self::Low),
'M' => Ok(Self::Medium),
'H' => Ok(Self::High),
'C' => Ok(Self::Critical),
match s {
"None" => Ok(Self::None),
"Low" => Ok(Self::Low),
"Medium" => Ok(Self::Medium),
"High" => Ok(Self::High),
"Critical" => Ok(Self::Critical),
_ => Err(CVSSError::InvalidCVSS {
value: c.to_string(),
value: s.to_string(),
scope: "SeverityType".to_string(),
}),
}
Expand Down
4 changes: 2 additions & 2 deletions cvss/src/v3/user_interaction.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::fmt::{Display, Formatter};
use crate::error::{CVSSError, Result};
use crate::metric::Metric;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use crate::metric::Metric;

// UI
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
Expand Down
3 changes: 1 addition & 2 deletions examples/cpe-example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ fn main() {
let gz_decoder = flate2::read::GzDecoder::new(gz_open_file);
let file = BufReader::new(gz_decoder);
let c: CPEList = quick_xml::de::from_reader(file).unwrap();
for cpe_item in c.cpe_item {
if let Some(cpe_item) = c.cpe_item.into_iter().next() {
println!("{:#?}", &cpe_item);
break;
}
}
3 changes: 1 addition & 2 deletions examples/cve-example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ fn main() {
let file = BufReader::new(gz_decoder);
let c: CVEContainer = serde_json::from_reader(file).unwrap();
println!("{:#?}", c.CVE_data_timestamp);
for cve_item in c.CVE_Items {
if let Some(cve_item) = c.CVE_Items.into_iter().next() {
println!("{:#?}", &cve_item);
println!("{}", serde_json::to_string_pretty(&cve_item).unwrap());
break;
}
}

0 comments on commit 13a06f7

Please sign in to comment.