Skip to content

Commit

Permalink
feat: allow getting by ID
Browse files Browse the repository at this point in the history
  • Loading branch information
ctron authored and bobmcwhirter committed Sep 19, 2023
1 parent 3e4662c commit 9ef59da
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 53 deletions.
127 changes: 77 additions & 50 deletions v11y/api/src/db/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use actix_web::web;
use std::borrow::Cow;
use std::path::Path;
use std::str::FromStr;

use derive_more::{Display, Error, From};
use futures::Stream;
use futures::StreamExt;
use sqlx::sqlite::SqliteConnectOptions;
use sqlx::{Error, Row, SqlitePool};
use sqlx::{Error, QueryBuilder, Row, SqlitePool};

use v11y_client::{Reference, ScoreType, Severity, Vulnerability};

Expand All @@ -21,6 +23,40 @@ pub struct Db {
pool: SqlitePool,
}

pub enum GetBy<'a> {
Id(Cow<'a, str>),
Alias(Cow<'a, str>),
}

impl<'a> GetBy<'a> {
pub fn alias(alias: impl Into<Cow<'a, str>>) -> Self {
Self::Alias(alias.into())
}

#[allow(unused)]
pub fn id(id: impl Into<Cow<'a, str>>) -> Self {
Self::Id(id.into())
}
}

impl From<web::Path<String>> for GetBy<'static> {
fn from(value: web::Path<String>) -> Self {
Self::Id(value.into_inner().into())
}
}

impl From<String> for GetBy<'static> {
fn from(value: String) -> Self {
Self::Id(value.into())
}
}

impl<'a> From<&'a str> for GetBy<'a> {
fn from(value: &'a str) -> Self {
Self::Id(Cow::Borrowed(value))
}
}

#[allow(unused)]
impl Db {
pub async fn new(base: impl AsRef<Path>) -> Result<Self, anyhow::Error> {
Expand Down Expand Up @@ -221,11 +257,10 @@ impl Db {
Ok(())
}

pub async fn get(&self, id: &str, origin: Option<String>) -> Result<Vec<Vulnerability>, DbError> {
let query = match origin {
Some(origin) => {
sqlx::query(
r#"
pub async fn get(&self, id: impl Into<GetBy<'_>>, origin: Option<String>) -> Result<Vec<Vulnerability>, DbError> {
let id = id.into();
let mut builder = QueryBuilder::new(
r#"
select
vulnerabilities.id,
vulnerabilities.origin,
Expand Down Expand Up @@ -253,52 +288,35 @@ impl Db {
left join
severities on severities.vulnerability_id = vulnerabilities.id and severities.origin = vulnerabilities.origin
where
vulnerabilities.id = $1 and vulnerabilities.origin = $2
order by
vulnerabilities.origin
"#,
)
.bind(id)
.bind(origin)
"#,
);

match id {
GetBy::Id(id) => {
builder.push(" vulnerabilities.id = ");
builder.push_bind(id);
}
GetBy::Alias(alias) => {
builder.push(" aliases.alias = ");
builder.push_bind(alias);
}
}

None => {
sqlx::query(
r#"
select
vulnerabilities.id,
vulnerabilities.origin,
vulnerabilities.modified,
vulnerabilities.published,
vulnerabilities.withdrawn,
vulnerabilities.summary,
vulnerabilities.details,
aliases.alias,
related.related,
refs.type,
refs.url,
severities.type as score_type,
severities.source,
severities.score,
severities.additional
from
vulnerabilities
left join
aliases on aliases.vulnerability_id = vulnerabilities.id and aliases.origin = vulnerabilities.origin
left join
related on related.vulnerability_id = vulnerabilities.id and related.origin = vulnerabilities.origin
left join
refs on refs.vulnerability_id = vulnerabilities.id and refs.origin = vulnerabilities.origin
left join
severities on severities.vulnerability_id = vulnerabilities.id and severities.origin = vulnerabilities.origin
where
vulnerabilities.id = $1
if let Some(origin) = origin {
builder.push("and vulnerabilities.origin = ");
builder.push_bind(origin);
}

builder.push(
r#"
order by
vulnerabilities.origin
"#,
).bind(id)
}
};
vulnerabilities.origin"#,
);

let query = builder.build();

use sqlx::Execute;
println!("SQL: {}", query.sql());

let vulns = query
.fetch(&self.pool)
Expand Down Expand Up @@ -695,7 +713,7 @@ mod test {

use v11y_client::{Reference, ScoreType, Severity, Vulnerability};

use crate::db::Db;
use crate::db::{Db, GetBy};

#[tokio::test]
async fn create_db() -> Result<(), anyhow::Error> {
Expand Down Expand Up @@ -1057,11 +1075,20 @@ mod test {

db.ingest(&snyk_vuln).await?;

// fetch by ID

let result = db.get("CVE-123", None).await?;

assert!(result.contains(&osv_vuln));
assert!(result.contains(&snyk_vuln));

// we should only get the snyk one

let result = db.get(GetBy::alias("GHSA-foo-ghz"), None).await?;

assert!(result.contains(&snyk_vuln));
assert_eq!(result.len(), 1);

Ok(())
}
}
4 changes: 3 additions & 1 deletion v11y/api/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod vulnerability;
paths(
crate::server::vulnerability::ingest_vulnerability,
crate::server::vulnerability::get,
crate::server::vulnerability::get_by_alias,
),
components(
schemas(
Expand All @@ -45,7 +46,8 @@ pub fn config(
web::scope("/api/v1")
.wrap(new_auth!(auth))
.service(vulnerability::ingest_vulnerability)
.service(vulnerability::get),
.service(vulnerability::get)
.service(vulnerability::get_by_alias),
)
.service(swagger_ui_with_auth(ApiDoc::openapi(), swagger_ui_oidc));
}
Expand Down
20 changes: 18 additions & 2 deletions v11y/api/src/server/vulnerability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use trustification_auth::Permission;

use v11y_client::Vulnerability;

use crate::db::DbError;
use crate::db::{DbError, GetBy};
use crate::server::Error;
use crate::AppState;

Expand Down Expand Up @@ -42,7 +42,23 @@ pub(crate) async fn ingest_vulnerability(
)]
#[get("/vulnerability/{id}")]
pub(crate) async fn get(state: web::Data<AppState>, id: web::Path<String>) -> actix_web::Result<impl Responder> {
let vuln = state.db.get(&id, None).await?;
let vuln = state.db.get(id, None).await?;
Ok(HttpResponse::Ok().json(vuln))
}

/// Retrieve vulnerability information by alias
#[utoipa::path(
responses(
(status = 200, description = "Successfully retrieved"),
(status = BAD_REQUEST, description = "Missing valid alias"),
),
)]
#[get("/vulnerability/by-alias/{alias}")]
pub(crate) async fn get_by_alias(
state: web::Data<AppState>,
alias: web::Path<String>,
) -> actix_web::Result<impl Responder> {
let vuln = state.db.get(GetBy::alias(&*alias), None).await?;
Ok(HttpResponse::Ok().json(vuln))
}

Expand Down

0 comments on commit 9ef59da

Please sign in to comment.