use crate::csaf::macros::skip_if_document_status_is_not::skip_if_document_status_is_not;
use crate::csaf::types::csaf_datetime::{CsafDateTime, ValidCsafDateTime};
use crate::csaf_traits::{CsafTrait, DistributionTrait, DocumentTrait, TlpTrait, TrackingTrait, VulnerabilityTrait};
use crate::schema::csaf2_1::schema::{DocumentStatus, LabelOfTlp};
use crate::validation::ValidationError;
fn create_disclosure_date_too_late_error(
doc_status: &DocumentStatus,
disclosure_date: &ValidCsafDateTime,
i_v: usize,
newest_revision_date: &ValidCsafDateTime,
) -> ValidationError {
ValidationError {
message: format!(
"Disclosure date ({disclosure_date}) for vulnerability at index {i_v} is newer than the newest revision date ({newest_revision_date}) on a document with TLP:CLEAR and document status {doc_status}"
),
instance_path: format!("/vulnerabilities/{i_v}/disclosure_date"),
}
}
pub fn test_6_1_45_inconsistent_disclosure_date(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 is_tlp_clear = match document.get_distribution_21() {
Ok(distribution) => match distribution.get_tlp_21() {
Ok(tlp) => tlp.get_label() == LabelOfTlp::Clear,
Err(_) => false,
},
Err(_) => false,
};
if !is_tlp_clear {
return Ok(());
}
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 (i_v, vulnerability) in doc.get_vulnerabilities().iter().enumerate() {
if let Some(disclosure_date) = vulnerability.get_disclosure_date() {
match disclosure_date {
CsafDateTime::Valid(valid_date) => {
if &valid_date > newest_revision_date {
errors
.get_or_insert_default()
.push(create_disclosure_date_too_late_error(
&status,
&valid_date,
i_v,
newest_revision_date,
));
}
},
CsafDateTime::Invalid(_) => {
},
}
}
}
errors.map_or(Ok(()), Err)
}
crate::test_validation::impl_validator!(
csaf2_1,
ValidatorForTest6_1_45,
test_6_1_45_inconsistent_disclosure_date
);
#[cfg(test)]
mod tests {
use super::*;
use crate::csaf2_1::testcases::TESTS_2_1;
use std::str::FromStr;
#[test]
fn test_test_6_1_45() {
let case_01_disclosure_date_too_late = Err(vec![create_disclosure_date_too_late_error(
&DocumentStatus::Final,
&ValidCsafDateTime::from_str("2024-02-24T10:00:00.000Z").unwrap(),
0,
&ValidCsafDateTime::from_str("2024-01-24T10:00:00.000Z").unwrap(),
)]);
let case_02_disclosure_date_too_late = Err(vec![create_disclosure_date_too_late_error(
&DocumentStatus::Final,
&ValidCsafDateTime::from_str("2025-02-26T10:00:00.000Z").unwrap(),
0,
&ValidCsafDateTime::from_str("2024-02-29T10:00:00.000Z").unwrap(),
)]);
let case_03_disclosure_date_too_late_with_timezone = Err(vec![create_disclosure_date_too_late_error(
&DocumentStatus::Final,
&ValidCsafDateTime::from_str("2024-01-24T09:00:00.000-06:00").unwrap(),
0,
&ValidCsafDateTime::from_str("2024-01-24T10:00:00.000Z").unwrap(),
)]);
TESTS_2_1.test_6_1_45.expect(
case_01_disclosure_date_too_late,
case_02_disclosure_date_too_late,
case_03_disclosure_date_too_late_with_timezone,
Ok(()),
Ok(()),
Ok(()),
Ok(()),
);
}
}