diff --git a/Cargo.lock b/Cargo.lock index 848a05842..8a5c92ad1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8651,6 +8651,7 @@ dependencies = [ "trustify-common", "trustify-cvss", "trustify-entity", + "trustify-module-analysis", "trustify-module-ingestor", "trustify-module-storage", "trustify-test-context", diff --git a/modules/fundamental/Cargo.toml b/modules/fundamental/Cargo.toml index b5a7deb73..ba42588a4 100644 --- a/modules/fundamental/Cargo.toml +++ b/modules/fundamental/Cargo.toml @@ -10,6 +10,7 @@ trustify-auth = { workspace = true } trustify-common = { workspace = true } trustify-cvss = { workspace = true } trustify-entity = { workspace = true } +trustify-module-analysis = { workspace = true } trustify-module-ingestor = { workspace = true } trustify-module-storage = { workspace = true } diff --git a/modules/fundamental/src/sbom/model/details.rs b/modules/fundamental/src/sbom/model/details.rs index 311530ec0..bd7214e55 100644 --- a/modules/fundamental/src/sbom/model/details.rs +++ b/modules/fundamental/src/sbom/model/details.rs @@ -13,6 +13,8 @@ use sea_orm::{ }; use sea_query::{Asterisk, Expr, Func, SimpleExpr}; use serde::{Deserialize, Serialize}; +use trustify_module_analysis::service::AnalysisService; +use uuid::Uuid; use std::collections::{hash_map::Entry, HashMap}; use trustify_common::{ cpe::CpeCompare, @@ -20,7 +22,7 @@ use trustify_common::{ multi_model::{FromQueryResultMultiModel, SelectIntoMultiModel}, ConnectionOrTransaction, VersionMatches, }, - memo::Memo, + memo::Memo, model::Paginated, }; use trustify_entity::{ advisory, base_purl, product, product_status, product_version, purl_status, @@ -42,7 +44,8 @@ impl SbomDetails { /// turn an (sbom, sbom_node) row into an [`SbomDetails`], if possible pub async fn from_entity( (sbom, node): (sbom::Model, Option), - service: &SbomService, + sbom_service: &SbomService, + graph_service: &AnalysisService, tx: &ConnectionOrTransaction<'_>, ) -> Result, Error> { let relevant_advisory_info = sbom @@ -109,15 +112,16 @@ impl SbomDetails { .all(tx) .await?; - let summary = SbomSummary::from_entity((sbom, node), service, tx).await?; + let summary = SbomSummary::from_entity((sbom, node), sbom_service, tx).await?; Ok(match summary { Some(summary) => Some(SbomDetails { summary: summary.clone(), advisories: SbomAdvisory::from_models( - &summary.clone().described_by, + summary.clone(), &relevant_advisory_info, &product_advisory_statuses, + graph_service, tx, ) .await?, @@ -136,11 +140,13 @@ pub struct SbomAdvisory { impl SbomAdvisory { pub async fn from_models( - described_by: &[SbomPackage], + summary: SbomSummary, statuses: &[QueryCatcher], product_statuses: &[ProductStatusCatcher], + graph_service: &AnalysisService, tx: &ConnectionOrTransaction<'_>, ) -> Result, Error> { + let described_by = summary.described_by; let mut advisories = HashMap::new(); let sbom_cpes = described_by @@ -248,6 +254,37 @@ impl SbomAdvisory { let mut packages = vec![]; if let Some(package) = &product.product_status.package { + // We need to enable search on namespace/name pattern as well + // Also, from the brief look at it, the search is case sensitive + // which doesn't cover all use cases + let parts = package.split('/').collect::>(); + + let name = if parts.len() == 1 {parts[0]} else {parts[1]}; + + log::debug!("graph {:?} {:?}", parts, name ); + + + let res = graph_service.retrieve_deps_by_name(name.to_string(), Paginated::default(), ()).await; + // it would be better to have an API that would do this directly + // fn retrieve_sbom_deps_by_name(sbom_id, name); + let purls = match res { + Ok(page) => { + page.items + .into_iter() + .filter_map(|dep| { + Uuid::parse_str(&dep.sbom_id) + .ok() + .filter(|uuid| *uuid == summary.head.id) + .map(|_| dep.purl) + }) + .collect() + }, + Err(_) => {vec![]} + }; + + log::debug!("purls {:?}", purls); + // We need SBOM package as a result, + // so at least id is needed, if not the whole struct let package = SbomPackage { name: package.to_string(), ..Default::default() diff --git a/modules/fundamental/src/sbom/service/sbom.rs b/modules/fundamental/src/sbom/service/sbom.rs index ea7b88f1b..540e1e940 100644 --- a/modules/fundamental/src/sbom/service/sbom.rs +++ b/modules/fundamental/src/sbom/service/sbom.rs @@ -15,6 +15,7 @@ use sea_orm::{ use sea_query::{extension::postgres::PgExpr, Expr, Func, JoinType, SimpleExpr}; use serde::Deserialize; use serde_json::Value; +use trustify_module_analysis::service::AnalysisService; use std::{collections::HashMap, fmt::Debug}; use tracing::instrument; @@ -67,8 +68,10 @@ impl SbomService { tx: TX, ) -> Result, Error> { + // Is it OK to use graph service like this, or is it a better way to reuse data? + let graph_service = AnalysisService::new(self.db.clone()); Ok(match self.fetch_sbom(id, &tx).await? { - Some(row) => SbomDetails::from_entity(row, self, &self.db.connection(&tx)).await?, + Some(row) => SbomDetails::from_entity(row, self, &graph_service, &self.db.connection(&tx)).await?, None => None, }) }