From 1362307e8335ca6f759b5016db0af452f3e27f8f Mon Sep 17 00:00:00 2001 From: Bob McWhirter Date: Mon, 18 Mar 2024 14:17:07 -0400 Subject: [PATCH] Improve relationship between Advisory and Vulnerability. Ingest OSV more fully to track affected/fixed details. --- graph/src/graph/advisory/csaf/mod.rs | 2 +- graph/src/graph/advisory/mod.rs | 16 +-- graph/src/graph/package/mod.rs | 18 ++-- graph/src/graph/package/package_version.rs | 4 +- graph/src/graph/package/qualified_package.rs | 2 +- graph/src/graph/sbom/mod.rs | 2 +- .../mod.rs} | 4 +- ingestors/src/advisory/osv/loader.rs | 100 +++++++++++++++++- ingestors/src/advisory/osv/schema.rs | 2 +- ingestors/src/cve/loader.rs | 2 +- 10 files changed, 124 insertions(+), 28 deletions(-) rename graph/src/graph/{vulnerability.rs => vulnerability/mod.rs} (97%) diff --git a/graph/src/graph/advisory/csaf/mod.rs b/graph/src/graph/advisory/csaf/mod.rs index ba0a947f6..2e6e0f7db 100644 --- a/graph/src/graph/advisory/csaf/mod.rs +++ b/graph/src/graph/advisory/csaf/mod.rs @@ -32,7 +32,7 @@ impl<'g> AdvisoryContext<'g> { //let v = graph.ingest_vulnerability(id).await?; let advisory_vulnerability = advisory - .ingest_vulnerability(id, Transactional::None) + .link_to_vulnerability(id, Transactional::None) .await?; if let Some(ps) = &vuln.product_status { diff --git a/graph/src/graph/advisory/mod.rs b/graph/src/graph/advisory/mod.rs index f6b815074..6805f7614 100644 --- a/graph/src/graph/advisory/mod.rs +++ b/graph/src/graph/advisory/mod.rs @@ -117,7 +117,7 @@ impl<'g> AdvisoryContext<'g> { .map(|vuln| (self, vuln).into())) } - pub async fn ingest_vulnerability>( + pub async fn link_to_vulnerability>( &self, identifier: &str, tx: TX, @@ -471,7 +471,7 @@ mod test { .await?; let advisory_vulnerability = advisory - .ingest_vulnerability("CVE-8675309", Transactional::None) + .link_to_vulnerability("CVE-8675309", Transactional::None) .await?; let affected1 = advisory_vulnerability @@ -528,7 +528,7 @@ mod test { .await?; let advisory_vulnerability = advisory - .ingest_vulnerability("CVE-1234567", Transactional::None) + .link_to_vulnerability("CVE-1234567", Transactional::None) .await?; let affected = advisory_vulnerability @@ -588,13 +588,13 @@ mod test { .await?; advisory - .ingest_vulnerability("CVE-123", Transactional::None) + .link_to_vulnerability("CVE-123", Transactional::None) .await?; advisory - .ingest_vulnerability("CVE-123", Transactional::None) + .link_to_vulnerability("CVE-123", Transactional::None) .await?; advisory - .ingest_vulnerability("CVE-456", Transactional::None) + .link_to_vulnerability("CVE-456", Transactional::None) .await?; Ok(()) @@ -615,7 +615,7 @@ mod test { .await?; let advisory_vulnerability = advisory - .ingest_vulnerability("CVE-42", Transactional::None) + .link_to_vulnerability("CVE-42", Transactional::None) .await?; advisory_vulnerability @@ -656,7 +656,7 @@ mod test { .await?; let advisory_vulnerability = advisory - .ingest_vulnerability("INTERAL-77", Transactional::None) + .link_to_vulnerability("INTERAL-77", Transactional::None) .await?; advisory_vulnerability diff --git a/graph/src/graph/package/mod.rs b/graph/src/graph/package/mod.rs index ab35dc8f1..05ff926ea 100644 --- a/graph/src/graph/package/mod.rs +++ b/graph/src/graph/package/mod.rs @@ -952,7 +952,7 @@ mod tests { .await?; let redhat_advisory_vulnerability = redhat_advisory - .ingest_vulnerability("CVE-77", Transactional::None) + .link_to_vulnerability("CVE-77", Transactional::None) .await?; redhat_advisory_vulnerability @@ -978,7 +978,7 @@ mod tests { .await?; let ghsa_advisory_vulnerability = ghsa_advisory - .ingest_vulnerability("CVE-77", Transactional::None) + .link_to_vulnerability("CVE-77", Transactional::None) .await?; ghsa_advisory_vulnerability @@ -1045,7 +1045,7 @@ mod tests { .await?; let redhat_advisory_vulnerability = redhat_advisory - .ingest_vulnerability("CVE-77", Transactional::None) + .link_to_vulnerability("CVE-77", Transactional::None) .await?; redhat_advisory_vulnerability @@ -1060,7 +1060,7 @@ mod tests { .await?; let ghsa_advisory_vulnerability = ghsa_advisory - .ingest_vulnerability("CVE-77", Transactional::None) + .link_to_vulnerability("CVE-77", Transactional::None) .await?; ghsa_advisory_vulnerability @@ -1100,7 +1100,7 @@ mod tests { .await?; let redhat_advisory_vulnerability = redhat_advisory - .ingest_vulnerability("CVE-87", Transactional::None) + .link_to_vulnerability("CVE-87", Transactional::None) .await?; redhat_advisory_vulnerability @@ -1124,7 +1124,7 @@ mod tests { .await?; let ghsa_advisory_vulnerability = ghsa_advisory - .ingest_vulnerability("CVE-87", Transactional::None) + .link_to_vulnerability("CVE-87", Transactional::None) .await?; ghsa_advisory_vulnerability @@ -1164,7 +1164,7 @@ mod tests { .await?; let redhat_advisory_vulnerability = redhat_advisory - .ingest_vulnerability("CVE-99", Transactional::None) + .link_to_vulnerability("CVE-99", Transactional::None) .await?; redhat_advisory_vulnerability @@ -1181,7 +1181,7 @@ mod tests { .await?; let ghsa_advisory_vulnerability = ghsa_advisory - .ingest_vulnerability("CVE-99", Transactional::None) + .link_to_vulnerability("CVE-99", Transactional::None) .await?; ghsa_advisory_vulnerability @@ -1201,7 +1201,7 @@ mod tests { .await?; let unrelated_advisory_vulnerability = redhat_advisory - .ingest_vulnerability("CVE-99", Transactional::None) + .link_to_vulnerability("CVE-99", Transactional::None) .await?; unrelated_advisory_vulnerability diff --git a/graph/src/graph/package/package_version.rs b/graph/src/graph/package/package_version.rs index e81abe948..d56479290 100644 --- a/graph/src/graph/package/package_version.rs +++ b/graph/src/graph/package/package_version.rs @@ -228,7 +228,7 @@ mod tests { .await?; let redhat_advisory_vulnerability = redhat_advisory - .ingest_vulnerability("CVE-1", Transactional::None) + .link_to_vulnerability("CVE-1", Transactional::None) .await?; redhat_advisory_vulnerability @@ -243,7 +243,7 @@ mod tests { .await?; let ghsa_advisory_vulnerability = ghsa_advisory - .ingest_vulnerability("CVE-1", Transactional::None) + .link_to_vulnerability("CVE-1", Transactional::None) .await?; ghsa_advisory_vulnerability diff --git a/graph/src/graph/package/qualified_package.rs b/graph/src/graph/package/qualified_package.rs index fc91e2a35..7016ad84d 100644 --- a/graph/src/graph/package/qualified_package.rs +++ b/graph/src/graph/package/qualified_package.rs @@ -152,7 +152,7 @@ mod tests { .await?; let advisory_vulnerability = advisory - .ingest_vulnerability("CVE-2", Transactional::None) + .link_to_vulnerability("CVE-2", Transactional::None) .await?; let affected_core = advisory_vulnerability diff --git a/graph/src/graph/sbom/mod.rs b/graph/src/graph/sbom/mod.rs index b5a66453e..43136a591 100644 --- a/graph/src/graph/sbom/mod.rs +++ b/graph/src/graph/sbom/mod.rs @@ -993,7 +993,7 @@ mod tests { .await?; let advisory_vulnerability = advisory - .ingest_vulnerability("CVE-00000001", Transactional::None) + .link_to_vulnerability("CVE-00000001", Transactional::None) .await?; advisory_vulnerability diff --git a/graph/src/graph/vulnerability.rs b/graph/src/graph/vulnerability/mod.rs similarity index 97% rename from graph/src/graph/vulnerability.rs rename to graph/src/graph/vulnerability/mod.rs index c019737e1..2882cb8c1 100644 --- a/graph/src/graph/vulnerability.rs +++ b/graph/src/graph/vulnerability/mod.rs @@ -186,11 +186,11 @@ mod tests { .await?; advisory1 - .ingest_vulnerability("CVE-8675309", Transactional::None) + .link_to_vulnerability("CVE-8675309", Transactional::None) .await?; advisory2 - .ingest_vulnerability("CVE-8675309", Transactional::None) + .link_to_vulnerability("CVE-8675309", Transactional::None) .await?; let cve = system diff --git a/ingestors/src/advisory/osv/loader.rs b/ingestors/src/advisory/osv/loader.rs index 364b96d3c..2caa2a1fd 100644 --- a/ingestors/src/advisory/osv/loader.rs +++ b/ingestors/src/advisory/osv/loader.rs @@ -1,8 +1,10 @@ use std::io::Read; +use std::str::FromStr; +use trustify_common::purl::Purl; use trustify_graph::graph::Graph; -use crate::advisory::osv::schema::Vulnerability; +use crate::advisory::osv::schema::{Event, Package, Vulnerability}; use crate::hashing::HashingRead; use crate::Error; @@ -41,7 +43,43 @@ impl<'g> OsvLoader<'g> { .await?; for cve_id in cve_ids { - advisory.ingest_vulnerability(cve_id, &tx).await?; + let advisory_vuln = advisory.link_to_vulnerability(cve_id, &tx).await?; + + for affected in &osv.affected { + if let Some(package) = &affected.package { + match package { + Package::Named { .. } => { + todo!() + } + Package::Purl { purl } => { + if let Ok(purl) = Purl::from_str(&purl) { + for range in affected.ranges.iter().flatten() { + let parsed_range = events_to_range(&range.events); + if let (Some(start), Some(end)) = &parsed_range { + advisory_vuln + .ingest_affected_package_range( + purl.clone(), + &start, + &end, + &tx, + ) + .await?; + } + + if let (_, Some(fixed)) = &parsed_range { + let mut fixed_purl = purl.clone(); + fixed_purl.version = Some(fixed.clone()); + + advisory_vuln + .ingest_fixed_package_version(fixed_purl, &tx) + .await?; + } + } + } + } + } + } + } } } @@ -51,6 +89,26 @@ impl<'g> OsvLoader<'g> { } } +fn events_to_range(events: &Vec) -> (Option, Option) { + let start = events.iter().find_map(|e| { + if let Event::Introduced(version) = e { + Some(version.clone()) + } else { + None + } + }); + + let end = events.iter().find_map(|e| { + if let Event::Fixed(version) = e { + Some(version.clone()) + } else { + None + } + }); + + (start, end) +} + #[cfg(test)] mod test { use std::fs::File; @@ -58,6 +116,7 @@ mod test { use std::str::FromStr; use test_log::test; + use trustify_common::advisory::Assertion; use trustify_common::db::{Database, Transactional}; use trustify_graph::graph::Graph; @@ -111,6 +170,43 @@ mod test { assert!(loaded_advisory.is_some()); + let loaded_advisory = loaded_advisory.unwrap(); + + let affected_assertions = loaded_advisory.affected_assertions(()).await?; + + assert_eq!(1, affected_assertions.assertions.len()); + + let affected_assertion = affected_assertions.assertions.get("pkg://cargo/hyper"); + assert!(affected_assertion.is_some()); + + let affected_assertion = &affected_assertion.unwrap()[0]; + + assert!( + matches!( affected_assertion, Assertion::Affected {vulnerability,start_version,end_version} + if start_version == "0.0.0-0" + && end_version == "0.14.10" + && vulnerability == "CVE-2021-32714" + ) + ); + + let fixed_assertions = loaded_advisory.fixed_assertions(()).await?; + + assert_eq!(1, fixed_assertions.assertions.len()); + + let fixed_assertion = fixed_assertions.assertions.get("pkg://cargo/hyper"); + assert!(fixed_assertion.is_some()); + + let fixed_assertion = fixed_assertion.unwrap(); + assert_eq!(1, fixed_assertion.len()); + + let fixed_assertion = &fixed_assertion[0]; + + assert!( + matches!( fixed_assertion, Assertion::Fixed{vulnerability ,version } + if version == "0.14.10" + && vulnerability == "CVE-2021-32714" + ) + ); Ok(()) } } diff --git a/ingestors/src/advisory/osv/schema.rs b/ingestors/src/advisory/osv/schema.rs index 299c5d2a7..39778c85c 100644 --- a/ingestors/src/advisory/osv/schema.rs +++ b/ingestors/src/advisory/osv/schema.rs @@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum Package { - Named { name: String, ecosystem: Ecosystem }, Purl { purl: String }, + Named { name: String, ecosystem: Ecosystem }, } /// A commit is a full SHA1 Git hash in hex format. diff --git a/ingestors/src/cve/loader.rs b/ingestors/src/cve/loader.rs index 2291ac50c..b796408b9 100644 --- a/ingestors/src/cve/loader.rs +++ b/ingestors/src/cve/loader.rs @@ -58,7 +58,7 @@ impl<'g> CveLoader<'g> { // Link the advisory to the backing vulnerability advisory - .ingest_vulnerability(cve.cve_metadata.cve_id(), &tx) + .link_to_vulnerability(cve.cve_metadata.cve_id(), &tx) .await?; tx.commit().await?;