Skip to content

Commit

Permalink
Add title and description to Vulnerability models and entities.
Browse files Browse the repository at this point in the history
Provide a handy method for getting a new db transaction.
CVE record importer uses transactions and sets title/description.
A few small renames of system->graph.
  • Loading branch information
Bob McWhirter authored and bobmcwhirter committed Mar 15, 2024
1 parent 3515611 commit 1e45efe
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 17 deletions.
2 changes: 2 additions & 0 deletions entity/src/vulnerability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub identifier: String,
pub title: Option<String>,
pub description_en: Option<String>,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
Expand Down
2 changes: 1 addition & 1 deletion graph/src/graph/advisory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ impl<'g> AdvisoryContext<'g> {

let entity = entity::advisory_vulnerability::ActiveModel {
advisory_id: Set(self.advisory.id),
vulnerability_id: Set(cve.cve.id),
vulnerability_id: Set(cve.vulnerability.id),
};

Ok((self, entity.insert(&self.graph.connection(tx)).await?).into())
Expand Down
9 changes: 8 additions & 1 deletion graph/src/graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use log::debug;
use migration::Migrator;
use postgresql_embedded;
use postgresql_embedded::PostgreSQL;
use sea_orm::{ConnectOptions, ConnectionTrait, Database, DatabaseConnection, DbErr, Statement};
use sea_orm::{
ConnectOptions, ConnectionTrait, Database, DatabaseConnection, DatabaseTransaction, DbErr,
Statement, TransactionTrait,
};
use sea_orm_migration::MigratorTrait;
use std::fmt::{Debug, Display, Formatter};
use std::sync::Arc;
Expand Down Expand Up @@ -59,6 +62,10 @@ impl Graph {
Self { db }
}

pub async fn transaction(&self) -> Result<DatabaseTransaction, error::Error> {
Ok(self.db.begin().await?)
}

pub(crate) fn connection<'db>(
&'db self,
tx: Transactional<'db>,
Expand Down
52 changes: 40 additions & 12 deletions graph/src/graph/vulnerability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
use crate::graph::advisory::AdvisoryContext;
use crate::graph::error::Error;
use crate::graph::Graph;
use sea_orm::ActiveValue::Set;
use sea_orm::ActiveValue::{Set, Unchanged};
use sea_orm::{
ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QuerySelect, RelationTrait,
ActiveModelTrait, ColumnTrait, EntityTrait, NotSet, QueryFilter, QuerySelect, RelationTrait,
};
use sea_query::JoinType;
use std::fmt::{Debug, Formatter};
Expand All @@ -25,6 +25,8 @@ impl Graph {
let entity = vulnerability::ActiveModel {
id: Default::default(),
identifier: Set(identifier.to_string()),
title: NotSet,
description_en: NotSet,
};

Ok((self, entity.insert(&self.connection(tx)).await?).into())
Expand All @@ -46,21 +48,21 @@ impl Graph {

#[derive(Clone)]
pub struct VulnerabilityContext {
pub(crate) system: Graph,
pub(crate) cve: vulnerability::Model,
pub(crate) graph: Graph,
pub(crate) vulnerability: vulnerability::Model,
}

impl Debug for VulnerabilityContext {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.cve.fmt(f)
self.vulnerability.fmt(f)
}
}

impl From<(&Graph, vulnerability::Model)> for VulnerabilityContext {
fn from((system, cve): (&Graph, Model)) -> Self {
Self {
system: system.clone(),
cve,
graph: system.clone(),
vulnerability: cve,
}
}
}
Expand All @@ -72,13 +74,39 @@ impl VulnerabilityContext {
JoinType::Join,
advisory_vulnerability::Relation::Advisory.def().rev(),
)
.filter(advisory_vulnerability::Column::VulnerabilityId.eq(self.cve.id))
.all(&self.system.connection(tx))
.filter(advisory_vulnerability::Column::VulnerabilityId.eq(self.vulnerability.id))
.all(&self.graph.connection(tx))
.await?
.drain(0..)
.map(|advisory| (&self.system, advisory).into())
.map(|advisory| (&self.graph, advisory).into())
.collect())
}

