From 3031d3b99556aaf8eee66185218b7ef0971c974f Mon Sep 17 00:00:00 2001 From: Fabrizio Sestito Date: Thu, 21 Nov 2024 08:20:46 +0100 Subject: [PATCH] refactor: remove traits for simplicity Signed-off-by: Fabrizio Sestito --- src/lib.rs | 125 ++++++++++++++++++++++++---------------- src/workload_helpers.rs | 116 ------------------------------------- 2 files changed, 76 insertions(+), 165 deletions(-) delete mode 100644 src/workload_helpers.rs diff --git a/src/lib.rs b/src/lib.rs index 23b251c..a2b55d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,26 @@ +mod sbombastic; +use sbombastic::storage::v1alpha1::VulnerabilityReport; + +mod settings; +use settings::{Settings, SeverityCount}; + +mod errors; +mod sarif; + use std::collections::{HashMap, HashSet}; use errors::{CVEViolationStats, ImageValidationError, Violation}; use guest::prelude::*; use kubewarden_policy_sdk::wapc_guest as guest; -use k8s_openapi::api::{ - apps::v1::{DaemonSet, Deployment, ReplicaSet, StatefulSet}, - batch::v1::{CronJob, Job}, - core::v1::{Pod, PodSpec, ReplicationController}, +use k8s_openapi::{ + api::{ + apps::v1::{DaemonSet, Deployment, ReplicaSet, StatefulSet}, + batch::v1::{CronJob, Job}, + core::v1::{Pod, PodSpec, ReplicationController}, + }, + apimachinery::pkg::apis::meta::v1::ObjectMeta, + Metadata, Resource, }; use kubewarden_policy_sdk::{ accept_request, host_capabilities::kubernetes::GetResourceRequest, logging, @@ -15,7 +28,7 @@ use kubewarden_policy_sdk::{ }; use lazy_static::lazy_static; use sarif::Vulnerability; -use serde::{de::DeserializeOwned, Serialize}; +use serde::de::DeserializeOwned; use slog::{info, o, warn, Logger}; #[cfg(test)] @@ -26,18 +39,6 @@ use kubewarden_policy_sdk::host_capabilities::{ kubernetes::get_resource, oci::get_manifest_digest, }; -mod workload_helpers; -use workload_helpers::{ImageHolder, ValidatingResource}; - -mod sbombastic; -use sbombastic::storage::v1alpha1::VulnerabilityReport; - -mod settings; -use settings::{Settings, SeverityCount}; - -mod errors; -mod sarif; - type ImageValidationErrors = HashMap; lazy_static! { @@ -56,17 +57,39 @@ fn validate(payload: &[u8]) -> CallResult { let validation_request: ValidationRequest = ValidationRequest::new(payload)?; match validation_request.request.kind.kind.as_str() { - "Deployment" => validate_resource::(validation_request), - "ReplicaSet" => validate_resource::(validation_request), - "StatefulSet" => validate_resource::(validation_request), - "DaemonSet" => validate_resource::(validation_request), - "ReplicationController" => validate_resource::(validation_request), - "Job" => validate_resource::(validation_request), - "CronJob" => validate_resource::(validation_request), - "Pod" => validate_resource::(validation_request), + "Deployment" => validate_resource::(validation_request, |deployment| { + deployment.spec.as_ref()?.template.spec.clone() + }), + "ReplicaSet" => validate_resource::(validation_request, |replicaset| { + replicaset.spec.as_ref()?.template.as_ref()?.spec.clone() + }), + "StatefulSet" => validate_resource::(validation_request, |statefulset| { + statefulset.spec.as_ref()?.template.spec.clone() + }), + "DaemonSet" => validate_resource::(validation_request, |daemonset| { + daemonset.spec.as_ref()?.template.spec.clone() + }), + "ReplicationController" => { + validate_resource::(validation_request, |rc| { + rc.spec.as_ref()?.template.as_ref()?.spec.clone() + }) + } + "Job" => validate_resource::(validation_request, |job| { + job.spec.as_ref()?.template.spec.clone() + }), + "CronJob" => validate_resource::(validation_request, |cronjob| { + cronjob + .spec + .as_ref()? + .job_template + .spec + .as_ref()? + .template + .spec + .clone() + }), + "Pod" => validate_resource::(validation_request, |pod| pod.spec.clone()), _ => { - // We were forwarded a request we cannot unmarshal or - // understand, just accept it warn!(LOG_DRAIN, "cannot unmarshal resource: this policy does not know how to evaluate this resource; accept it"); accept_request() } @@ -75,20 +98,22 @@ fn validate(payload: &[u8]) -> CallResult { // validate any resource that contains a Pod. e.g. Deployment, StatefulSet, ... // it does not modify the container with the manifest digest. -fn validate_resource( +fn validate_resource( validation_request: ValidationRequest, -) -> CallResult { + extract_spec: fn(&T) -> Option, +) -> CallResult +where + T: Resource + Metadata + DeserializeOwned, +{ let resource = match serde_json::from_value::(validation_request.request.object.clone()) { Ok(resource) => resource, Err(_) => { - // We were forwarded a request we cannot unmarshal or - // understand, just accept it warn!(LOG_DRAIN, "cannot unmarshal resource: this policy does not know how to evaluate this resource; accept it"); return accept_request(); } }; - let spec = match resource.spec() { + let spec = match extract_spec(&resource) { Some(spec) => spec, None => { return accept_request(); @@ -116,7 +141,7 @@ fn validate_resource( reject_request( Some(format!( "Resource {} is not accepted: {}", - &resource.name(), + resource.metadata().name.as_ref().unwrap(), rejection_details, )), None, @@ -124,35 +149,37 @@ fn validate_resource( None, ) } - /// verify all images defined in the PodSpec. The violations are stored in the `image_validation_errors` map. fn verify_all_images_in_podspec( spec: &PodSpec, settings: &Settings, image_validation_errors: &mut ImageValidationErrors, ) { - verify_container_images(&spec.containers, settings, image_validation_errors); + let mut images: Vec = spec + .containers + .iter() + .map(|c| c.image.clone().unwrap()) + .collect(); if let Some(init_containers) = &spec.init_containers { - verify_container_images(init_containers, settings, image_validation_errors); + let init_images: Vec = init_containers + .iter() + .map(|c| c.image.clone().unwrap()) + .collect(); + images.extend(init_images); } if let Some(ephemeral_containers) = &spec.ephemeral_containers { - verify_container_images(ephemeral_containers, settings, image_validation_errors); + let ephemeral_images: Vec = ephemeral_containers + .iter() + .map(|c| c.image.clone().unwrap()) + .collect(); + + images.extend(ephemeral_images); } -} -/// verify all images defined in the list of containers. The violations are stored in the `image_validation_errors` map. -fn verify_container_images( - containers: &[T], - settings: &Settings, - image_validation_errors: &mut ImageValidationErrors, -) where - T: ImageHolder + PartialEq, -{ - for container in containers { - let container_image = container.get_image().unwrap(); - verify_container_image(&container_image, settings, image_validation_errors); + for image in images { + verify_container_image(&image, settings, image_validation_errors); } } diff --git a/src/workload_helpers.rs b/src/workload_helpers.rs deleted file mode 100644 index 8b3ab0d..0000000 --- a/src/workload_helpers.rs +++ /dev/null @@ -1,116 +0,0 @@ -use k8s_openapi::api::{ - apps::v1::{DaemonSet, Deployment, ReplicaSet, StatefulSet}, - batch::v1::{CronJob, Job}, - core::v1::{Container, EphemeralContainer, Pod, PodSpec, ReplicationController}, -}; - -/// Represents an abstraction of an struct that contains an image -/// Used to reuse code for Container and EphemeralContainer -pub(crate) trait ImageHolder: Clone { - fn get_image(&self) -> Option; -} - -impl ImageHolder for Container { - fn get_image(&self) -> Option { - self.image.clone() - } -} - -impl ImageHolder for EphemeralContainer { - fn get_image(&self) -> Option { - self.image.clone() - } -} - -/// Represents all resources that can be validated with this policy -pub(crate) trait ValidatingResource { - fn name(&self) -> String; - fn spec(&self) -> Option; -} - -impl ValidatingResource for Pod { - fn name(&self) -> String { - self.metadata.name.clone().unwrap_or_default() - } - - fn spec(&self) -> Option { - self.spec.clone() - } -} - -impl ValidatingResource for Deployment { - fn name(&self) -> String { - self.metadata.name.clone().unwrap_or_default() - } - - fn spec(&self) -> Option { - self.spec.as_ref()?.template.spec.clone() - } -} - -impl ValidatingResource for ReplicaSet { - fn name(&self) -> String { - self.metadata.name.clone().unwrap_or_default() - } - - fn spec(&self) -> Option { - self.spec.as_ref()?.template.as_ref()?.spec.clone() - } -} - -impl ValidatingResource for StatefulSet { - fn name(&self) -> String { - self.metadata.name.clone().unwrap_or_default() - } - - fn spec(&self) -> Option { - self.spec.as_ref()?.template.spec.clone() - } -} - -impl ValidatingResource for DaemonSet { - fn name(&self) -> String { - self.metadata.name.clone().unwrap_or_default() - } - - fn spec(&self) -> Option { - self.spec.as_ref()?.template.spec.clone() - } -} - -impl ValidatingResource for ReplicationController { - fn name(&self) -> String { - self.metadata.name.clone().unwrap_or_default() - } - - fn spec(&self) -> Option { - self.spec.as_ref()?.template.as_ref()?.spec.clone() - } -} - -impl ValidatingResource for Job { - fn name(&self) -> String { - self.metadata.name.clone().unwrap_or_default() - } - - fn spec(&self) -> Option { - self.spec.as_ref()?.template.spec.clone() - } -} - -impl ValidatingResource for CronJob { - fn name(&self) -> String { - self.metadata.name.clone().unwrap_or_default() - } - - fn spec(&self) -> Option { - self.spec - .as_ref()? - .job_template - .spec - .as_ref()? - .template - .spec - .clone() - } -}