From b95c61d61c35bed9f6cdc73de7c4f06027c42ab7 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Wed, 28 Feb 2024 13:07:03 +0100 Subject: [PATCH] Add: integrate openvasctl into openvasd and make it configurable --- rust/Cargo.lock | 4 +- rust/models/src/scan.rs | 2 +- rust/openvasctl/Cargo.toml | 7 +- rust/openvasctl/src/config.rs | 18 +- rust/openvasctl/src/ctl.rs | 2 +- rust/openvasctl/src/error.rs | 22 ++- rust/openvasctl/src/openvas.rs | 2 +- rust/openvasctl/src/scheduler.rs | 33 ++-- rust/openvasd/Cargo.toml | 1 + rust/openvasd/src/config.rs | 122 +++++++++++- rust/openvasd/src/controller/context.rs | 5 +- rust/openvasd/src/controller/mod.rs | 2 +- rust/openvasd/src/main.rs | 89 +++++++-- rust/openvasd/src/scan.rs | 238 ++++++++++++++++++++++++ rust/openvasd/src/storage/file.rs | 4 +- rust/osp/src/commands.rs | 3 +- 16 files changed, 492 insertions(+), 62 deletions(-) create mode 100644 rust/openvasd/src/scan.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 1585457ed..db6739044 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1943,7 +1943,7 @@ dependencies = [ ] [[package]] -name = "openvas" +name = "openvasctl" version = "0.1.0" dependencies = [ "configparser", @@ -1951,6 +1951,7 @@ dependencies = [ "osp", "redis", "redis-storage", + "serde", "storage", "sysinfo", "tokio", @@ -1977,6 +1978,7 @@ dependencies = [ "models", "nasl-interpreter", "notus", + "openvasctl", "osp", "pbkdf2", "rand", diff --git a/rust/models/src/scan.rs b/rust/models/src/scan.rs index 24b26a788..4110da265 100644 --- a/rust/models/src/scan.rs +++ b/rust/models/src/scan.rs @@ -12,7 +12,7 @@ use super::{scanner_preference::ScannerPreference, target::Target, vt::VT}; serde(deny_unknown_fields) )] pub struct Scan { - #[cfg_attr(feature = "serde_support", serde(skip_deserializing, default = "uuid"))] + #[cfg_attr(feature = "serde_support", serde(default = "uuid"))] /// Unique ID of a scan pub scan_id: String, /// Information about the target to scan diff --git a/rust/openvasctl/Cargo.toml b/rust/openvasctl/Cargo.toml index e910b0d4f..2faab5779 100644 --- a/rust/openvasctl/Cargo.toml +++ b/rust/openvasctl/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "openvas" +name = "openvasctl" version = "0.1.0" edition = "2021" @@ -15,3 +15,8 @@ storage = { version = "0.1.0", path = "../storage" } sysinfo = "0.30.5" tokio = { version = "1.36.0", features = ["full"] } tracing = "0.1.40" +serde = { version = "1", features = ["derive"], optional = true } + +[features] +default = ["serde_support"] +serde_support = ["serde"] diff --git a/rust/openvasctl/src/config.rs b/rust/openvasctl/src/config.rs index 789fad796..d6991da4b 100644 --- a/rust/openvasctl/src/config.rs +++ b/rust/openvasctl/src/config.rs @@ -1,9 +1,25 @@ use std::time::Duration; -#[derive(Default)] +#[derive(Debug, Clone)] +#[cfg_attr( + feature = "serde_support", + derive(serde::Serialize, serde::Deserialize), + serde(deny_unknown_fields) +)] pub struct Config { pub max_queued_scans: Option, pub max_running_scans: Option, pub min_free_mem: Option, pub check_interval: Duration, } + +impl Default for Config { + fn default() -> Self { + Self { + max_queued_scans: Default::default(), + max_running_scans: Default::default(), + min_free_mem: Default::default(), + check_interval: Duration::from_secs(1), + } + } +} diff --git a/rust/openvasctl/src/ctl.rs b/rust/openvasctl/src/ctl.rs index eda8f1b62..12ac1faab 100644 --- a/rust/openvasctl/src/ctl.rs +++ b/rust/openvasctl/src/ctl.rs @@ -71,7 +71,7 @@ impl OpenvasController { let mut scan = match self.remove_running(&scan_id) { Some(scan) => scan, - None => return Err(OpenvasError::ScanNotFound), + None => return Err(OpenvasError::ScanNotFound(scan_id)), }; cmd::stop(&scan_id, self.sudo) diff --git a/rust/openvasctl/src/error.rs b/rust/openvasctl/src/error.rs index 4b8f233af..87912178b 100644 --- a/rust/openvasctl/src/error.rs +++ b/rust/openvasctl/src/error.rs @@ -1,13 +1,23 @@ -use std::io; +use std::{fmt::Display, io}; pub enum OpenvasError { - MissingID, - DuplicateScanID, + DuplicateScanID(String), MissingExec, - ScanNotFound, - ScanAlreadyExists, + ScanNotFound(String), CmdError(io::Error), - BrokenChannel, MaxQueuedScans, UnableToRunExec, } + +impl Display for OpenvasError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + OpenvasError::DuplicateScanID(id) => write!(f, "a scan with ID {id} already exists"), + OpenvasError::MissingExec => write!(f, "unable to launch openvas executable"), + OpenvasError::ScanNotFound(id) => write!(f, "a scan with ID {id} not found"), + OpenvasError::CmdError(e) => write!(f, "unable to run command: {e}"), + OpenvasError::MaxQueuedScans => write!(f, "maximum number of queued scan reached"), + OpenvasError::UnableToRunExec => write!(f, "unable to run openvas"), + } + } +} diff --git a/rust/openvasctl/src/openvas.rs b/rust/openvasctl/src/openvas.rs index e251832db..79411b77a 100644 --- a/rust/openvasctl/src/openvas.rs +++ b/rust/openvasctl/src/openvas.rs @@ -59,7 +59,7 @@ impl ScanController for OpenvasControl { let mut scan = match self.remove_running(&scan_id) { Some(scan) => scan, - None => return Err(OpenvasError::ScanNotFound), + None => return Err(OpenvasError::ScanNotFound(id.to_string())), }; cmd::stop(&scan_id, self.sudo) diff --git a/rust/openvasctl/src/scheduler.rs b/rust/openvasctl/src/scheduler.rs index eb82f6865..4571de221 100644 --- a/rust/openvasctl/src/scheduler.rs +++ b/rust/openvasctl/src/scheduler.rs @@ -1,16 +1,11 @@ -use std::{ - collections::VecDeque, - sync::{Arc, Mutex}, -}; +use std::{collections::VecDeque, sync::Arc}; use sysinfo::System; -use tokio::task; +use tokio::{sync::Mutex, task}; use crate::{config::Config, ctl::ScanController, error::OpenvasError}; -/// Estimated memory a scan will use -const SCAN_MEM: u64 = 1024 * 1024 * 512; - +#[derive(Debug, Clone)] pub struct Scheduler { queue: Arc>>, config: Config, @@ -25,10 +20,10 @@ where S: ScanController + std::marker::Send + std::marker::Sync + 'static, { /// Create a new OpenvasController - pub fn new(config: Option, controller: S) -> Self { + pub fn new(config: Config, controller: S) -> Self { Self { queue: Default::default(), - config: config.unwrap_or_default(), + config, controller: Arc::new(controller), } } @@ -36,21 +31,21 @@ where /// Add a new scan to the queue. An error is returned, if the queue is either full or the scan /// ID is already registered. pub async fn add(&mut self, scan: models::Scan) -> Result<(), OpenvasError> { - let queue = self.queue.lock().unwrap(); + let queue = self.queue.lock().await; if let Some(max_queued_scans) = self.config.max_queued_scans { if queue.len() == max_queued_scans { return Err(OpenvasError::MaxQueuedScans); } } if queue.iter().any(|x| x.scan_id == scan.scan_id) { - return Err(OpenvasError::DuplicateScanID); + return Err(OpenvasError::DuplicateScanID(scan.scan_id)); } - self.queue.lock().unwrap().push_back(scan); + self.queue.lock().await.push_back(scan); Ok(()) } - fn remove_queue(&mut self, id: &str) -> bool { - let mut queue = self.queue.lock().unwrap(); + async fn remove_queue(&mut self, id: &str) -> bool { + let mut queue = self.queue.lock().await; if let Some(index) = queue.iter().position(|x| x.scan_id == id) { queue.remove(index); return true; @@ -63,7 +58,7 @@ where /// as a scan should not actually start, when it is removed. An error is returned, if /// the scan in not registered in the scheduler. pub async fn stop(&mut self, id: &str) -> Result<(), OpenvasError> { - if self.remove_queue(id) { + if self.remove_queue(id).await { return Ok(()); } @@ -85,7 +80,7 @@ where // TODO: Remove forgotten finished scans // Are scans in the queue? - if self.queue.lock().unwrap().is_empty() { + if self.queue.lock().await.is_empty() { continue; } @@ -99,7 +94,7 @@ where // Check available resources sys.refresh_memory(); if let Some(min_free_mem) = self.config.min_free_mem { - if sys.available_memory() - self.controller.num_init() as u64 * SCAN_MEM + if sys.available_memory() - self.controller.num_init() as u64 * min_free_mem < min_free_mem { continue; @@ -107,7 +102,7 @@ where } // Start next scan in queue - let mut queue = self.queue.lock().unwrap(); + let mut queue = self.queue.lock().await; if let Some(scan) = queue.pop_front() { let controller = self.controller.clone(); controller.set_init(&scan.scan_id); diff --git a/rust/openvasd/Cargo.toml b/rust/openvasd/Cargo.toml index 5692c41ae..b65c64e36 100644 --- a/rust/openvasd/Cargo.toml +++ b/rust/openvasd/Cargo.toml @@ -14,6 +14,7 @@ storage = { path = "../storage" } redis-storage = { path = "../redis-storage" } infisto = { path = "../infisto" } notus = { path = "../notus" } +openvasctl = { path = "../openvasctl" } hyper = { version = "1", features = ["full"] } hyper-rustls = "0" tokio = { version = "1.28.1", features = ["full"] } diff --git a/rust/openvasd/src/config.rs b/rust/openvasd/src/config.rs index 0a89ccbed..481dbd190 100644 --- a/rust/openvasd/src/config.rs +++ b/rust/openvasd/src/config.rs @@ -30,6 +30,54 @@ pub struct Redis { pub url: String, } +#[derive(Deserialize, Serialize, Debug, Clone, Default)] +pub struct Wrapper { + #[serde(default)] + pub wrapper_type: WrapperType, + #[serde(default)] + pub ospd: OspdWrapper, + #[serde(default)] + pub openvasctl: openvasctl::config::Config, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub enum WrapperType { + #[serde(rename = "ospd")] + OSPD, + #[serde(rename = "openvasctl")] + Openvasctl, +} + +impl Default for WrapperType { + fn default() -> Self { + Self::OSPD + } +} + +impl TypedValueParser for WrapperType { + type Value = WrapperType; + + fn parse_ref( + &self, + cmd: &clap::Command, + _: Option<&clap::Arg>, + value: &std::ffi::OsStr, + ) -> Result { + Ok(match value.to_str().unwrap_or_default() { + "ospd" => WrapperType::OSPD, + "openvasctl" => WrapperType::Openvasctl, + _ => { + let mut cmd = cmd.clone(); + let err = cmd.error( + clap::error::ErrorKind::InvalidValue, + "`{}` is not a wrapper type.", + ); + return Err(err); + } + }) + } +} + #[derive(Deserialize, Serialize, Debug, Clone)] pub struct OspdWrapper { pub result_check_interval: Duration, @@ -142,7 +190,7 @@ impl TypedValueParser for StorageType { let mut cmd = cmd.clone(); let err = cmd.error( clap::error::ErrorKind::InvalidValue, - "`{}` is not an storage type.", + "`{}` is not a storage type.", ); return Err(err); } @@ -186,7 +234,7 @@ pub struct Config { #[serde(default)] pub tls: Tls, #[serde(default)] - pub ospd: OspdWrapper, + pub wrapper: Wrapper, #[serde(default)] pub listener: Listener, #[serde(default)] @@ -318,6 +366,43 @@ impl Config { .action(ArgAction::Set) .help("API key that must be set as X-API-KEY header to gain access"), ) + .arg( + clap::Arg::new("wrapper-type") + .env("WRAPPER_TYPE") + .long("wrapper-type") + .value_name("ospd,openvasctl") + .value_parser(WrapperType::OSPD) + .help("Type of wrapper used to manage scans") + ) + .arg( + clap::Arg::new("max-queued-scans") + .env("MAX_QUEUED_SCANS") + .long("max-queued-scans") + .action(ArgAction::Set) + .help("Maximum number of queued scans") + ) + .arg( + clap::Arg::new("max-running-scans") + .env("MAX_RUNNING_SCANS") + .long("max-running-scans") + .action(ArgAction::Set) + .help("Maximum number of active running scans, omit for no limits") + ) + .arg( + clap::Arg::new("min-free-mem") + .env("MIN_FREE_MEMORY") + .long("min-free-mem") + .action(ArgAction::Set) + .help("Minimum memory available to start a new scan") + ) + .arg( + clap::Arg::new("check-interval") + .env("SCHEDULER_CHECK_INTERVAL") + .long("check_interval") + .value_parser(clap::value_parser!(u64)) + .value_name("SECONDS") + .help("Check interval of the Scheduler if a new scan can be started") + ) .arg( clap::Arg::new("ospd-socket") .env("OSPD_SOCKET") @@ -394,14 +479,29 @@ impl Config { if let Some(interval) = cmds.get_one::("feed-check-interval") { config.feed.check_interval = Duration::from_secs(*interval); } + if let Some(wrapper_type) = cmds.get_one::("wrapper-type") { + config.wrapper.wrapper_type = wrapper_type.clone() + } + if let Some(max_queued_scans) = cmds.get_one::("max-queued-scans") { + config.wrapper.openvasctl.max_queued_scans = Some(*max_queued_scans) + } + if let Some(max_running_scans) = cmds.get_one::("max_running_scans") { + config.wrapper.openvasctl.max_running_scans = Some(*max_running_scans) + } + if let Some(min_free_mem) = cmds.get_one::("min-free-mem") { + config.wrapper.openvasctl.min_free_mem = Some(*min_free_mem) + } + if let Some(check_interval) = cmds.get_one::("check-interval") { + config.wrapper.openvasctl.check_interval = Duration::from_secs(*check_interval) + } if let Some(interval) = cmds.get_one::("result-check-interval") { - config.ospd.result_check_interval = Duration::from_secs(*interval); + config.wrapper.ospd.result_check_interval = Duration::from_secs(*interval); } if let Some(path) = cmds.get_one::("ospd-socket") { - config.ospd.socket = path.clone(); + config.wrapper.ospd.socket = path.clone(); } if let Some(interval) = cmds.get_one::("read-timeout") { - config.ospd.read_timeout = Some(Duration::from_secs(*interval)); + config.wrapper.ospd.read_timeout = Some(Duration::from_secs(*interval)); } if let Some(path) = cmds.get_one::("feed-path") { @@ -475,9 +575,15 @@ mod tests { assert!(config.tls.key.is_none()); assert!(config.tls.client_certs.is_none()); - assert_eq!(config.ospd.result_check_interval, Duration::from_secs(1)); - assert_eq!(config.ospd.socket, PathBuf::from("/var/run/ospd/ospd.sock")); - assert!(config.ospd.read_timeout.is_none()); + assert_eq!( + config.wrapper.ospd.result_check_interval, + Duration::from_secs(1) + ); + assert_eq!( + config.wrapper.ospd.socket, + PathBuf::from("/var/run/ospd/ospd.sock") + ); + assert!(config.wrapper.ospd.read_timeout.is_none()); assert_eq!(config.listener.address, ([127, 0, 0, 1], 3000).into()); diff --git a/rust/openvasd/src/controller/context.rs b/rust/openvasd/src/controller/context.rs index 1335a680c..d5f3ab8e7 100644 --- a/rust/openvasd/src/controller/context.rs +++ b/rust/openvasd/src/controller/context.rs @@ -159,10 +159,7 @@ impl ContextBuilder { } } -impl ContextBuilder -where - S: Clone, -{ +impl ContextBuilder { /// Sets the scanner. This is required. pub fn scanner(self, scanner: S) -> ContextBuilder> where diff --git a/rust/openvasd/src/controller/mod.rs b/rust/openvasd/src/controller/mod.rs index 6479496df..c543583fd 100644 --- a/rust/openvasd/src/controller/mod.rs +++ b/rust/openvasd/src/controller/mod.rs @@ -505,7 +505,7 @@ mod tests { let controller = Arc::new(ctx); let resp = post_scan(&scan, Arc::clone(&controller)).await; - assert_eq!(resp.status(), 201); + assert_eq!(resp.status(), 201); let req: Request> = Request::builder() .uri("/scans") diff --git a/rust/openvasd/src/main.rs b/rust/openvasd/src/main.rs index b752909f5..b799f39a1 100644 --- a/rust/openvasd/src/main.rs +++ b/rust/openvasd/src/main.rs @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: GPL-2.0-or-later +use models::scanner::{ScanStarter, ScanStopper, ScanDeleter, ScanResultFetcher}; use ::notus::{loader::hashsum::HashsumProductLoader, notus::Notus}; use nasl_interpreter::FSPluginLoader; use notus::NotusWrapper; @@ -15,9 +16,23 @@ pub mod response; pub mod storage; pub mod tls; -fn create_context(db: DB, config: &config::Config) -> controller::Context { - let scanner = osp::Scanner::new(config.ospd.socket.clone(), config.ospd.read_timeout); - let rc = config.ospd.result_check_interval; +fn create_context( + db: DB, + sh: ScanHandler, + config: &config::Config, +) -> controller::Context +where + ScanHandler: ScanStarter + + ScanStopper + + ScanDeleter + + ScanResultFetcher + + std::fmt::Debug + + std::marker::Sync + + std::marker::Send + + 'static, +{ + // TODO: move result check interval into overall `wrapper` rename wrapper to something useful + let rc = config.wrapper.ospd.result_check_interval; let fc = ( config.feed.path.clone(), config.feed.check_interval, @@ -37,24 +52,27 @@ fn create_context(db: DB, config: &config::Config) -> controller::Context Result<(), Box> { - let config = config::Config::load(); - let filter = tracing_subscriber::EnvFilter::builder() - .with_default_directive(tracing::metadata::LevelFilter::INFO.into()) - .parse_lossy(format!("{},rustls=info,h2=info", &config.log.level)); - tracing::debug!("config: {:?}", config); - tracing_subscriber::fmt().with_env_filter(filter).init(); - if !config.ospd.socket.exists() { - tracing::warn!("OSPD socket {} does not exist. Some commands will not work until the socket is created!", config.ospd.socket.display()); - } +async fn run( + scanner: S, + config: &config::Config, +) -> Result<(), Box> +where + S: ScanStarter + + ScanStopper + + ScanDeleter + + ScanResultFetcher + + std::fmt::Debug + + std::marker::Sync + + std::marker::Send + + 'static, +{ match config.storage.storage_type { config::StorageType::Redis => { tracing::info!("using in redis {}", config.storage.redis.url); @@ -71,13 +89,13 @@ async fn main() -> Result<(), Box> { &config.feed.path, &config.notus.advisories_path, ), + scanner, &config, ); controller::run(ctx, &config).await } config::StorageType::InMemory => { tracing::info!("using in memory store. No sensitive data will be stored on disk."); - // Self::new(crate::crypt::ChaCha20Crypt::default(), "/var/lib/openvas/feed".to_string()) let ctx = create_context( storage::inmemory::Storage::new( @@ -85,6 +103,7 @@ async fn main() -> Result<(), Box> { &config.feed.path, &config.notus.advisories_path, ), + scanner, &config, ); controller::run(ctx, &config).await @@ -102,6 +121,7 @@ async fn main() -> Result<(), Box> { &config.feed.path, &config.notus.advisories_path, )?, + scanner, &config, ); controller::run(ctx, &config).await @@ -115,6 +135,7 @@ async fn main() -> Result<(), Box> { &config.feed.path, &config.notus.advisories_path, )?, + scanner, &config, ); controller::run(ctx, &config).await @@ -122,3 +143,39 @@ async fn main() -> Result<(), Box> { } } } + +#[tokio::main] +async fn main() -> Result<(), Box> { + let config = config::Config::load(); + let filter = tracing_subscriber::EnvFilter::builder() + .with_default_directive(tracing::metadata::LevelFilter::INFO.into()) + .parse_lossy(format!("{},rustls=info,h2=info", &config.log.level)); + tracing::debug!("config: {:?}", config); + tracing_subscriber::fmt().with_env_filter(filter).init(); + if !config.wrapper.ospd.socket.exists() { + tracing::warn!("OSPD socket {} does not exist. Some commands will not work until the socket is created!", config.wrapper.ospd.socket.display()); + } + // TODO verify which one and create + + match config.wrapper.wrapper_type { + config::WrapperType::OSPD => { + run( + osp::Scanner::new( + config.wrapper.ospd.socket.clone(), + config.wrapper.ospd.read_timeout, + ), + &config, + ) + .await + } + config::WrapperType::Openvasctl => { + todo!("need a openvas impl for scanner traits") + // run( + // scan::OpenvasctlWrapper::new(config.wrapper.openvasctl.clone()), + // &config, + // ) + // .await + } + } + // extract to method +} diff --git a/rust/openvasd/src/scan.rs b/rust/openvasd/src/scan.rs new file mode 100644 index 000000000..269714eb0 --- /dev/null +++ b/rust/openvasd/src/scan.rs @@ -0,0 +1,238 @@ +// SPDX-FileCopyrightText: 2023 Greenbone AG +// +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{path::PathBuf, sync::PoisonError, time::Duration}; + +use async_trait::async_trait; +use futures_util::lock::Mutex; +use openvasctl::{openvas::OpenvasControl, scheduler::Scheduler}; + +/// The result of a fetch operation +pub type FetchResult = (models::Status, Vec); + +impl From for Error { + fn from(value: osp::Error) -> Self { + Self::Unexpected(format!("{value:?}")) + } +} + +#[derive(Debug, Clone)] +/// OSPD wrapper, is used to utilize ospd +pub struct OSPDWrapper { + /// Path to the socket + socket: PathBuf, + /// Read timeout in seconds + r_timeout: Option, +} + +#[derive(Debug)] +pub enum Error { + Unexpected(String), + SocketDoesNotExist(PathBuf), + Poisoned, +} + +impl std::error::Error for Error {} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::SocketDoesNotExist(p) => { + write!(f, "The OSPD socket {} does not exist", p.display()) + } + _ => write!(f, "{:?}", self), + } + } +} + +impl From for Error { + fn from(value: crate::storage::Error) -> Self { + Self::Unexpected(format!("{value:?}")) + } +} +impl From> for Error { + fn from(_: PoisonError) -> Self { + Self::Poisoned + } +} + +impl OSPDWrapper { + /// Creates a new instance of OSPDWrapper + pub fn new(socket: PathBuf, r_timeout: Option) -> Self { + Self { socket, r_timeout } + } + + fn check_socket(&self) -> Result { + if !self.socket.exists() { + return Err(Error::SocketDoesNotExist(self.socket.clone())); + } + Ok(self.socket.clone()) + } + async fn spawn_blocking(&self, f: F) -> Result + where + F: FnOnce(PathBuf) -> Result + Send + 'static, + R: Send + 'static, + E: Into + Send + 'static, + { + let socket = self.check_socket()?; + tokio::task::spawn_blocking(move || f(socket).map_err(Into::into)) + .await + .map_err(|_| Error::Poisoned)? + } +} + +/// Starts a scan +#[async_trait] +pub trait ScanStarter { + /// Starts a scan + async fn start_scan(&self, scan: models::Scan) -> Result<(), Error>; +} + +/// Stops a scan +#[async_trait] +pub trait ScanStopper { + /// Stops a scan + async fn stop_scan(&self, id: I) -> Result<(), Error> + where + I: AsRef + Send + 'static; +} + +/// Deletes a scan +#[async_trait] +pub trait ScanDeleter { + async fn delete_scan(&self, id: I) -> Result<(), Error> + where + I: AsRef + Send + 'static; +} + +#[async_trait] +pub trait ScanResultFetcher { + /// Fetches the results of a scan and combines the results with response + async fn fetch_results(&self, id: I) -> Result + where + I: AsRef + Send + 'static; +} + + +#[async_trait] +impl ScanStarter for OSPDWrapper { + async fn start_scan(&self, scan: models::Scan) -> Result<(), Error> { + let rtimeout = self.r_timeout; + self.spawn_blocking(move |socket| { + osp::start_scan(socket, rtimeout, &scan) + .map(|_| ()) + .map_err(Error::from) + }) + .await + } +} + +#[async_trait] +impl ScanStopper for OSPDWrapper { + async fn stop_scan(&self, id: I) -> Result<(), Error> + where + I: AsRef + Send + 'static, + { + let rtimeout = self.r_timeout; + self.spawn_blocking(move |socket| { + osp::stop_scan(socket, rtimeout, id) + .map(|_| ()) + .map_err(Error::from) + }) + .await + } +} + +#[async_trait] +impl ScanDeleter for OSPDWrapper { + async fn delete_scan(&self, id: I) -> Result<(), Error> + where + I: AsRef + Send + 'static, + { + let rtimeout = self.r_timeout; + self.spawn_blocking(move |socket| { + osp::delete_scan(socket, rtimeout, id) + .map(|_| ()) + .map_err(Error::from) + }) + .await + } +} + +#[async_trait] +impl ScanResultFetcher for OSPDWrapper { + async fn fetch_results(&self, id: I) -> Result + where + I: AsRef + Send + 'static, + { + let rtimeout = self.r_timeout; + self.spawn_blocking(move |socket| { + osp::get_delete_scan_results(socket, rtimeout, id) + .map(|r| (r.clone().into(), r.into())) + .map_err(Error::from) + }) + .await + } +} + +#[derive(Debug)] +pub struct OpenvasctlWrapper { + scheduler: Mutex>, +} + +impl OpenvasctlWrapper { + pub fn new(config: openvasctl::config::Config) -> Self { + let controller = OpenvasControl::new(); + Self { + scheduler: Mutex::new(Scheduler::new(config, controller)), + } + } +} + +#[async_trait] +impl ScanStarter for OpenvasctlWrapper { + async fn start_scan(&self, scan: models::Scan) -> Result<(), Error> { + self.scheduler + .lock() + .await + .add(scan) + .await + .map_err(|e| Error::Unexpected(e.to_string())) + } +} + +#[async_trait] +impl ScanStopper for OpenvasctlWrapper { + async fn stop_scan(&self, id: I) -> Result<(), Error> + where + I: AsRef + Send + 'static, + { + self.scheduler + .lock() + .await + .stop(id.as_ref()) + .await + .map_err(|e| Error::Unexpected(e.to_string())) + } +} + +#[async_trait] +impl ScanDeleter for OpenvasctlWrapper { + async fn delete_scan(&self, _: I) -> Result<(), Error> + where + I: AsRef + Send + 'static, + { + unimplemented!() + } +} + +#[async_trait] +impl ScanResultFetcher for OpenvasctlWrapper { + async fn fetch_results(&self, _: I) -> Result + where + I: AsRef + Send + 'static, + { + unimplemented!() + } +} diff --git a/rust/openvasd/src/storage/file.rs b/rust/openvasd/src/storage/file.rs index bee7b2b3d..fa7858d92 100644 --- a/rust/openvasd/src/storage/file.rs +++ b/rust/openvasd/src/storage/file.rs @@ -624,7 +624,9 @@ mod tests { } for s in scans.clone().into_iter() { - storage.get_scan(&s.scan_id).await.unwrap(); + let r = storage.get_scan(&s.scan_id).await; + dbg!(&r); + r.unwrap(); } storage.remove_scan("5").await.unwrap(); storage.insert_scan(scans[5].clone()).await.unwrap(); diff --git a/rust/osp/src/commands.rs b/rust/osp/src/commands.rs index 7ca1d3e45..9a7c9c7e5 100644 --- a/rust/osp/src/commands.rs +++ b/rust/osp/src/commands.rs @@ -408,7 +408,7 @@ mod tests { // of the same protocol. // ports that have no assigned protocol are in the front let expected = r#" - + @@ -425,6 +425,7 @@ mod tests { "#; + let expected = expected.replace("replace_me", &scan.scan_id); let expected = expected .trim() .lines()