use crate::csaf::types::csaf_vuln_metric::CsafVulnerabilityMetric;
use crate::schema::csaf2_0::schema::Score;
use crate::schema::csaf2_1::schema::{Content, Epss, QualitativeSeverityRating};
use serde::de::Error as SerdeError;
use serde_json::{Map, Value};
use ssvc::selection_list::SelectionList;
fn get_cvss_version(cvss: &Map<String, Value>) -> Option<String> {
cvss.get("version").and_then(|v| v.as_str()).map(|v| v.to_string())
}
pub trait ContentTrait {
fn get_cvss_metric_types(&self) -> Vec<CsafVulnerabilityMetric> {
let mut types: Vec<CsafVulnerabilityMetric> = Vec::new();
if let Some(version) = self.get_cvss_v2().and_then(get_cvss_version) {
types.push(CsafVulnerabilityMetric::CvssV2(version));
}
if let Some(version) = self.get_cvss_v3().and_then(get_cvss_version) {
types.push(CsafVulnerabilityMetric::CvssV3(version));
}
if let Some(version) = self.get_cvss_v4().and_then(get_cvss_version) {
types.push(CsafVulnerabilityMetric::CvssV4(version));
}
types
}
fn get_vulnerability_metric_types(&self) -> Vec<CsafVulnerabilityMetric> {
let mut types: Vec<CsafVulnerabilityMetric> = self.get_cvss_metric_types();
if self.has_ssvc_v2() {
types.push(CsafVulnerabilityMetric::SsvcV2);
}
if self.has_epss() {
types.push(CsafVulnerabilityMetric::Epss);
}
if self.has_qualitative_severity() {
types.push(CsafVulnerabilityMetric::QualitativeSeverityRating);
}
types
}
fn has_ssvc_v2(&self) -> bool;
fn get_ssvc_v2(&self) -> Result<SelectionList, serde_json::Error>;
fn get_ssvc_v2_raw(&self) -> Option<&Map<String, Value>>;
fn get_cvss_v2(&self) -> Option<&Map<String, Value>>;
fn has_cvss_v2(&self) -> bool {
self.get_cvss_v2().is_some()
}
fn get_cvss_v3(&self) -> Option<&Map<String, Value>>;
fn has_cvss_v3(&self) -> bool {
self.get_cvss_v3().is_some()
}
fn get_cvss_v4(&self) -> Option<&Map<String, Value>>;
fn has_cvss_v4(&self) -> bool {
self.get_cvss_v4().is_some()
}
fn has_any_cvss(&self) -> bool {
self.has_cvss_v2() || self.has_cvss_v3() || self.has_cvss_v4()
}
fn get_epss(&self) -> Option<&Epss>;
fn has_epss(&self) -> bool {
self.get_epss().is_some()
}
fn get_qualitative_severity(&self) -> Option<&QualitativeSeverityRating>;
fn has_qualitative_severity(&self) -> bool {
self.get_qualitative_severity().is_some()
}
fn get_content_json_path(&self, vulnerability_idx: usize, metric_idx: usize) -> String;
}
impl ContentTrait for Score {
fn has_ssvc_v2(&self) -> bool {
false
}
fn get_ssvc_v2(&self) -> Result<SelectionList, serde_json::Error> {
Err(SerdeError::custom("SSVC metrics are not implemented in CSAF 2.0"))
}
fn get_ssvc_v2_raw(&self) -> Option<&Map<String, Value>> {
None
}
fn get_cvss_v2(&self) -> Option<&Map<String, Value>> {
if self.cvss_v2.is_empty() {
None
} else {
Some(&self.cvss_v2)
}
}
fn get_cvss_v3(&self) -> Option<&Map<String, Value>> {
if self.cvss_v3.is_empty() {
None
} else {
Some(&self.cvss_v3)
}
}
fn get_cvss_v4(&self) -> Option<&Map<String, Value>> {
None
}
fn get_epss(&self) -> Option<&Epss> {
None
}
fn get_qualitative_severity(&self) -> Option<&QualitativeSeverityRating> {
None
}
fn get_content_json_path(&self, vulnerability_idx: usize, metric_idx: usize) -> String {
format!("/vulnerabilities/{vulnerability_idx}/scores/{metric_idx}")
}
}
impl ContentTrait for Content {
fn has_ssvc_v2(&self) -> bool {
!self.ssvc_v2.is_empty()
}
fn get_ssvc_v2(&self) -> Result<SelectionList, serde_json::Error> {
serde_json::from_value::<SelectionList>(Value::Object(self.ssvc_v2.clone()))
}
fn get_ssvc_v2_raw(&self) -> Option<&Map<String, Value>> {
if self.ssvc_v2.is_empty() {
None
} else {
Some(&self.ssvc_v2)
}
}
fn get_cvss_v2(&self) -> Option<&Map<String, Value>> {
if self.cvss_v2.is_empty() {
None
} else {
Some(&self.cvss_v2)
}
}
fn get_cvss_v3(&self) -> Option<&Map<String, Value>> {
if self.cvss_v3.is_empty() {
None
} else {
Some(&self.cvss_v3)
}
}
fn get_cvss_v4(&self) -> Option<&Map<String, Value>> {
if self.cvss_v4.is_empty() {
None
} else {
Some(&self.cvss_v4)
}
}
fn get_epss(&self) -> Option<&Epss> {
self.epss.as_ref()
}
fn get_qualitative_severity(&self) -> Option<&QualitativeSeverityRating> {
self.qualitative_severity_rating.as_ref()
}
fn get_content_json_path(&self, vulnerability_idx: usize, metric_idx: usize) -> String {
format!("/vulnerabilities/{vulnerability_idx}/metrics/{metric_idx}/content",)
}
}