pub async fn set_title(
&self,
title: Option<String>,
tx: Transactional<'_>,
) -> Result<(), Error> {
let mut entity: vulnerability::ActiveModel = self.vulnerability.clone().into();
entity.title = Set(title);

entity.save(&self.graph.connection(tx)).await?;

Ok(())
}

pub async fn set_description_en(
&self,
description_en: Option<String>,
tx: Transactional<'_>,
) -> Result<(), Error> {
let mut entity: vulnerability::ActiveModel = self.vulnerability.clone().into();
entity.description_en = Set(description_en);

entity.save(&self.graph.connection(tx)).await?;

Ok(())
}
}

#[cfg(test)]
Expand All @@ -103,8 +131,8 @@ mod tests {
.ingest_vulnerability("CVE-456", Transactional::None)
.await?;

assert_eq!(cve1.cve.id, cve2.cve.id);
assert_ne!(cve1.cve.id, cve3.cve.id);
assert_eq!(cve1.vulnerability.id, cve2.vulnerability.id);
assert_ne!(cve1.vulnerability.id, cve3.vulnerability.id);

let not_found = system
.get_vulnerability("CVE-NOT_FOUND", Transactional::None)
Expand Down
1 change: 1 addition & 0 deletions ingestors/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ ring = "0.17.8"
hex = "0.4.3"
csaf = "0.5.0"
sha2 = "0.10.8"
sea-orm = "*"

[dev-dependencies]
test-log = { version = "0.2.15", features = ["env_logger", "trace"] }
Expand Down
1 change: 1 addition & 0 deletions ingestors/src/cve/cve_record/v5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ pub struct Containers {
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct CnaContainer {
pub title: Option<String>,
pub descriptions: Vec<Description>,
pub problem_types: Vec<ProblemType>,
}
Expand Down
26 changes: 23 additions & 3 deletions ingestors/src/cve/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,43 @@ impl<'g> CveLoader<'g> {
let mut reader = HashingRead::new(record);
let cve: CveRecord = serde_json::from_reader(&mut reader)?;

self.graph
let tx = self.graph.transaction().await?;

let vulnerability = self
.graph
.ingest_vulnerability(cve.cve_metadata.cve_id(), Transactional::None)
.await?;

let hashes = reader.hashes();
vulnerability
.set_title(cve.containers.cna.title.clone(), Transactional::Some(&tx))
.await?;

vulnerability
.set_description_en(
cve.containers
.cna
.descriptions
.iter()
.find(|e| e.lang == "en")
.map(|en| en.value.clone()),
Transactional::Some(&tx),
)
.await?;

let hashes = reader.hashes();
let sha256 = hex::encode(hashes.sha256.as_ref());

self.graph
.ingest_advisory(
cve.cve_metadata.cve_id(),
location,
sha256,
Transactional::None,
Transactional::Some(&tx),
)
.await?;

tx.commit().await?;

Ok(())
}
}
Expand Down
11 changes: 11 additions & 0 deletions ingestors/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
pub mod advisory;
pub mod cve;

pub mod hashing;

use sea_orm::error::DbErr;

#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Json(serde_json::Error),
#[error(transparent)]
Graph(trustify_graph::graph::error::Error),
#[error(transparent)]
Db(DbErr),
}

impl From<serde_json::Error> for Error {
Expand All @@ -21,3 +26,9 @@ impl From<trustify_graph::graph::error::Error> for Error {
Self::Graph(value)
}
}

impl From<DbErr> for Error {
fn from(value: DbErr) -> Self {
Self::Db(value)
}
}
4 changes: 4 additions & 0 deletions migration/src/m0000011_create_vulnerability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ impl MigrationTrait for Migration {
.string()
.not_null(),
)
.col(ColumnDef::new(Vulnerability::Title).string())
.col(ColumnDef::new(Vulnerability::DescriptionEn).string())
.to_owned(),
)
.await
Expand All @@ -50,4 +52,6 @@ pub enum Vulnerability {
Timestamp,
// --
Identifier,
Title,
DescriptionEn,
}

0 comments on commit 1e45efe

Please sign in to comment.