From cdb294a365d006b5e84c3b3b781bef14bb9b83fc Mon Sep 17 00:00:00 2001 From: Adir Shitrit Date: Sat, 8 Nov 2025 12:43:11 +0200 Subject: [PATCH] Add configuration file support to CLI --- ghost-cli/src/main.rs | 38 ++++++++++++++++++++++++++++++++++--- ghost-core/src/detection.rs | 12 +++++++++--- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/ghost-cli/src/main.rs b/ghost-cli/src/main.rs index 0bdeb61..d37809c 100644 --- a/ghost-cli/src/main.rs +++ b/ghost-cli/src/main.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::{Arg, Command}; -use ghost_core::{memory, process, thread, DetectionEngine, ThreatLevel}; +use ghost_core::{memory, process, thread, DetectionEngine, DetectionConfig, ThreatLevel}; use log::{debug, error, info, warn}; use serde_json; use std::time::Instant; @@ -64,6 +64,13 @@ fn main() -> Result<()> { .action(clap::ArgAction::SetTrue) .help("Suppress all output except errors"), ) + .arg( + Arg::new("config") + .short('c') + .long("config") + .value_name("FILE") + .help("Load configuration from file"), + ) .get_matches(); // Initialize logging based on debug flag @@ -84,16 +91,41 @@ fn main() -> Result<()> { let target_pid = matches.get_one::("pid"); let target_process = matches.get_one::("process"); let output_file = matches.get_one::("output"); + let config_file = matches.get_one::("config"); + + // Load configuration if specified + let config = if let Some(config_path) = config_file { + info!("Loading configuration from: {}", config_path); + match DetectionConfig::load(config_path) { + Ok(cfg) => { + debug!("Configuration loaded successfully"); + Some(cfg) + } + Err(e) => { + error!("Failed to load configuration from {}: {}", config_path, e); + if !quiet { + eprintln!("Error: Failed to load configuration: {}", e); + } + return Err(e.into()); + } + } + } else { + None + }; info!("Starting Ghost process injection detection"); - debug!("Configuration - Format: {}, Verbose: {}, Quiet: {}, Target PID: {:?}, Target Process: {:?}", format, verbose, quiet, target_pid, target_process); + debug!("Configuration - Format: {}, Verbose: {}, Quiet: {}, Target PID: {:?}, Target Process: {:?}, Config: {:?}", + format, verbose, quiet, target_pid, target_process, config_file); if !quiet { println!("Ghost v0.1.0 - Process Injection Detection\n"); } let scan_start = Instant::now(); - let mut engine = DetectionEngine::new(); + let mut engine = DetectionEngine::with_config(config).map_err(|e| { + error!("Failed to initialize detection engine: {}", e); + anyhow::anyhow!("Detection engine initialization failed: {}", e) + })?; let processes = if let Some(pid_str) = target_pid { let pid: u32 = pid_str.parse().map_err(|e| { diff --git a/ghost-core/src/detection.rs b/ghost-core/src/detection.rs index fb80552..b19162c 100644 --- a/ghost-core/src/detection.rs +++ b/ghost-core/src/detection.rs @@ -1,7 +1,7 @@ use crate::{ detect_hook_injection, AnomalyDetector, MemoryProtection, MemoryRegion, ProcessInfo, ShellcodeDetector, ThreadInfo, ThreatIntelligence, ThreatContext, - EvasionDetector, EvasionResult + EvasionDetector, EvasionResult, DetectionConfig, GhostError }; #[cfg(target_os = "linux")] use crate::EbpfDetector; @@ -32,6 +32,7 @@ pub struct DetectionEngine { anomaly_detector: AnomalyDetector, threat_intelligence: ThreatIntelligence, evasion_detector: EvasionDetector, + config: Option, #[cfg(target_os = "linux")] ebpf_detector: Option, } @@ -43,7 +44,11 @@ struct ProcessBaseline { } impl DetectionEngine { - pub fn new() -> Result { + pub fn new() -> Result { + Self::with_config(None) + } + + pub fn with_config(config: Option) -> Result { let baseline = ProcessBaseline::new(); let shellcode_detector = ShellcodeDetector::new(); let hollowing_detector = HollowingDetector::new(); @@ -74,6 +79,7 @@ impl DetectionEngine { anomaly_detector, threat_intelligence, evasion_detector, + config, #[cfg(target_os = "linux")] ebpf_detector, }) @@ -304,7 +310,7 @@ impl DetectionEngine { /// Process eBPF detection events (Linux only) #[cfg(target_os = "linux")] - pub fn process_ebpf_events(&mut self) -> Result, DetectionError> { + pub fn process_ebpf_events(&mut self) -> Result, GhostError> { if let Some(ref mut ebpf_detector) = self.ebpf_detector { match ebpf_detector.process_events() { Ok(ebpf_events) => {