use crate::csaf::macros::skip_if_document_status_is_not::skip_if_document_status_is_not;
use crate::csaf::types::csaf_datetime::CsafDateTime::Valid;
use crate::csaf::types::csaf_datetime::{CsafDateTime, ValidCsafDateTime};
use crate::csaf_traits::{
CsafTrait, DocumentTrait, FirstKnownExploitationDatesTrait, TrackingTrait, VulnerabilityTrait, WithDate,
};
use crate::schema::csaf2_1::schema::DocumentStatus;
use crate::validation::ValidationError;
use std::fmt;
enum DateProperty {
Date,
ExploitationDate,
}
impl fmt::Display for DateProperty {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DateProperty::Date => write!(f, "date"),
DateProperty::ExploitationDate => write!(f, "exploitation_date"),
}
}
}
fn create_date_too_new_error(
doc_status: &DocumentStatus,
date: &ValidCsafDateTime,
newest_revision_date: &ValidCsafDateTime,
v_i: usize,
f_i: usize,
property: DateProperty,
) -> ValidationError {
ValidationError {
message: format!(
"The {property} '{date}' of the first known exploitation date is newer than the newest revision date ({newest_revision_date}) on a document with status {doc_status}"
),
instance_path: format!("/vulnerabilities/{v_i}/first_known_exploitation_dates/{f_i}/{property}"),
}
}
pub fn test_6_1_52_inconsistent_first_known_exploitation_dates(
doc: &impl CsafTrait,
) -> Result<(), Vec<ValidationError>> {
let document = doc.get_document();
let tracking = document.get_tracking();
let status = tracking.get_status();
skip_if_document_status_is_not!(status, Final, Interim);
let mut revision_history = tracking.aggregate_revision_history();
revision_history.inplace_sort_by_date_then_number();
let newest_revision_date = match revision_history.last() {
Some(rev) => match &rev.date {
CsafDateTime::Valid(date) => date,
CsafDateTime::Invalid(_) => return Ok(()), },
None => return Ok(()), };
let mut errors: Option<Vec<ValidationError>> = None;
for (v_i, vulnerability) in doc.get_vulnerabilities().iter().enumerate() {
if let Some(first_known_exploitation_dates) = vulnerability.get_first_known_exploitation_dates() {
for (f_i, first_known_exploitation_date) in first_known_exploitation_dates.iter().enumerate() {
if let Valid(valid_first_known_explot_date) = first_known_exploitation_date.get_date() {
if &valid_first_known_explot_date > newest_revision_date {
errors.get_or_insert_default().push(create_date_too_new_error(
&status,
&valid_first_known_explot_date,
newest_revision_date,
v_i,
f_i,
DateProperty::Date,
));
}
} else {
}
if let Valid(valid_exploitation_date) = first_known_exploitation_date.get_exploitation_date() {
if &valid_exploitation_date > newest_revision_date {
errors.get_or_insert_default().push(create_date_too_new_error(
&status,
&valid_exploitation_date,
newest_revision_date,
v_i,
f_i,
DateProperty::ExploitationDate,
));
}
} else {
}
}
}
}
errors.map_or(Ok(()), Err)
}
crate::test_validation::impl_validator!(
csaf2_1,
ValidatorForTest6_1_52,
test_6_1_52_inconsistent_first_known_exploitation_dates
);
#[cfg(test)]
mod tests {
use super::*;
use crate::csaf2_1::testcases::TESTS_2_1;
use std::str::FromStr;
#[test]
fn test_test_6_1_52() {
let case_01_newest_revision_date = ValidCsafDateTime::from_str("2024-01-24T10:00:00.000Z").unwrap();
let case_01_date_and_exploit_date_after_newest_rev_date = Err(vec![
create_date_too_new_error(
&DocumentStatus::Final,
&ValidCsafDateTime::from_str("2024-01-24T13:00:00.000Z").unwrap(),
&case_01_newest_revision_date,
0,
0,
DateProperty::Date,
),
create_date_too_new_error(
&DocumentStatus::Final,
&ValidCsafDateTime::from_str("2024-01-24T12:34:56.789Z").unwrap(),
&case_01_newest_revision_date,
0,
0,
DateProperty::ExploitationDate,
),
]);
let case_02_newest_revision_date = ValidCsafDateTime::from_str("2024-03-26T09:59:59.999998-07:00").unwrap();
let case_02_multiple_vulns_multiple_first_exploit_dates_also_timezones = Err(vec![
create_date_too_new_error(
&DocumentStatus::Final,
&ValidCsafDateTime::from_str("2024-03-27T10:00:00.000+17:00").unwrap(),
&case_02_newest_revision_date,
1,
0,
DateProperty::Date,
),
create_date_too_new_error(
&DocumentStatus::Final,
&ValidCsafDateTime::from_str("2024-03-25T23:00:00.000-18:00").unwrap(),
&case_02_newest_revision_date,
1,
0,
DateProperty::ExploitationDate,
),
create_date_too_new_error(
&DocumentStatus::Final,
&ValidCsafDateTime::from_str("2024-03-25T23:29:59.999999-17:30").unwrap(),
&case_02_newest_revision_date,
1,
1,
DateProperty::Date,
),
create_date_too_new_error(
&DocumentStatus::Final,
&ValidCsafDateTime::from_str("2024-03-27T07:59:59.999999+15:00").unwrap(),
&case_02_newest_revision_date,
1,
1,
DateProperty::ExploitationDate,
),
]);
TESTS_2_1.test_6_1_52.expect(
case_01_date_and_exploit_date_after_newest_rev_date,
case_02_multiple_vulns_multiple_first_exploit_dates_also_timezones,
Ok(()),
Ok(()),
);
}
}