diff --git a/Cargo.toml b/Cargo.toml index 89762ab..15beab5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,6 @@ members = [ "crates/scanr-sca", "crates/scanr-cli", "crates/scanr-engine", + "crates/scanr-container", ] resolver = "2" diff --git a/crates/scanr-cli/Cargo.toml b/crates/scanr-cli/Cargo.toml index aee25b1..6d7c292 100644 --- a/crates/scanr-cli/Cargo.toml +++ b/crates/scanr-cli/Cargo.toml @@ -14,6 +14,7 @@ categories = ["command-line-utilities", "development-tools"] clap = { version = "4.5", features = ["derive"] } crossterm = "0.28" ratatui = "0.28" +scanr-container = { version = "0.1.1", path = "../scanr-container" } scanr-engine = { version = "0.1.1", path = "../scanr-engine" } scanr-sca = { version = "0.1.1", path = "../scanr-sca" } serde = { version = "1.0", features = ["derive"] } diff --git a/crates/scanr-cli/src/main.rs b/crates/scanr-cli/src/main.rs index a99cb52..f7c969c 100644 --- a/crates/scanr-cli/src/main.rs +++ b/crates/scanr-cli/src/main.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use std::process; use clap::{Parser, Subcommand}; +use scanr_engine::ScanEngine; use serde::Serialize; mod tui; @@ -53,6 +54,11 @@ enum Commands { #[arg(short, long)] recursive: bool, }, + /// Scan a container image target (engine skeleton). + Image { + /// Image target (for example: ghcr.io/org/app:latest). + target: String, + }, /// Software bill of materials operations. Sbom { #[command(subcommand)] @@ -558,6 +564,22 @@ async fn main() { process::exit(final_ci_exit_code); } } + Some(Commands::Image { target }) => { + let container_engine = scanr_container::ContainerEngine::new(); + let result = match container_engine.scan(scanr_engine::ScanInput::Image(target)) { + Ok(result) => result, + Err(error) => { + eprintln!("Image scan failed: {error}"); + process::exit(1); + } + }; + + println!("Scanr Container Scan"); + println!("Engine: {}", result.metadata.engine_name); + println!("Target: {}", result.metadata.target); + println!("Status: placeholder implementation (C1 skeleton)"); + println!("Findings: {}", result.findings.len()); + } Some(Commands::Sbom { command }) => match command { SbomCommands::Generate { path, output } => { match scanr_sca::generate_cyclonedx_sbom(&path) { diff --git a/crates/scanr-container/Cargo.toml b/crates/scanr-container/Cargo.toml new file mode 100644 index 0000000..afe8f54 --- /dev/null +++ b/crates/scanr-container/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "scanr-container" +version = "0.1.1" +edition = "2024" +description = "Container security engine for Scanr" +license = "Apache-2.0" +repository = "https://github.com/Open-Lab-s/Scanr" +homepage = "https://scanr.dev" +readme = "../../README.md" +keywords = ["security", "devsecops", "container", "vulnerability", "scanr"] +categories = ["development-tools"] + +[dependencies] +scanr-engine = { version = "0.1.1", path = "../scanr-engine" } +scanr-sca = { version = "0.1.1", path = "../scanr-sca" } diff --git a/crates/scanr-container/src/lib.rs b/crates/scanr-container/src/lib.rs new file mode 100644 index 0000000..197bc63 --- /dev/null +++ b/crates/scanr-container/src/lib.rs @@ -0,0 +1,41 @@ +use scanr_engine::{EngineType, ScanEngine, ScanInput, ScanMetadata, ScanResult}; +use scanr_sca::ScaEngine; + +#[derive(Debug, Default, Clone)] +pub struct ContainerEngine { + pub sca_engine: ScaEngine, +} + +impl ContainerEngine { + pub fn new() -> Self { + Self { + sca_engine: ScaEngine::new(), + } + } +} + +impl ScanEngine for ContainerEngine { + fn name(&self) -> &'static str { + "container" + } + + fn scan(&self, input: ScanInput) -> scanr_engine::EngineResult { + let target = match input { + ScanInput::Image(image) => image, + ScanInput::Tar(path) | ScanInput::Path(path) => path.display().to_string(), + }; + + let _ = &self.sca_engine; + + Ok(ScanResult { + findings: Vec::new(), + metadata: ScanMetadata { + engine: EngineType::Container, + engine_name: self.name().to_string(), + target, + total_dependencies: 0, + total_vulnerabilities: 0, + }, + }) + } +}