refactor: comprehensive codebase improvements and documentation

- Enhanced error handling with expanded GhostError variants and From impls
- Fixed race conditions in TUI (ui.rs unwrap calls)
- Added comprehensive module documentation with doc comments
- Improved type safety with proper validation in DetectionConfig
- Implemented Linux process enumeration via procfs
- Refactored TUI for better state management and removed emojis
- Enhanced CLI with proper logging initialization
- Added example configuration file (examples/ghost.toml)
- Updated README with complete feature documentation
- Added performance optimizations (saturating arithmetic, reduced clones)
- Improved testing framework with proper struct initialization
- Added validation and preset modes to DetectionConfig
This commit is contained in:
pandaadir05
2025-11-17 21:28:37 +02:00
parent 9ef666ba9d
commit 96b0d12099
14 changed files with 879 additions and 236 deletions

View File

@@ -1,15 +1,44 @@
//! Process enumeration and information retrieval.
//!
//! This module provides cross-platform process enumeration capabilities,
//! allowing the detection engine to gather information about running processes.
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Serialize, Deserialize)]
/// Information about a running process.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ProcessInfo {
/// Process identifier.
pub pid: u32,
/// Parent process identifier.
pub ppid: u32,
/// Process name (executable name).
pub name: String,
/// Full path to the executable, if available.
pub path: Option<String>,
/// Number of threads in the process.
pub thread_count: u32,
}
impl ProcessInfo {
/// Creates a new ProcessInfo instance.
pub fn new(pid: u32, ppid: u32, name: String) -> Self {
Self {
pid,
ppid,
name,
path: None,
thread_count: 1,
}
}
/// Returns true if this is likely a system process.
pub fn is_system_process(&self) -> bool {
self.pid == 0 || self.pid == 4 || self.name == "System" || self.name == "Idle"
}
}
impl fmt::Display for ProcessInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}] {}", self.pid, self.name)
@@ -92,17 +121,119 @@ mod platform {
}
}
#[cfg(not(windows))]
#[cfg(target_os = "linux")]
mod platform {
use super::ProcessInfo;
use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
pub fn enumerate_processes() -> Result<Vec<ProcessInfo>> {
let mut processes = Vec::new();
let proc_dir = Path::new("/proc");
if !proc_dir.exists() {
return Err(anyhow::anyhow!("procfs not available"));
}
for entry in fs::read_dir(proc_dir).context("Failed to read /proc directory")? {
let entry = match entry {
Ok(e) => e,
Err(_) => continue,
};
let file_name = entry.file_name();
let pid_str = match file_name.to_str() {
Some(s) => s,
None => continue,
};
let pid: u32 = match pid_str.parse() {
Ok(p) => p,
Err(_) => continue,
};
if let Ok(info) = get_process_info(pid) {
processes.push(info);
}
}
Ok(processes)
}
fn get_process_info(pid: u32) -> Result<ProcessInfo> {
let stat_path = format!("/proc/{}/stat", pid);
let stat_content =
fs::read_to_string(&stat_path).context("Failed to read process stat")?;
let (name, ppid, thread_count) = parse_stat(&stat_content)?;
let exe_path = format!("/proc/{}/exe", pid);
let path = fs::read_link(&exe_path).ok().map(|p| p.to_string_lossy().into_owned());
Ok(ProcessInfo {
pid,
ppid,
name,
path,
thread_count,
})
}
fn parse_stat(stat: &str) -> Result<(String, u32, u32)> {
let open_paren = stat.find('(').context("Invalid stat format")?;
let close_paren = stat.rfind(')').context("Invalid stat format")?;
let name = stat[open_paren + 1..close_paren].to_string();
let rest = &stat[close_paren + 2..];
let fields: Vec<&str> = rest.split_whitespace().collect();
if fields.len() < 18 {
return Err(anyhow::anyhow!("Insufficient fields in stat"));
}
let ppid: u32 = fields[1].parse().context("Failed to parse PPID")?;
let thread_count: u32 = fields[17].parse().unwrap_or(1);
Ok((name, ppid, thread_count))
}
}
#[cfg(target_os = "macos")]
mod platform {
use super::ProcessInfo;
use anyhow::Result;
pub fn enumerate_processes() -> Result<Vec<ProcessInfo>> {
// TODO: Implement Linux/macOS enumeration
// macOS implementation would use libproc or sysctl
// For now, return empty to indicate platform support is partial
log::warn!("macOS process enumeration not yet fully implemented");
Ok(Vec::new())
}
}
#[cfg(not(any(windows, target_os = "linux", target_os = "macos")))]
mod platform {
use super::ProcessInfo;
use anyhow::Result;
pub fn enumerate_processes() -> Result<Vec<ProcessInfo>> {
Err(anyhow::anyhow!("Process enumeration not supported on this platform"))
}
}
/// Enumerates all running processes on the system.
///
/// # Platform Support
///
/// - **Windows**: Uses the ToolHelp API to enumerate processes.
/// - **Linux**: Reads from the /proc filesystem.
/// - **macOS**: Partial support (not yet implemented).
///
/// # Errors
///
/// Returns an error if process enumeration fails due to insufficient
/// privileges or platform limitations.
pub fn enumerate_processes() -> anyhow::Result<Vec<ProcessInfo>> {
platform::enumerate_processes()
}