diff --git a/src/lib.rs b/src/lib.rs index ab56f7b..2393911 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,9 @@ use std::collections::{HashMap, HashSet}; use errors::{CVEViolationStats, ImageValidationError, Violation}; use guest::prelude::*; -use kubewarden_policy_sdk::wapc_guest as guest; +use kubewarden_policy_sdk::{ + host_capabilities::kubernetes::ListResourcesByNamespaceRequest, wapc_guest as guest, +}; use k8s_openapi::{ api::{ @@ -23,8 +25,8 @@ use k8s_openapi::{ Metadata, Resource, }; use kubewarden_policy_sdk::{ - accept_request, host_capabilities::kubernetes::GetResourceRequest, logging, - protocol_version_guest, reject_request, request::ValidationRequest, validate_settings, + accept_request, logging, protocol_version_guest, reject_request, request::ValidationRequest, + validate_settings, }; use lazy_static::lazy_static; use sarif::Vulnerability; @@ -32,11 +34,13 @@ use serde::de::DeserializeOwned; use slog::{info, o, warn, Logger}; #[cfg(test)] -use crate::tests::{mock_kubernetes_sdk::get_resource, mock_oci_sdk::get_manifest_digest}; +use crate::tests::{ + mock_kubernetes_sdk::list_resources_by_namespace, mock_oci_sdk::get_manifest_digest, +}; #[cfg(not(test))] use kubewarden_policy_sdk::host_capabilities::{ - kubernetes::get_resource, oci::get_manifest_digest, + kubernetes::list_resources_by_namespace, oci::get_manifest_digest, }; type ImageValidationErrors = HashMap; @@ -242,13 +246,15 @@ fn verify_image_vulnerabilities( ) -> Result<(), ImageValidationError> { let namespace = settings.vulnerability_report_namespace.as_str(); - let vulnerability_report: VulnerabilityReport = match get_resource(&GetResourceRequest { - api_version: "storage.sbombastic.rancher.io/v1alpha1".to_string(), - kind: "VulnerabilityReport".to_string(), - name: digest.to_string(), - namespace: Some(namespace.to_string()), - disable_cache: false, - }) { + let vulnerability_reports = match list_resources_by_namespace( + &ListResourcesByNamespaceRequest { + api_version: "storage.sbombastic.rancher.io/v1alpha1".to_string(), + kind: "VulnerabilityReport".to_string(), + namespace: namespace.to_string(), + label_selector: None, + field_selector: Some(format!("spec.imageMetadata.digest={}", digest)), + }, + ) { Ok(report) => report, Err(e) => { if settings.ignore_missing_vulnerability_report { @@ -272,7 +278,23 @@ fn verify_image_vulnerabilities( } }; - let trivy_report: sarif::trivy::Report = vulnerability_report.try_into()?; + if vulnerability_reports.items.is_empty() { + if settings.ignore_missing_vulnerability_report { + info!(LOG_DRAIN, + "ignoring missing vulnerability report because ignoreMissingVulnerabilityReport is enabled"; + "digest" => digest, + "namespace" => namespace, + ); + + return Ok(()); + } + + return Err(ImageValidationError::VulnerabilityReportNotFound()); + } + + let vulnerability_report: &VulnerabilityReport = vulnerability_reports.items.first().unwrap(); + + let trivy_report: sarif::trivy::Report = vulnerability_report.clone().try_into()?; let report = sarif::Report::new(trivy_report, &settings.allow_always)?; verify_vulnerability_report(&report, settings)?; @@ -402,10 +424,15 @@ mod tests { #[automock()] pub mod kubernetes_sdk { - use kubewarden_policy_sdk::host_capabilities::kubernetes::GetResourceRequest; + use kubewarden_policy_sdk::host_capabilities::kubernetes::ListResourcesByNamespaceRequest; #[allow(dead_code)] - pub fn get_resource(_req: &GetResourceRequest) -> anyhow::Result { + pub fn list_resources_by_namespace( + _req: &ListResourcesByNamespaceRequest, + ) -> anyhow::Result> + where + T: k8s_openapi::ListableResource + serde::de::DeserializeOwned + Clone + 'static, + { Err(anyhow::anyhow!("not mocked")) } } @@ -596,7 +623,7 @@ mod tests { } }); - let ctx_get_resource = mock_kubernetes_sdk::get_resource_context(); + let ctx_get_resource = mock_kubernetes_sdk::list_resources_by_namespace_context(); ctx_get_resource .expect::() .times(1) @@ -607,7 +634,12 @@ mod tests { if req.kind != "VulnerabilityReport" { return Err(anyhow!("it's not searching the expected Kind")); } - if req.name != image_digest { + if req.namespace != "sbombastic" { + return Err(anyhow!("it's not searching the expected namespace")); + } + if req.field_selector + != Some(format!("spec.imageMetadata.digest={}", image_digest).to_owned()) + { return Err(anyhow!( "it's not searching the expected VulnerabilityReport" )); @@ -622,10 +654,16 @@ mod tests { let vulnerability_report: VulnerabilityReport = serde_yaml::from_reader(fixture_file).expect("cannot parse fixture file"); - return Ok(vulnerability_report); + return Ok(k8s_openapi::List { + items: vec![vulnerability_report], + ..Default::default() + }); }; - Err(anyhow!("VulnerabilityReport not found")) + Ok(k8s_openapi::List { + items: vec![], + ..Default::default() + }) }); let response = validate(serde_json::to_vec(&request).unwrap().as_slice()).unwrap();