diff --git a/Cargo.toml b/Cargo.toml index 74abc47..6c7dbc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ regex = "1.11.0" reqwest = { version = "0.12.8", features = ["json"] } serde = { version = "1.0.210", features = ["derive"] } serde_json = "1.0.132" +tera = "1.20.0" tokio = { version = "1.40.0", features = [ "macros", "process", diff --git a/Dockerfile b/Dockerfile index 63fc3b0..824b8d5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,12 @@ FROM rust:1.82.0-slim AS runtime WORKDIR /app +RUN apt-get update && apt-get install -y ca-certificates && update-ca-certificates + +COPY src/router/templates/ /app/templates/ +ENV TEMPLATE_DIR=/app/templates + +COPY configuration.yaml /app/configuration.yaml COPY --from=builder /app/target/release/vuln-aggregator vuln-aggregator COPY --from=builder /app/zli zli diff --git a/configuration.yaml b/configuration.yaml index aa55283..156e429 100644 --- a/configuration.yaml +++ b/configuration.yaml @@ -1,5 +1,5 @@ server: - port: 8000 + port: 3000 host: 0.0.0.0 logger: level: "info" diff --git a/src/alt_registry_images/fetching_images/image.rs b/src/alt_registry_images/fetching_images/image.rs index 0133023..babf072 100644 --- a/src/alt_registry_images/fetching_images/image.rs +++ b/src/alt_registry_images/fetching_images/image.rs @@ -1,128 +1,117 @@ -const API_ALT_IMAGES: &str = "https://registry.altlinux.org/v2"; - -use once_cell::sync::Lazy; -use regex::Regex; -use serde::{ - Deserialize, - Serialize, -}; - -use crate::alt_registry_images::branch::Branch; - -static VERSION_REGEX: Lazy = - Lazy::new(|| VersionRegex::new().unwrap_or_else(|err| panic!("{}", err))); - -struct VersionRegex(Regex); - -impl VersionRegex { - fn new() -> Result { - Regex::new(r"^(v\d+\.\d+\.\d+(-\d+|-[\w]+)?|(\d+\.\d+(\.\d+)?)(-\d+)?|\d+\.\d+)$") - .map(VersionRegex) - .map_err(|err| format!("regex build error: {}", err)) - } -} - -pub(crate) async fn fetch_branch_images( - branch: &Branch, -) -> Result, String> { - let mut images = Vec::new(); - - for image_name in branch_images_names(branch).await? { - for tag in image_tags(&image_name).await? { - if !VERSION_REGEX.0.is_match(&tag) && tag != branch.as_str() { - continue; - } - - images.push(Image { - name: image_name.clone(), - tag, - }) - } - } - - Ok(images) -} - -pub(super) async fn fetch_all_images() -> Result, String> { - let branches = [Branch::Sisyphus, Branch::P11, Branch::P10]; - - let mut images = Vec::new(); - - for branch in branches { - images.extend(fetch_branch_images(&branch).await?) - } - - Ok(images) -} - -async fn image_tags(image_name: &str) -> Result, String> { - let res = - reqwest::get(format!("{}/{}/tags/list", API_ALT_IMAGES, image_name)) - .await - .map_err(|err| format!("request execution error: {}", err))? - .json::() - .await - .map_err(|err| format!("TagList parsing error: {}", err))?; - - Ok(res.tags) -} - -async fn branch_images_names(branch: &Branch) -> Result, String> { - let mut images = Vec::new(); - - for image in images_names().await? { - if image.contains(branch.as_str()) - || branch_is_in_the_tags(&image, branch).await? - { - images.push(image.to_string()); - } - } - - Ok(images) -} - -async fn images_names() -> Result, String> { - let res = reqwest::get(format!("{}/_catalog", API_ALT_IMAGES)) - .await - .map_err(|err| format!("request execution error: {}", err))? - .json::() - .await - .map_err(|err| format!("image parsing error: {}", err))?; - - Ok(res.repositories) -} - -async fn branch_is_in_the_tags( - image: &str, - branch: &Branch, -) -> Result { - Ok(image_tags(image) - .await? - .contains(&branch.as_str().to_owned())) -} - -#[derive(Deserialize, Serialize)] -struct Images { - repositories: Vec, -} - -#[derive(Clone)] -pub(crate) struct Image { - name: String, - tag: String, -} - -impl Image { - pub(crate) fn name_ref(&self) -> &str { - &self.name - } - - pub(crate) fn tag_ref(&self) -> &str { - &self.tag - } -} - -#[derive(Deserialize, Serialize)] -struct TagsList { - tags: Vec, -} +const API_ALT_IMAGES: &str = "https://registry.altlinux.org/v2"; + +use once_cell::sync::Lazy; +use regex::Regex; +use serde::{Deserialize, Serialize}; + +use crate::alt_registry_images::branch::Branch; + +static VERSION_REGEX: Lazy = + Lazy::new(|| VersionRegex::new().unwrap_or_else(|err| panic!("{}", err))); + +struct VersionRegex(Regex); + +impl VersionRegex { + fn new() -> Result { + Regex::new(r"^(v\d+\.\d+\.\d+(-\d+|-[\w]+)?|(\d+\.\d+(\.\d+)?)(-\d+)?|\d+\.\d+)$") + .map(VersionRegex) + .map_err(|err| format!("regex build error: {}", err)) + } +} + +pub(crate) async fn fetch_branch_images(branch: &Branch) -> Result, String> { + let mut images = Vec::new(); + + for image_name in branch_images_names(branch).await? { + for tag in image_tags(&image_name).await? { + if !VERSION_REGEX.0.is_match(&tag) && tag != branch.as_str() { + continue; + } + + images.push(Image { + name: image_name.clone(), + tag, + }) + } + } + + Ok(images) +} + +pub(super) async fn fetch_all_images() -> Result, String> { + let branches = [Branch::Sisyphus, Branch::P11, Branch::P10]; + + let mut images = Vec::new(); + + for branch in branches { + images.extend(fetch_branch_images(&branch).await?) + } + + Ok(images) +} + +async fn image_tags(image_name: &str) -> Result, String> { + let res = reqwest::get(format!("{}/{}/tags/list", API_ALT_IMAGES, image_name)) + .await + .map_err(|err| format!("request execution error: {}", err))? + .json::() + .await + .map_err(|err| format!("TagList parsing error: {}", err))?; + + Ok(res.tags) +} + +async fn branch_images_names(branch: &Branch) -> Result, String> { + let mut images = Vec::new(); + + for image in images_names().await? { + if image.contains(branch.as_str()) || branch_is_in_the_tags(&image, branch).await? { + images.push(image.to_string()); + } + } + + Ok(images) +} + +async fn images_names() -> Result, String> { + let res = reqwest::get(format!("{}/_catalog", API_ALT_IMAGES)) + .await + .map_err(|err| format!("request execution error: {}", err))? + .json::() + .await + .map_err(|err| format!("image parsing error: {}", err))?; + + Ok(res.repositories) +} + +async fn branch_is_in_the_tags(image: &str, branch: &Branch) -> Result { + Ok(image_tags(image) + .await? + .contains(&branch.as_str().to_owned())) +} + +#[derive(Deserialize, Serialize)] +struct Images { + repositories: Vec, +} + +#[derive(Clone)] +pub(crate) struct Image { + name: String, + tag: String, +} + +impl Image { + pub(crate) fn name_ref(&self) -> &str { + &self.name + } + + pub(crate) fn tag_ref(&self) -> &str { + &self.tag + } +} + +#[derive(Deserialize, Serialize)] +struct TagsList { + tags: Vec, +} diff --git a/src/alt_registry_images/fetching_images/vulnerable_images.rs b/src/alt_registry_images/fetching_images/vulnerable_images.rs index e38ea92..301714d 100644 --- a/src/alt_registry_images/fetching_images/vulnerable_images.rs +++ b/src/alt_registry_images/fetching_images/vulnerable_images.rs @@ -1,10 +1,9 @@ use tokio::process::Command; -use super::image::{ - self, - Image, +use super::image::{self, Image}; +use crate::alt_registry_images::{ + degree_of_vulnerability::DegreeOfVulnerability, vulnerability::Vulnerabilities, }; -use crate::alt_registry_images::vulnerability::Vulnerabilities; pub(crate) async fn vulnerable_images() -> Result, String> { let mut images = Vec::new(); @@ -46,3 +45,65 @@ pub(crate) async fn image_vulnerabilities( .map_err(|err| format!("json parsing error: {}", err))?, )) } + +pub(crate) async fn images_that_vulnerabilities_are_eliminated_by_reassembly( +) -> Result, String> { + let mut images = Vec::new(); + + for image in vulnerable_images().await? { + let vulnes = image_vulnerabilities(&image).await?.ok_or_else(|| { + "the image is without vulnerabilities, but the opposite was expected".to_owned() + })?; + + if vulnes + .cve_list() + .iter() + .filter(|cve| { + cve.severity() == DegreeOfVulnerability::Critical.as_str() + || cve.severity() == DegreeOfVulnerability::High.as_str() + || cve.severity() == DegreeOfVulnerability::Medium.as_str() + }) + .all(|cve| { + cve.package_list_ref() + .iter() + .all(|package| package.name().contains("golang.org")) + }) + { + images.push(image.clone()) + } + } + + Ok(images) +} + +#[cfg(test)] +mod tests { + + #[tokio::test] + async fn images_that_vulnerabilities_are_eliminated_by_reassembly() { + let images = super::images_that_vulnerabilities_are_eliminated_by_reassembly() + .await + .unwrap(); + + for image in images { + let cves = super::image_vulnerabilities(&image).await.unwrap().unwrap(); + + if !cves + .cve_list() + .iter() + .filter(|cve| { + cve.severity() == "CRITICAL" + || cve.severity() == "HIGH" + || cve.severity() == "MEDIUM" + }) + .all(|cve| { + cve.package_list_ref() + .iter() + .all(|package| package.name().contains("golang.org")) + }) + { + panic!("not every vulnerability passes the test") + }; + } + } +} diff --git a/src/alt_registry_images/vulnerability.rs b/src/alt_registry_images/vulnerability.rs index d9714de..b4762d5 100644 --- a/src/alt_registry_images/vulnerability.rs +++ b/src/alt_registry_images/vulnerability.rs @@ -1,7 +1,4 @@ -use serde::{ - Deserialize, - Serialize, -}; +use serde::{Deserialize, Serialize}; #[derive(Deserialize)] pub(crate) struct Vulnerabilities { diff --git a/src/configuration.rs b/src/configuration.rs index 8aa5bc1..305fe1f 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -1,42 +1,37 @@ -use std::env; - -use config::{ - Config, - ConfigError, - File, -}; -use once_cell::sync::Lazy; -use serde::Deserialize; - -pub static SETTINGS: Lazy = - Lazy::new(|| Settings::new().expect("failed to setup settings")); - -#[derive(Deserialize)] -pub struct Settings { - pub server: Server, - pub logger: Logger, -} - -#[derive(Deserialize)] -pub struct Server { - pub host: String, - pub port: u16, -} - -#[derive(Deserialize)] -pub struct Logger { - pub level: String, -} - -impl Settings { - fn new() -> Result { - let mut builder = - Config::builder().add_source(File::with_name("configuration")); - - if let Ok(port) = env::var("PORT") { - builder = builder.set_override("server.port", port)?; - } - - builder.build()?.try_deserialize() - } -} +use std::env; + +use config::{Config, ConfigError, File}; +use once_cell::sync::Lazy; +use serde::Deserialize; + +pub static SETTINGS: Lazy = + Lazy::new(|| Settings::new().expect("failed to setup settings")); + +#[derive(Deserialize)] +pub struct Settings { + pub server: Server, + pub logger: Logger, +} + +#[derive(Deserialize)] +pub struct Server { + pub host: String, + pub port: u16, +} + +#[derive(Deserialize)] +pub struct Logger { + pub level: String, +} + +impl Settings { + fn new() -> Result { + let mut builder = Config::builder().add_source(File::with_name("configuration.yaml")); + + if let Ok(port) = env::var("PORT") { + builder = builder.set_override("server.port", port)?; + } + + builder.build()?.try_deserialize() + } +} diff --git a/src/main.rs b/src/main.rs index 514d73d..5fcfcbd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,29 +1,26 @@ -use tokio::net::TcpListener; -use tracing_subscriber::EnvFilter; -use vuln_aggregator::configuration::SETTINGS; - -#[tokio::main] -async fn main() { - tracing_subscriber::fmt() - .with_ansi(false) - .with_target(false) - .with_env_filter( - EnvFilter::try_from_default_env() - .unwrap_or(EnvFilter::new(SETTINGS.logger.level.as_str())), - ) - .pretty() - .init(); - - let listener = TcpListener::bind(format!( - "{}:{}", - SETTINGS.server.host, SETTINGS.server.port - )) - .await - .expect("build listener error"); - - tracing::info!("Listening on {}", listener.local_addr().unwrap()); - - axum::serve(listener, vuln_aggregator::router::build::app().await) - .await - .expect("serve app error"); -} +use tokio::net::TcpListener; +use tracing_subscriber::EnvFilter; +use vuln_aggregator::configuration::SETTINGS; + +#[tokio::main] +async fn main() { + tracing_subscriber::fmt() + .with_ansi(false) + .with_target(false) + .with_env_filter( + EnvFilter::try_from_default_env() + .unwrap_or(EnvFilter::new(SETTINGS.logger.level.as_str())), + ) + .pretty() + .init(); + + let listener = TcpListener::bind(format!("{}:{}", SETTINGS.server.host, SETTINGS.server.port)) + .await + .expect("build listener error"); + + tracing::info!("Listening on {}", listener.local_addr().unwrap()); + + axum::serve(listener, vuln_aggregator::router::build::app().await) + .await + .expect("serve app error"); +} diff --git a/src/router/build.rs b/src/router/build.rs index 9bd1a8c..3882b2c 100644 --- a/src/router/build.rs +++ b/src/router/build.rs @@ -1,14 +1,17 @@ -use axum::{ - body::Body, - extract::Request, - routing, - Router, -}; +use std::env; + +use axum::{body::Body, extract::Request, routing, Extension, Router}; +use tera::Tera; use tower_http::trace::TraceLayer; use super::routing::routes; pub async fn app() -> Router { + let template_dir = + env::var("TEMPLATE_DIR").unwrap_or_else(|_| "src/router/templates".to_string()); + + let tera = Tera::new(&format!("{}/**/*", template_dir)).expect("failed to load templates"); + Router::new() .route( "/vulnerable_branch_images_considering_vulnerability/:branch", @@ -16,7 +19,7 @@ pub async fn app() -> Router { ) .route( "/images_that_vulnerabilities_are_eliminated_by_reassembly", - routing::get(routes::images_that_vulnerabilities_are_eliminated_by_reassembly), + routing::get(routes::images_that_vulnerabilities_are_eliminated_by_reassembly_handler), ) .layer( TraceLayer::new_for_http().make_span_with(|request: &Request| { @@ -32,4 +35,5 @@ pub async fn app() -> Router { ) }), ) + .layer(Extension(tera)) } diff --git a/src/router/routing.rs b/src/router/routing.rs index 91e4e2c..a3258e6 100644 --- a/src/router/routing.rs +++ b/src/router/routing.rs @@ -1,38 +1,35 @@ -pub mod output_image; -pub mod routes; - -use axum::{ - http::StatusCode, - response::{ - IntoResponse, - Response, - }, - Json, -}; - -pub(crate) enum AppError { - NonSupportedParams(String), - Other(String), -} - -impl IntoResponse for AppError { - fn into_response(self) -> Response { - let (status, details) = match self { - AppError::NonSupportedParams(err) => { - tracing::debug!(err); - - (StatusCode::BAD_REQUEST, err) - } - AppError::Other(err) => { - tracing::error!(err); - - ( - StatusCode::INTERNAL_SERVER_ERROR, - "something went wrong".to_owned(), - ) - } - }; - - (status, Json(serde_json::json!({"details": details}))).into_response() - } -} +pub mod output_image; +pub mod routes; + +use axum::{ + http::StatusCode, + response::{IntoResponse, Response}, + Json, +}; + +pub(crate) enum AppError { + NonSupportedParams(String), + Other(String), +} + +impl IntoResponse for AppError { + fn into_response(self) -> Response { + let (status, details) = match self { + AppError::NonSupportedParams(err) => { + tracing::debug!(err); + + (StatusCode::BAD_REQUEST, err) + } + AppError::Other(err) => { + tracing::error!(err); + + ( + StatusCode::INTERNAL_SERVER_ERROR, + "something went wrong".to_owned(), + ) + } + }; + + (status, Json(serde_json::json!({"details": details}))).into_response() + } +} diff --git a/src/router/routing/output_image.rs b/src/router/routing/output_image.rs index 8071d6e..c07c209 100644 --- a/src/router/routing/output_image.rs +++ b/src/router/routing/output_image.rs @@ -1,20 +1,12 @@ -use serde::{ - Deserialize, - Serialize, -}; +use serde::{Deserialize, Serialize}; use super::AppError; use crate::alt_registry_images::{ - fetching_images::{ - image::Image, - vulnerable_images, - }, + fetching_images::{image::Image, vulnerable_images}, vulnerability::Cve, }; -pub(super) async fn output_images( - images: Vec, -) -> Result { +pub(super) async fn output_images(images: Vec) -> Result { let mut output_images = Vec::new(); for image in images { diff --git a/src/router/routing/routes.rs b/src/router/routing/routes.rs index 975435f..e421544 100644 --- a/src/router/routing/routes.rs +++ b/src/router/routing/routes.rs @@ -1,32 +1,22 @@ use axum::{ - extract::{ - Path, - Query, - }, - response::IntoResponse, - Json, + extract::{Path, Query}, + response::{Html, IntoResponse}, + Extension, Json, }; use serde::Deserialize; +use tera::Tera; -use super::{ - output_image, - AppError, -}; +use super::{output_image, AppError}; use crate::alt_registry_images::{ - self, - branch::Branch, - degree_of_vulnerability::DegreeOfVulnerability, + self, branch::Branch, degree_of_vulnerability::DegreeOfVulnerability, fetching_images::vulnerable_images, }; pub(crate) async fn vulnerable_branch_images_considering_vulnerability( Path(branch): Path, - Query(degree_of_vulnerability_template): Query< - DegreeOfVulnerabilityTemplate, - >, + Query(degree_of_vulnerability_template): Query, ) -> Result { - let branch = Branch::try_from(branch.as_str()) - .map_err(AppError::NonSupportedParams)?; + let branch = Branch::try_from(branch.as_str()).map_err(AppError::NonSupportedParams)?; let degree_of_vulnerability = DegreeOfVulnerability::try_from( degree_of_vulnerability_template @@ -36,10 +26,7 @@ pub(crate) async fn vulnerable_branch_images_considering_vulnerability( .map_err(AppError::NonSupportedParams)?; let mut images = Vec::new(); - for image in - alt_registry_images::fetching_images::image::fetch_branch_images( - &branch, - ) + for image in alt_registry_images::fetching_images::image::fetch_branch_images(&branch) .await .map_err(AppError::Other)? { @@ -71,43 +58,21 @@ pub(crate) async fn vulnerable_branch_images_considering_vulnerability( Ok(Json(output_image::output_images(images).await?)) } -pub(crate) async fn images_that_vulnerabilities_are_eliminated_by_reassembly( +pub(crate) async fn images_that_vulnerabilities_are_eliminated_by_reassembly_handler( + Extension(tera): Extension, ) -> Result { - let mut images = Vec::new(); - - for image in vulnerable_images::vulnerable_images() + let images = vulnerable_images::images_that_vulnerabilities_are_eliminated_by_reassembly() .await - .map_err(AppError::Other)? - { - let vulnes = vulnerable_images::image_vulnerabilities(&image) - .await - .map_err(AppError::Other)? - .ok_or_else(|| { - AppError::Other( - "the image is without vulnerabilities, but the opposite was expected" - .to_owned(), - ) - })?; + .map_err(AppError::Other)?; - if vulnes - .cve_list() - .iter() - .filter(|cve| { - cve.severity() == DegreeOfVulnerability::Critical.as_str() - || cve.severity() == DegreeOfVulnerability::High.as_str() - || cve.severity() == DegreeOfVulnerability::Medium.as_str() - }) - .all(|cve| { - cve.package_list_ref() - .iter() - .all(|package| package.name().contains("golang.org")) - }) - { - images.push(image.clone()) - } - } + let images = output_image::output_images(images).await?; - Ok(Json(output_image::output_images(images).await?)) + let mut context = tera::Context::new(); + context.insert("images", &images.images()); + + Ok(Html(tera.render("index.html", &context).map_err( + |err| AppError::Other(format!("failed render html: {}", err)), + )?)) } #[derive(Deserialize)] diff --git a/src/router/templates/index.html b/src/router/templates/index.html new file mode 100644 index 0000000..c611616 --- /dev/null +++ b/src/router/templates/index.html @@ -0,0 +1,59 @@ + + + {% include "utils/header.html" %} + +
+

Images that vulnerabilities are eliminated by reassembly

+ {% for image in images %} +
+ + +
+ {% for cve in image.CVEList %} +
+ + +
+ {{ cve.Title }}
+ {% if cve.PackageList | length > 0 %} +
+ Packages + {% for package in cve.PackageList %} +
+ {{ package.Name }}
+
+
+ Installed Version + {{ package.InstalledVersion }} +
+
+ Fixed Version + {{ package.FixedVersion }} +
+
+
+ {% endfor %} +
+ {% endif %} + Description: {{ cve.Description }} +
+
+ {% endfor %} +
+
+ {% endfor %} +
+ + diff --git a/src/router/templates/utils/header.html b/src/router/templates/utils/header.html new file mode 100644 index 0000000..526d693 --- /dev/null +++ b/src/router/templates/utils/header.html @@ -0,0 +1,149 @@ + + + + + + Images that vulnerabilities are eliminated by reassembly + + + diff --git a/tests/images_that_vulnerabilities_are_eliminated_by_reassembly.rs b/tests/images_that_vulnerabilities_are_eliminated_by_reassembly.rs deleted file mode 100644 index bdb0966..0000000 --- a/tests/images_that_vulnerabilities_are_eliminated_by_reassembly.rs +++ /dev/null @@ -1,51 +0,0 @@ -use axum::{ - body::{ - to_bytes, - Body, - }, - http::{ - Method, - Request, - }, -}; -use tower::ServiceExt; -use vuln_aggregator::router::routing::output_image::OutputImages; - -#[tokio::test] -async fn returns_only_those_images_that_can_be_fixed_by_reassembling() { - let request = Request::builder() - .method(Method::GET) - .uri("/images_that_vulnerabilities_are_eliminated_by_reassembly") - .body(Body::empty()) - .expect("build request error"); - - let app = vuln_aggregator::router::build::app().await; - - let response = app.oneshot(request).await.expect("request execution error"); - - let body_bytes = to_bytes(response.into_body(), usize::MAX).await.unwrap(); - - let body_str = String::from_utf8(body_bytes.to_vec()).unwrap(); - - let output = serde_json::from_str::(&body_str).unwrap(); - - if !output.images().iter().all(|image| { - image - .cve_list() - .as_ref() - .unwrap() - .iter() - .filter(|cve| { - cve.severity() == "CRITICAL" - || cve.severity() == "HIGH" - || cve.severity() == "MEDIUM" - }) - .all(|cve| { - cve.package_list_ref() - .iter() - .all(|package| package.name().contains("golang.org")) - }) - }) { - panic!("not every vulnerability passes the test") - } -} diff --git a/tests/vulnerable_branch_images_considering_vulnerability.rs b/tests/vulnerable_branch_images_considering_vulnerability.rs index 19cc26a..05d8dc5 100644 --- a/tests/vulnerable_branch_images_considering_vulnerability.rs +++ b/tests/vulnerable_branch_images_considering_vulnerability.rs @@ -1,97 +1,88 @@ -use axum::{ - body::{ - to_bytes, - Body, - }, - http::{ - Method, - Request, - StatusCode, - }, -}; -use tower::ServiceExt; -use vuln_aggregator::router::routing::output_image::OutputImages; - -#[tokio::test] -async fn returned_200_if_the_branch_and_degree_of_vulnerailities_is_supported() -{ - let request = Request::builder() - .method(Method::GET) - .uri("/vulnerable_branch_images_considering_vulnerability/sisyphus?degree_vulnerability=NONE") - .body(Body::empty()) - .expect("build request error"); - - let app = vuln_aggregator::router::build::app().await; - - let response = app.oneshot(request).await.expect("request execution error"); - - assert_eq!(response.status(), StatusCode::OK); -} - -#[tokio::test] -async fn returned_400_if_the_branch_is_not_supported() { - let request = Request::builder() - .method(Method::GET) - .uri( - "/vulnerable_branch_images_considering_vulnerability/not_supported?degree_vulnerability=NONE", - ) - .body(Body::empty()) - .expect("build request error"); - - let app = vuln_aggregator::router::build::app().await; - - let response = app.oneshot(request).await.expect("request execution error"); - - assert_eq!(response.status(), StatusCode::BAD_REQUEST); -} - -#[tokio::test] -async fn returned_400_if_the_degree_of_vulnerabilities_is_not_supported() { - let request = Request::builder() - .method(Method::GET) - .uri("/vulnerable_branch_images_considering_vulnerability/sisyphus?degree_vulnerability=not_supported") - .body(Body::empty()) - .expect("build request error"); - - let app = vuln_aggregator::router::build::app().await; - - let response = app.oneshot(request).await.expect("request execution error"); - - assert_eq!(response.status(), StatusCode::BAD_REQUEST); -} - -#[tokio::test] -async fn images_of_only_the_selected_branch_with_the_selected_vulnerability_are_returned( -) { - let request = Request::builder() - .method(Method::GET) - .uri("/vulnerable_branch_images_considering_vulnerability/sisyphus?degree_vulnerability=CRITICAL") - .body(Body::empty()) - .expect("build request error"); - - let app = vuln_aggregator::router::build::app().await; - - let response = app.oneshot(request).await.expect("request execution error"); - - let body_bytes = to_bytes(response.into_body(), usize::MAX).await.unwrap(); - - let body_str = String::from_utf8(body_bytes.to_vec()).unwrap(); - - let output = serde_json::from_str::(&body_str).unwrap(); - - for image in output.images() { - if !image.image().to_owned().contains("sisyphus") { - panic!("unexpected branch") - } - - if !image - .cve_list() - .as_ref() - .unwrap() - .iter() - .any(|cve| cve.severity() == "CRITICAL") - { - panic!("there is no selected vulnerability") - } - } -} +use axum::{ + body::{to_bytes, Body}, + http::{Method, Request, StatusCode}, +}; +use tower::ServiceExt; +use vuln_aggregator::router::routing::output_image::OutputImages; + +#[tokio::test] +async fn returned_200_if_the_branch_and_degree_of_vulnerailities_is_supported() { + let request = Request::builder() + .method(Method::GET) + .uri("/vulnerable_branch_images_considering_vulnerability/sisyphus?degree_vulnerability=NONE") + .body(Body::empty()) + .expect("build request error"); + + let app = vuln_aggregator::router::build::app().await; + + let response = app.oneshot(request).await.expect("request execution error"); + + assert_eq!(response.status(), StatusCode::OK); +} + +#[tokio::test] +async fn returned_400_if_the_branch_is_not_supported() { + let request = Request::builder() + .method(Method::GET) + .uri( + "/vulnerable_branch_images_considering_vulnerability/not_supported?degree_vulnerability=NONE", + ) + .body(Body::empty()) + .expect("build request error"); + + let app = vuln_aggregator::router::build::app().await; + + let response = app.oneshot(request).await.expect("request execution error"); + + assert_eq!(response.status(), StatusCode::BAD_REQUEST); +} + +#[tokio::test] +async fn returned_400_if_the_degree_of_vulnerabilities_is_not_supported() { + let request = Request::builder() + .method(Method::GET) + .uri("/vulnerable_branch_images_considering_vulnerability/sisyphus?degree_vulnerability=not_supported") + .body(Body::empty()) + .expect("build request error"); + + let app = vuln_aggregator::router::build::app().await; + + let response = app.oneshot(request).await.expect("request execution error"); + + assert_eq!(response.status(), StatusCode::BAD_REQUEST); +} + +#[tokio::test] +async fn images_of_only_the_selected_branch_with_the_selected_vulnerability_are_returned() { + let request = Request::builder() + .method(Method::GET) + .uri("/vulnerable_branch_images_considering_vulnerability/sisyphus?degree_vulnerability=CRITICAL") + .body(Body::empty()) + .expect("build request error"); + + let app = vuln_aggregator::router::build::app().await; + + let response = app.oneshot(request).await.expect("request execution error"); + + let body_bytes = to_bytes(response.into_body(), usize::MAX).await.unwrap(); + + let body_str = String::from_utf8(body_bytes.to_vec()).unwrap(); + + let output = serde_json::from_str::(&body_str).unwrap(); + + for image in output.images() { + if !image.image().to_owned().contains("sisyphus") { + panic!("unexpected branch") + } + + if !image + .cve_list() + .as_ref() + .unwrap() + .iter() + .any(|cve| cve.severity() == "CRITICAL") + { + panic!("there is no selected vulnerability") + } + } +}