From 8ebaece8d4f1b06db615ab56758466c874d301d3 Mon Sep 17 00:00:00 2001 From: JimFuller-RedHat Date: Tue, 28 Jan 2025 16:43:20 +0100 Subject: [PATCH] normalise Contains relationships --- entity/src/relationship.rs | 28 +++++++++ migration/src/lib.rs | 2 + .../src/m0000860_normalise_relationships.rs | 57 +++++++++++++++++++ modules/analysis/src/service/mod.rs | 10 ++-- modules/ingestor/src/graph/sbom/cyclonedx.rs | 25 +++----- modules/ingestor/src/graph/sbom/spdx.rs | 36 ++++++------ 6 files changed, 119 insertions(+), 39 deletions(-) create mode 100644 migration/src/m0000860_normalise_relationships.rs diff --git a/entity/src/relationship.rs b/entity/src/relationship.rs index 669221cb7..4cf9a2fa8 100644 --- a/entity/src/relationship.rs +++ b/entity/src/relationship.rs @@ -50,6 +50,34 @@ pub enum Relationship { #[sea_orm(num_value = 14)] PackageOf, #[sea_orm(num_value = 15)] + Contains, + #[sea_orm(num_value = 16)] + Dependency, + #[sea_orm(num_value = 17)] + DevDependency, + #[sea_orm(num_value = 18)] + OptionalDependency, + #[sea_orm(num_value = 19)] + ProvidedDependency, + #[sea_orm(num_value = 20)] + TestDependency, + #[sea_orm(num_value = 21)] + RuntimeDependency, + #[sea_orm(num_value = 22)] + Example, + #[sea_orm(num_value = 23)] + Generates, + #[sea_orm(num_value = 24)] + Variant, + #[sea_orm(num_value = 25)] + BuildTool, + #[sea_orm(num_value = 26)] + DevTool, + #[sea_orm(num_value = 27)] + Describes, + #[sea_orm(num_value = 28)] + Packages, + #[sea_orm(num_value = 29)] Undefined, } diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 1ddb7a8c6..43ad17027 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -102,6 +102,7 @@ mod m0000820_create_conversation; mod m0000830_perf_indexes; mod m0000840_add_relationship_14_15; mod m0000850_python_version; +mod m0000860_normalise_relationships; pub struct Migrator; @@ -211,6 +212,7 @@ impl MigratorTrait for Migrator { Box::new(m0000830_perf_indexes::Migration), Box::new(m0000840_add_relationship_14_15::Migration), Box::new(m0000850_python_version::Migration), + Box::new(m0000860_normalise_relationships::Migration), ] } } diff --git a/migration/src/m0000860_normalise_relationships.rs b/migration/src/m0000860_normalise_relationships.rs new file mode 100644 index 000000000..e132346c3 --- /dev/null +++ b/migration/src/m0000860_normalise_relationships.rs @@ -0,0 +1,57 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; +const DATA: [(i32, &str); 14] = [ + (16, "Contains"), + (17, "Dependency"), + (18, "DevDependency"), + (19, "OptionalDependency"), + (20, "ProvidedDependency"), + (21, "TestDependency"), + (22, "RuntimeDependency"), + (23, "Example"), + (24, "Generates"), + (25, "Variant"), + (26, "BuildTool"), + (27, "DevTool"), + (28, "Describes"), + (29, "Packages"), +]; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + for (id, description) in DATA { + let insert = Query::insert() + .into_table(Relationship::Table) + .columns([Relationship::Id, Relationship::Description]) + .values_panic([id.into(), description.into()]) + .to_owned(); + + manager.exec_stmt(insert).await?; + } + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + for (id, _) in DATA { + let insert = Query::delete() + .from_table(Relationship::Table) + .and_where(Expr::col(Relationship::Id).lt(id)) + .to_owned(); + + manager.exec_stmt(insert).await?; + } + + Ok(()) + } +} + +#[derive(DeriveIden)] +pub enum Relationship { + Table, + Id, + Description, +} diff --git a/modules/analysis/src/service/mod.rs b/modules/analysis/src/service/mod.rs index ee9081527..1b0d0389f 100644 --- a/modules/analysis/src/service/mod.rs +++ b/modules/analysis/src/service/mod.rs @@ -56,11 +56,11 @@ pub fn dep_nodes( return; } visited.insert(node); - for neighbor in graph.neighbors_directed(node, Direction::Incoming) { + for neighbor in graph.neighbors_directed(node, Direction::Outgoing) { if let Some(dep_packagenode) = graph.node_weight(neighbor).cloned() { // Attempt to find the edge and get the relationship in a more elegant way if let Some(relationship) = graph - .find_edge(neighbor, node) + .find_edge(node, neighbor) .and_then(|edge_index| graph.edge_weight(edge_index)) { let dep_node = DepNode { @@ -102,10 +102,10 @@ pub fn ancestor_nodes( while let Some(node) = stack.pop() { if discovered.visit(node) { - for succ in graph.neighbors_directed(node, Direction::Outgoing) { + for succ in graph.neighbors_directed(node, Direction::Incoming) { if !discovered.is_visited(&succ) { if let Some(anc_packagenode) = graph.node_weight(succ).cloned() { - if let Some(edge) = graph.find_edge(node, succ) { + if let Some(edge) = graph.find_edge(succ, node) { if let Some(relationship) = graph.edge_weight(edge) { let anc_node = AncNode { sbom_id: anc_packagenode.sbom_id, @@ -133,7 +133,7 @@ pub fn ancestor_nodes( } } } - if graph.neighbors_directed(node, Direction::Outgoing).count() == 0 { + if graph.neighbors_directed(node, Direction::Incoming).count() == 0 { continue; // we are at the root } } diff --git a/modules/ingestor/src/graph/sbom/cyclonedx.rs b/modules/ingestor/src/graph/sbom/cyclonedx.rs index 7959626db..f6c66e6f7 100644 --- a/modules/ingestor/src/graph/sbom/cyclonedx.rs +++ b/modules/ingestor/src/graph/sbom/cyclonedx.rs @@ -9,7 +9,7 @@ use crate::graph::{ }; use sea_orm::ConnectionTrait; use serde_cyclonedx::cyclonedx::v_1_6::{ - Component, ComponentEvidenceIdentity, CycloneDx, LicenseChoiceUrl, RefLinkType, + Component, ComponentEvidenceIdentity, CycloneDx, LicenseChoiceUrl, }; use std::str::FromStr; use time::{format_description::well_known::Iso8601, OffsetDateTime}; @@ -137,9 +137,9 @@ impl SbomContext { // create a relationship creator.relate( - bom_ref, - Relationship::DescribedBy, CYCLONEDX_DOC_REF.to_string(), + Relationship::Describes, + bom_ref, ); } } @@ -151,12 +151,16 @@ impl SbomContext { // create relationships for left in sbom.dependencies.iter().flatten() { - creator.relate_all(&left.ref_, Relationship::DependencyOf, &left.depends_on); + for target in left.depends_on.iter().flatten() { + creator.relate(left.ref_.clone(), Relationship::Dependency, target.clone()); + } // https://github.com/trustification/trustify/issues/1131 // Do we need to qualify this so that only "arch=src" refs // get the GeneratedFrom relationship? - creator.relate_all(&left.ref_, Relationship::GeneratedFrom, &left.provides); + for target in left.depends_on.iter().flatten() { + creator.relate(left.ref_.clone(), Relationship::Generates, target.clone()); + } } // create @@ -208,17 +212,6 @@ impl<'a> Creator<'a> { self.relations.push((left, rel, right)); } - pub fn relate_all( - &mut self, - source: &RefLinkType, - rel: Relationship, - targets: &Option>, - ) { - for target in targets.iter().flatten() { - self.relate(target.clone(), rel, source.clone()); - } - } - pub async fn create(self, db: &impl ConnectionTrait) -> anyhow::Result<()> { let mut purls = PurlCreator::new(); let mut cpes = CpeCreator::new(); diff --git a/modules/ingestor/src/graph/sbom/spdx.rs b/modules/ingestor/src/graph/sbom/spdx.rs index 12a86780f..eb8d96797 100644 --- a/modules/ingestor/src/graph/sbom/spdx.rs +++ b/modules/ingestor/src/graph/sbom/spdx.rs @@ -251,31 +251,31 @@ impl<'spdx> TryFrom<(&'spdx str, &'spdx RelationshipType, &'spdx str)> for SpdxR ) -> Result { match rel { RelationshipType::AncestorOf => Ok((left, Relationship::AncestorOf, right)), - RelationshipType::BuildToolOf => Ok((left, Relationship::BuildToolOf, right)), - RelationshipType::ContainedBy => Ok((left, Relationship::ContainedBy, right)), - RelationshipType::Contains => Ok((right, Relationship::ContainedBy, left)), - RelationshipType::DependencyOf => Ok((left, Relationship::DependencyOf, right)), - RelationshipType::DependsOn => Ok((right, Relationship::DependencyOf, left)), + RelationshipType::BuildToolOf => Ok((right, Relationship::BuildTool, left)), + RelationshipType::ContainedBy => Ok((right, Relationship::Contains, left)), + RelationshipType::Contains => Ok((left, Relationship::Contains, right)), + RelationshipType::DependencyOf => Ok((right, Relationship::Dependency, left)), + RelationshipType::DependsOn => Ok((left, Relationship::Dependency, right)), RelationshipType::DescendantOf => Ok((right, Relationship::AncestorOf, left)), - RelationshipType::DescribedBy => Ok((left, Relationship::DescribedBy, right)), - RelationshipType::Describes => Ok((right, Relationship::DescribedBy, left)), - RelationshipType::DevDependencyOf => Ok((left, Relationship::DevDependencyOf, right)), - RelationshipType::DevToolOf => Ok((left, Relationship::DevToolOf, right)), - RelationshipType::ExampleOf => Ok((left, Relationship::ExampleOf, right)), - RelationshipType::GeneratedFrom => Ok((left, Relationship::GeneratedFrom, right)), - RelationshipType::Generates => Ok((right, Relationship::GeneratedFrom, left)), + RelationshipType::DescribedBy => Ok((right, Relationship::Describes, left)), + RelationshipType::Describes => Ok((left, Relationship::Describes, right)), + RelationshipType::DevDependencyOf => Ok((right, Relationship::DevDependency, left)), + RelationshipType::DevToolOf => Ok((right, Relationship::DevTool, left)), + RelationshipType::ExampleOf => Ok((right, Relationship::Example, left)), + RelationshipType::GeneratedFrom => Ok((right, Relationship::Generates, left)), + RelationshipType::Generates => Ok((left, Relationship::Generates, right)), RelationshipType::OptionalDependencyOf => { - Ok((left, Relationship::OptionalDependencyOf, right)) + Ok((right, Relationship::OptionalDependency, left)) } - RelationshipType::PackageOf => Ok((right, Relationship::PackageOf, left)), + RelationshipType::PackageOf => Ok((right, Relationship::Packages, left)), RelationshipType::ProvidedDependencyOf => { - Ok((left, Relationship::ProvidedDependencyOf, right)) + Ok((right, Relationship::ProvidedDependency, left)) } RelationshipType::RuntimeDependencyOf => { - Ok((left, Relationship::RuntimeDependencyOf, right)) + Ok((right, Relationship::RuntimeDependency, left)) } - RelationshipType::TestDependencyOf => Ok((left, Relationship::TestDependencyOf, right)), - RelationshipType::VariantOf => Ok((left, Relationship::VariantOf, right)), + RelationshipType::TestDependencyOf => Ok((right, Relationship::TestDependency, left)), + RelationshipType::VariantOf => Ok((right, Relationship::Variant, left)), _ => Err(()), } .map(|(left, rel, right)| Self(left, rel, right))