use geekorm::prelude::*;
use super::SecuritySeverity;
#[derive(Data, Debug, Clone, Default, PartialEq)]
pub enum AdvisorySource {
#[geekorm(aliases = "alpine,alpinesecdb")]
AlpineSecDB,
#[geekorm(aliases = "amazon,aws,amazonwebservices")]
AmazonWebServices,
#[geekorm(aliases = "anchore")]
Anchore,
#[geekorm(aliases = "chainguard")]
Chainguard,
#[geekorm(aliases = "debian,debian-sec,debian-distro-debian-12")]
Debian,
#[geekorm(aliases = "github,ghad,githubadvisories")]
GitHubAdvisoryDatabase,
#[geekorm(aliases = "nvd,nationalvulnerabilitydatabase")]
NationalVulnerabilityDatabase,
#[geekorm(aliases = "oracle,oracleoval")]
OracleOval,
#[geekorm(aliases = "redhat,redhatsecurity")]
RedHatSecurity,
#[geekorm(aliases = "suse,suseoval")]
SuseOval,
#[geekorm(aliases = "ubuntu")]
UbuntuSecurity,
#[geekorm(aliases = "wolfi")]
WolfiSecDB,
#[geekorm(aliases = "custom")]
Custom,
#[default]
Unknown,
}
#[derive(Table, Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct Advisories {
#[geekorm(primary_key, auto_increment)]
pub id: PrimaryKey<i32>,
#[geekorm(unique)]
pub name: String,
pub source: AdvisorySource,
pub severity: SecuritySeverity,
#[geekorm(new = "chrono::Utc::now()")]
pub created_at: chrono::DateTime<chrono::Utc>,
#[geekorm(new = "chrono::Utc::now()")]
pub updated_at: chrono::DateTime<chrono::Utc>,
#[geekorm(skip)]
#[serde(skip)]
pub metadata: Vec<AdvisoriesMetadata>,
}
impl Advisories {
pub async fn fetch_metadata<'a, T>(&mut self, connection: &'a T) -> Result<(), geekorm::Error>
where
T: geekorm::GeekConnection<Connection = T> + 'a,
{
self.metadata = AdvisoriesMetadata::fetch_by_advisory_id(connection, self.id).await?;
Ok(())
}
pub fn has_metadata(&self, key: impl Into<String>) -> bool {
let key = key.into();
self.metadata.iter().any(|m| m.key == key)
}
pub async fn add_metadata<'a, T>(
&mut self,
connection: &'a T,
key: impl Into<String>,
value: impl Into<String>,
) -> Result<(), geekorm::Error>
where
T: GeekConnection<Connection = T> + 'a,
{
let key = key.into();
log::debug!("Adding advisory metadata `{}`", key);
let meta = match AdvisoriesMetadata::query_first(
connection,
AdvisoriesMetadata::query_select()
.where_eq("key", key.clone())
.and()
.where_eq("advisory_id", self.id)
.build()?,
)
.await
{
Ok(meta) => meta,
Err(_) => {
let mut meta = AdvisoriesMetadata::new(key, value.into(), self.id);
meta.save(connection).await?;
meta
}
};
self.metadata.push(meta);
Ok(())
}
pub async fn get_metadata<'a, T>(
&mut self,
connection: &'a T,
key: impl Into<String>,
) -> Result<Option<AdvisoriesMetadata>, geekorm::Error>
where
T: geekorm::GeekConnection<Connection = T> + 'a,
{
let key = key.into();
log::debug!("Getting advisory metadata `{}`", key);
let meta = self.metadata.iter().find(|m| m.key == key);
if let Some(meta) = meta {
return Ok(Some(meta.clone()));
}
let meta = if let Ok(m) = AdvisoriesMetadata::query_first(
connection,
AdvisoriesMetadata::query_select()
.where_eq("key", key)
.and()
.where_eq("advisory_id", self.id)
.build()?,
)
.await
{
m
} else {
return Ok(None);
};
self.metadata.push(meta.clone());
Ok(Some(meta))
}
}
#[derive(Table, Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct AdvisoriesMetadata {
#[geekorm(primary_key, auto_increment)]
pub id: PrimaryKey<i32>,
pub key: String,
pub value: String,
#[geekorm(foreign_key = "Advisories.id")]
pub advisory_id: ForeignKey<i32, Advisories>,
#[geekorm(new = "chrono::Utc::now()")]
pub updated_at: chrono::DateTime<chrono::Utc>,
}