use crate::parsing::{OptionalParser, Parser};
use crate::v3::base::BaseVector;
use crate::v3::environmental::EnvironmentalVector;
use crate::v3::temporal::TemporalVector;
use serde::{Deserialize, Serialize};
use std::fmt::{Error, Formatter};
pub mod base;
pub mod environmental;
pub mod roundup;
pub mod temporal;
#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
pub struct CVSS3Vector {
pub base: BaseVector,
pub temporal: Option<TemporalVector>,
pub environmental: Option<EnvironmentalVector>,
}
impl CVSS3Vector {
pub fn parse_strict(cvss_string: &str) -> Result<Self, Vec<&'static str>>
where
Self: Sized,
{
let mut split = Box::new(cvss_string.split('/'));
let mut parsers = (None, None, None);
let mut errors = Vec::new();
let res = BaseVector::parse_strict(&mut split);
if res.is_ok() {
parsers.0 = res.ok();
} else {
errors.append(&mut res.unwrap_err());
}
parsers.1 = TemporalVector::parse_optional(&split);
parsers.2 = EnvironmentalVector::parse_optional(&split);
if parsers.0.is_some() {
Ok(CVSS3Vector {
base: parsers.0.unwrap(),
temporal: parsers.1,
environmental: parsers.2,
})
} else {
Err(errors)
}
}
pub fn parse_nonstrict(cvss_string: &str) -> Result<Self, Vec<&'static str>>
where
Self: Sized,
{
let split = Box::new(cvss_string.split('/'));
let mut parsers = (None, None, None);
let mut errors = Vec::new();
let res = BaseVector::parse_nonstrict(&split);
if res.is_ok() {
parsers.0 = res.ok();
} else {
errors.append(&mut res.unwrap_err());
}
parsers.1 = TemporalVector::parse_optional(&split);
parsers.2 = EnvironmentalVector::parse_optional(&split);
if parsers.0.is_some() {
Ok(CVSS3Vector {
base: parsers.0.unwrap(),
temporal: parsers.1,
environmental: parsers.2,
})
} else {
Err(errors)
}
}
pub fn score(&self) -> f64 {
match self.environmental {
Some(environmental_vector) => environmental_vector.score(self.base, self.temporal),
None => match self.temporal {
Some(temporal_vector) => temporal_vector.score(self.base.score()),
None => self.base.score(),
},
}
}
}
impl std::fmt::Display for CVSS3Vector {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
let mut fields = Vec::new();
fields.push(format!("{}", self.base));
match self.temporal {
None => (),
Some(field) => fields.push(format!("{}", field)),
};
match self.environmental {
None => (),
Some(field) => fields.push(format!("{}", field)),
};
write!(f, "{}", fields.join("/"))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::v3::base::exploitability::{
AttackComplexity, AttackVector, Exploitability, PrivilegesRequired, UserInteraction,
};
use crate::v3::base::impact::{Availability, Confidentiality, Impact, Integrity};
use crate::v3::base::scope::Scope;
use crate::v3::environmental::modified_base::modified_exploitability::{
ModifiedAttackComplexity, ModifiedAttackVector, ModifiedExploitability,
};
use crate::v3::environmental::modified_base::ModifiedBaseVector;
use crate::v3::environmental::security_requirements::{
AvailabilityRequirement, ConfidentialityRequirement, IntegrityRequirement,
SecurityRequirements,
};
use crate::v3::temporal::{ExploitCodeMaturity, RemediationLevel, ReportConfidence};
#[test]
fn test_formatting() {
assert_eq!(
"CVSS:3.0/AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H/E:F/RL:O/RC:C/CR:H/IR:H/AR:M/MAV:N/MAC:L",
format!("{}", provide_cvss_vector())
);
}
#[test]
fn test_parsing() {
assert_eq!(
Ok(provide_cvss_vector()),
CVSS3Vector::parse_strict(
"CVSS:3.0/AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H/E:F/RL:O/RC:C/CR:H/IR:H/AR:M/MAV:N/MAC:L"),
);
assert_eq!(
Ok(provide_cvss_vector()),
CVSS3Vector::parse_nonstrict(
"CVSS:3.0/AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H/E:F/RL:O/RC:C/CR:H/IR:H/AR:M/MAV:N/MAC:L"),
);
assert!(CVSS3Vector::parse_strict(
"CVSS:3.0/AC:H/AV:A/PR:N/UI:N/S:U/C:H/I:H/A:H/E:F/RL:O/RC:C/CR:H/IR:H/AR:M/MAV:N/MAC:L"
)
.is_err());
assert_eq!(
Ok(provide_cvss_vector()),
CVSS3Vector::parse_nonstrict(
"CVSS:3.0/AC:H/AV:A/PR:N/UI:N/S:U/C:H/I:H/A:H/E:F/RL:O/RC:C/CR:H/IR:H/AR:M/MAV:N/MAC:L"
),
);
assert!(CVSS3Vector::parse_strict(
"CVSS:3.0/AC:H/PR:N/UI:N/S:U/C:H/I:H/AV:A/A:H/E:F/RL:O/RC:C/CR:H/IR:H/AR:M/MAV:N/MAC:L"
)
.is_err());
assert_eq!(
Ok(provide_cvss_vector()),
CVSS3Vector::parse_nonstrict(
"CVSS:3.0/AC:H/PR:N/UI:N/S:U/C:H/I:H/AV:A/A:H/E:F/RL:O/RC:C/CR:H/IR:H/AR:M/MAV:N/MAC:L"
),
);
assert!(CVSS3Vector::parse_strict(
"AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H/E:F/RL:O/RC:C/CR:H/IR:H/AR:M/MAV:N/MAC:L"
)
.is_err());
assert_eq!(
Ok(provide_cvss_vector()),
CVSS3Vector::parse_nonstrict(
"AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H/E:F/RL:O/RC:C/CR:H/IR:H/AR:M/MAV:N/MAC:L"
),
);
assert!(CVSS3Vector::parse_strict("fsjfskhf").is_err());
assert!(CVSS3Vector::parse_nonstrict("fsjfskhf").is_err());
assert!(CVSS3Vector::parse_strict("fs//jf/skhf").is_err());
assert!(CVSS3Vector::parse_nonstrict("fs//jf/skhf").is_err());
}
#[test]
fn test_scoring() {
assert_eq!(9.1, provide_cvss_vector().score());
}
fn provide_cvss_vector() -> CVSS3Vector {
CVSS3Vector {
base: provide_base_vector(),
temporal: Some(provide_temporal_vector()),
environmental: Some(provide_environmental_vector()),
}
}
fn provide_base_vector() -> BaseVector {
BaseVector {
exploitability: Exploitability {
attack_vector: AttackVector::Adjacent,
attack_complexity: AttackComplexity::High,
privileges_required: PrivilegesRequired::None,
user_interaction: UserInteraction::None,
},
scope: Scope::Unchanged,
impact: Impact {
confidentiality: Confidentiality::High,
integrity: Integrity::High,
availability: Availability::High,
},
}
}
fn provide_temporal_vector() -> TemporalVector {
TemporalVector {
exploit_code_maturity: Some(ExploitCodeMaturity::Functional),
remediation_level: Some(RemediationLevel::OfficialFix),
report_confidence: Some(ReportConfidence::Confirmed),
}
}
fn provide_environmental_vector() -> EnvironmentalVector {
let modified_base_vector = ModifiedBaseVector {
modified_exploitability: Some(ModifiedExploitability {
modified_attack_vector: Some(ModifiedAttackVector::Network),
modified_attack_complexity: Some(ModifiedAttackComplexity::Low),
modified_privileges_required: None,
modified_user_interaction: None,
}),
modified_scope: None,
modified_impact: None,
};
let security_requirements = SecurityRequirements {
confidentiality_requirement: Some(ConfidentialityRequirement::High),
integrity_requirement: Some(IntegrityRequirement::High),
availability_requirement: Some(AvailabilityRequirement::Medium),
};
EnvironmentalVector {
security_requirements: Some(security_requirements),
modified_base: Some(modified_base_vector),
}
}
}