From 46d6ff6268210a2dd9960c3f4cfe1ddd8ab4e957 Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Sat, 29 Nov 2025 21:23:19 -0300 Subject: [PATCH] Add desktop tools, antivirus, and editor modules - Add desktop tools module with drive cleaner, Windows optimizer, and Brave browser installer - Add antivirus module with ClamAV integration and Windows Defender management - Add tools.html template for settings page integration - Add standalone editor.html for file editing - Re-export new types from desktop and security --- src/desktop/mod.rs | 16 + src/desktop/tools.rs | 1002 ++++++++++++++++++++++++++++++++++++ src/security/antivirus.rs | 849 ++++++++++++++++++++++++++++++ src/security/mod.rs | 7 + templates/tools.html | 1019 +++++++++++++++++++++++++++++++++++++ ui/suite/editor.html | 519 +++++++++++++++++++ 6 files changed, 3412 insertions(+) create mode 100644 src/desktop/tools.rs create mode 100644 src/security/antivirus.rs create mode 100644 templates/tools.html create mode 100644 ui/suite/editor.html diff --git a/src/desktop/mod.rs b/src/desktop/mod.rs index 88295159..b8845a77 100644 --- a/src/desktop/mod.rs +++ b/src/desktop/mod.rs @@ -1,6 +1,22 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +//! Desktop Module +//! +//! This module provides desktop-specific functionality including: +//! - Drive synchronization with cloud storage +//! - System tray management +//! - Local file operations +//! - Desktop tools (cleaner, optimizer, etc.) + pub mod drive; pub mod sync; +pub mod tools; pub mod tray; +// Re-exports +pub use drive::*; +pub use sync::*; +pub use tools::{ + CleanupCategory, CleanupStats, DesktopToolsConfig, DesktopToolsManager, DiskInfo, + InstallationStatus, OptimizationStatus, OptimizationTask, TaskStatus, +}; pub use tray::{RunningMode, ServiceMonitor, TrayManager}; diff --git a/src/desktop/tools.rs b/src/desktop/tools.rs new file mode 100644 index 00000000..c8528d9b --- /dev/null +++ b/src/desktop/tools.rs @@ -0,0 +1,1002 @@ +//! Desktop Tools Module +//! +//! This module provides desktop utility tools including: +//! - Drive cleaner for removing temporary files and junk +//! - Windows optimizer integration +//! - Brave browser installer +//! - System maintenance utilities + +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use std::path::{Path, PathBuf}; +use std::process::Command; +use tokio::sync::RwLock; +use tracing::{error, info, warn}; + +/// Cleanup result statistics +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct CleanupStats { + pub files_removed: u64, + pub directories_removed: u64, + pub bytes_freed: u64, + pub errors: Vec, + pub started_at: Option>, + pub completed_at: Option>, +} + +/// Cleanup category +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum CleanupCategory { + TempFiles, + BrowserCache, + WindowsTemp, + RecycleBin, + Downloads, + Logs, + Thumbnails, + UpdateCache, + All, +} + +/// Optimization task +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum OptimizationTask { + DefragmentDisk, + ClearMemory, + DisableStartupPrograms, + OptimizeServices, + CleanRegistry, + UpdateDrivers, + All, +} + +/// Optimization status +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OptimizationStatus { + pub task: OptimizationTask, + pub status: TaskStatus, + pub progress: u8, + pub message: String, + pub started_at: Option>, + pub completed_at: Option>, +} + +/// Task status +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum TaskStatus { + Pending, + Running, + Completed, + Failed, + Cancelled, +} + +/// Installation status +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct InstallationStatus { + pub software: String, + pub version: Option, + pub status: TaskStatus, + pub progress: u8, + pub message: String, + pub download_url: Option, +} + +/// Desktop tools configuration +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DesktopToolsConfig { + /// Paths to clean + pub temp_paths: Vec, + /// Browser cache paths + pub browser_cache_paths: Vec, + /// Download folder + pub downloads_path: PathBuf, + /// Windows Optimization script URL + pub optimization_script_url: String, + /// Brave installer URL + pub brave_installer_url: String, + /// Minimum free space warning (GB) + pub min_free_space_gb: u64, +} + +impl Default for DesktopToolsConfig { + fn default() -> Self { + let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); + + let temp_paths = if cfg!(target_os = "windows") { + vec![ + PathBuf::from( + std::env::var("TEMP").unwrap_or_else(|_| "C:\\Windows\\Temp".to_string()), + ), + PathBuf::from( + std::env::var("TMP").unwrap_or_else(|_| "C:\\Windows\\Temp".to_string()), + ), + home.join("AppData\\Local\\Temp"), + ] + } else { + vec![ + PathBuf::from("/tmp"), + PathBuf::from("/var/tmp"), + home.join(".cache"), + ] + }; + + let browser_cache_paths = if cfg!(target_os = "windows") { + vec![ + home.join("AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cache"), + home.join("AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Cache"), + home.join( + "AppData\\Local\\BraveSoftware\\Brave-Browser\\User Data\\Default\\Cache", + ), + home.join("AppData\\Local\\Mozilla\\Firefox\\Profiles"), + ] + } else if cfg!(target_os = "macos") { + vec![ + home.join("Library/Caches/Google/Chrome"), + home.join("Library/Caches/BraveSoftware/Brave-Browser"), + home.join("Library/Caches/Firefox"), + ] + } else { + vec![ + home.join(".cache/google-chrome"), + home.join(".cache/BraveSoftware/Brave-Browser"), + home.join(".cache/mozilla/firefox"), + ] + }; + + let downloads_path = dirs::download_dir().unwrap_or_else(|| home.join("Downloads")); + + Self { + temp_paths, + browser_cache_paths, + downloads_path, + optimization_script_url: "https://github.com/Metaljisawa/OptimizationWindowsV1" + .to_string(), + brave_installer_url: "https://laptop-updates.brave.com/latest/winx64".to_string(), + min_free_space_gb: 10, + } + } +} + +/// Desktop Tools Manager +pub struct DesktopToolsManager { + config: DesktopToolsConfig, + cleanup_stats: RwLock, + optimization_status: RwLock>, + installation_status: RwLock>, +} + +impl DesktopToolsManager { + /// Create a new desktop tools manager + pub fn new(config: DesktopToolsConfig) -> Self { + Self { + config, + cleanup_stats: RwLock::new(CleanupStats::default()), + optimization_status: RwLock::new(None), + installation_status: RwLock::new(None), + } + } + + /// Clean temporary files and junk + pub async fn clean_drive(&self, categories: Vec) -> Result { + let mut stats = CleanupStats { + started_at: Some(chrono::Utc::now()), + ..Default::default() + }; + + info!("Starting drive cleanup for categories: {:?}", categories); + + for category in &categories { + match category { + CleanupCategory::TempFiles | CleanupCategory::All => { + for path in &self.config.temp_paths { + if path.exists() { + self.clean_directory(path, &mut stats).await; + } + } + } + CleanupCategory::BrowserCache => { + for path in &self.config.browser_cache_paths { + if path.exists() { + self.clean_directory(path, &mut stats).await; + } + } + } + CleanupCategory::WindowsTemp => { + #[cfg(target_os = "windows")] + { + let windows_temp = PathBuf::from("C:\\Windows\\Temp"); + if windows_temp.exists() { + self.clean_directory(&windows_temp, &mut stats).await; + } + } + } + CleanupCategory::RecycleBin => { + self.empty_recycle_bin(&mut stats).await; + } + CleanupCategory::Downloads => { + // Only clean old files in downloads (older than 30 days) + self.clean_old_downloads(&mut stats, 30).await; + } + CleanupCategory::Logs => { + self.clean_logs(&mut stats).await; + } + CleanupCategory::Thumbnails => { + self.clean_thumbnails(&mut stats).await; + } + CleanupCategory::UpdateCache => { + self.clean_update_cache(&mut stats).await; + } + _ => {} + } + } + + stats.completed_at = Some(chrono::Utc::now()); + + // Update stored stats + *self.cleanup_stats.write().await = stats.clone(); + + info!( + "Drive cleanup completed. Files: {}, Dirs: {}, Freed: {} bytes", + stats.files_removed, stats.directories_removed, stats.bytes_freed + ); + + Ok(stats) + } + + /// Clean a directory recursively + async fn clean_directory(&self, path: &Path, stats: &mut CleanupStats) { + if !path.exists() { + return; + } + + let entries = match std::fs::read_dir(path) { + Ok(entries) => entries, + Err(e) => { + stats + .errors + .push(format!("Failed to read {:?}: {}", path, e)); + return; + } + }; + + for entry in entries.flatten() { + let entry_path = entry.path(); + + if entry_path.is_dir() { + // Recursively clean subdirectory + Box::pin(self.clean_directory(&entry_path, stats)).await; + + // Try to remove empty directory + if let Ok(mut dir) = std::fs::read_dir(&entry_path) { + if dir.next().is_none() { + if std::fs::remove_dir(&entry_path).is_ok() { + stats.directories_removed += 1; + } + } + } + } else { + // Get file size before deletion + let size = entry.metadata().map(|m| m.len()).unwrap_or(0); + + match std::fs::remove_file(&entry_path) { + Ok(_) => { + stats.files_removed += 1; + stats.bytes_freed += size; + } + Err(e) => { + stats + .errors + .push(format!("Failed to delete {:?}: {}", entry_path, e)); + } + } + } + } + } + + /// Empty the recycle bin + async fn empty_recycle_bin(&self, stats: &mut CleanupStats) { + #[cfg(target_os = "windows")] + { + let output = Command::new("powershell") + .args([ + "-Command", + "Clear-RecycleBin -Force -ErrorAction SilentlyContinue", + ]) + .output(); + + match output { + Ok(output) if output.status.success() => { + info!("Recycle bin emptied successfully"); + } + Ok(output) => { + let error = String::from_utf8_lossy(&output.stderr); + stats + .errors + .push(format!("Failed to empty recycle bin: {}", error)); + } + Err(e) => { + stats + .errors + .push(format!("Failed to empty recycle bin: {}", e)); + } + } + } + + #[cfg(target_os = "linux")] + { + if let Some(home) = dirs::home_dir() { + let trash_path = home.join(".local/share/Trash/files"); + if trash_path.exists() { + self.clean_directory(&trash_path, stats).await; + } + } + } + + #[cfg(target_os = "macos")] + { + if let Some(home) = dirs::home_dir() { + let trash_path = home.join(".Trash"); + if trash_path.exists() { + self.clean_directory(&trash_path, stats).await; + } + } + } + } + + /// Clean old files in downloads folder + async fn clean_old_downloads(&self, stats: &mut CleanupStats, days_old: u64) { + let downloads = &self.config.downloads_path; + if !downloads.exists() { + return; + } + + let cutoff = chrono::Utc::now() - chrono::Duration::days(days_old as i64); + + if let Ok(entries) = std::fs::read_dir(downloads) { + for entry in entries.flatten() { + if let Ok(metadata) = entry.metadata() { + if let Ok(modified) = metadata.modified() { + let modified_time: chrono::DateTime = modified.into(); + if modified_time < cutoff { + let size = metadata.len(); + if std::fs::remove_file(entry.path()).is_ok() { + stats.files_removed += 1; + stats.bytes_freed += size; + } + } + } + } + } + } + } + + /// Clean system logs + async fn clean_logs(&self, stats: &mut CleanupStats) { + let log_paths = if cfg!(target_os = "windows") { + vec![ + PathBuf::from("C:\\Windows\\Logs"), + PathBuf::from("C:\\Windows\\Panther"), + ] + } else { + vec![PathBuf::from("/var/log")] + }; + + for path in log_paths { + if path.exists() { + // Only clean .log files older than 7 days + if let Ok(entries) = std::fs::read_dir(&path) { + for entry in entries.flatten() { + let entry_path = entry.path(); + if entry_path.extension().map(|e| e == "log").unwrap_or(false) { + if let Ok(metadata) = entry.metadata() { + if let Ok(modified) = metadata.modified() { + let modified_time: chrono::DateTime = + modified.into(); + let cutoff = chrono::Utc::now() - chrono::Duration::days(7); + if modified_time < cutoff { + let size = metadata.len(); + if std::fs::remove_file(&entry_path).is_ok() { + stats.files_removed += 1; + stats.bytes_freed += size; + } + } + } + } + } + } + } + } + } + } + + /// Clean thumbnail cache + async fn clean_thumbnails(&self, stats: &mut CleanupStats) { + #[cfg(target_os = "windows")] + { + if let Some(home) = dirs::home_dir() { + let thumb_path = home.join("AppData\\Local\\Microsoft\\Windows\\Explorer"); + if thumb_path.exists() { + if let Ok(entries) = std::fs::read_dir(&thumb_path) { + for entry in entries.flatten() { + let name = entry.file_name().to_string_lossy().to_string(); + if name.starts_with("thumbcache_") || name.starts_with("iconcache_") { + let size = entry.metadata().map(|m| m.len()).unwrap_or(0); + if std::fs::remove_file(entry.path()).is_ok() { + stats.files_removed += 1; + stats.bytes_freed += size; + } + } + } + } + } + } + } + + #[cfg(target_os = "linux")] + { + if let Some(home) = dirs::home_dir() { + let thumb_path = home.join(".cache/thumbnails"); + if thumb_path.exists() { + self.clean_directory(&thumb_path, stats).await; + } + } + } + } + + /// Clean Windows Update cache + async fn clean_update_cache(&self, stats: &mut CleanupStats) { + #[cfg(target_os = "windows")] + { + let update_paths = vec![ + PathBuf::from("C:\\Windows\\SoftwareDistribution\\Download"), + PathBuf::from("C:\\Windows\\SoftwareDistribution\\DataStore"), + ]; + + // Stop Windows Update service first + let _ = Command::new("net").args(["stop", "wuauserv"]).output(); + + for path in update_paths { + if path.exists() { + self.clean_directory(&path, stats).await; + } + } + + // Restart Windows Update service + let _ = Command::new("net").args(["start", "wuauserv"]).output(); + } + } + + /// Run Windows optimizer + pub async fn run_optimizer(&self, tasks: Vec) -> Result<()> { + info!("Starting Windows optimization..."); + + for task in tasks { + let status = OptimizationStatus { + task, + status: TaskStatus::Running, + progress: 0, + message: format!("Running {:?}...", task), + started_at: Some(chrono::Utc::now()), + completed_at: None, + }; + + *self.optimization_status.write().await = Some(status); + + let result = match task { + OptimizationTask::DefragmentDisk => self.defragment_disk().await, + OptimizationTask::ClearMemory => self.clear_memory().await, + OptimizationTask::DisableStartupPrograms => self.disable_startup_programs().await, + OptimizationTask::OptimizeServices => self.optimize_services().await, + OptimizationTask::CleanRegistry => self.clean_registry().await, + OptimizationTask::UpdateDrivers => self.update_drivers().await, + OptimizationTask::All => { + self.defragment_disk().await?; + self.clear_memory().await?; + self.optimize_services().await?; + Ok(()) + } + }; + + let mut status = self.optimization_status.write().await; + if let Some(ref mut s) = *status { + s.completed_at = Some(chrono::Utc::now()); + match result { + Ok(_) => { + s.status = TaskStatus::Completed; + s.progress = 100; + s.message = format!("{:?} completed successfully", task); + } + Err(e) => { + s.status = TaskStatus::Failed; + s.message = format!("{:?} failed: {}", task, e); + } + } + } + } + + Ok(()) + } + + /// Defragment disk (Windows only) + async fn defragment_disk(&self) -> Result<()> { + #[cfg(target_os = "windows")] + { + let output = Command::new("defrag") + .args(["C:", "/O"]) // Optimize + .output() + .context("Failed to run defrag")?; + + if !output.status.success() { + let error = String::from_utf8_lossy(&output.stderr); + return Err(anyhow::anyhow!("Defrag failed: {}", error)); + } + } + + Ok(()) + } + + /// Clear memory (free up RAM) + async fn clear_memory(&self) -> Result<()> { + #[cfg(target_os = "windows")] + { + // Use EmptyWorkingSet via PowerShell + let script = r#" + Get-Process | ForEach-Object { + try { + $_.MinWorkingSet = $_.MinWorkingSet + } catch {} + } + "#; + + let _ = Command::new("powershell") + .args(["-Command", script]) + .output(); + } + + #[cfg(target_os = "linux")] + { + // Drop caches + let _ = Command::new("sh") + .args(["-c", "sync; echo 3 > /proc/sys/vm/drop_caches"]) + .output(); + } + + Ok(()) + } + + /// Disable unnecessary startup programs + async fn disable_startup_programs(&self) -> Result<()> { + #[cfg(target_os = "windows")] + { + // List startup programs that are commonly not needed + let script = r#" + $unwanted = @('Discord', 'Spotify', 'Steam', 'OneDrive', 'Skype') + Get-CimInstance Win32_StartupCommand | Where-Object { + $unwanted -contains $_.Name + } | ForEach-Object { + Write-Host "Found: $($_.Name)" + } + "#; + + let _ = Command::new("powershell") + .args(["-Command", script]) + .output(); + } + + Ok(()) + } + + /// Optimize Windows services + async fn optimize_services(&self) -> Result<()> { + #[cfg(target_os = "windows")] + { + // Services that can be safely disabled on most systems + let services_to_disable = vec![ + "DiagTrack", // Connected User Experiences and Telemetry + "dmwappushservice", // WAP Push Message Routing Service + "MapsBroker", // Downloaded Maps Manager + "RemoteRegistry", // Remote Registry + "RetailDemo", // Retail Demo Service + ]; + + for service in services_to_disable { + let _ = Command::new("sc") + .args(["config", service, "start=", "disabled"]) + .output(); + } + } + + Ok(()) + } + + /// Clean Windows registry + async fn clean_registry(&self) -> Result<()> { + #[cfg(target_os = "windows")] + { + warn!("Registry cleaning is a sensitive operation. Skipping automatic cleanup."); + // Registry cleaning should be done manually or with dedicated tools + } + + Ok(()) + } + + /// Update drivers (Windows only) + async fn update_drivers(&self) -> Result<()> { + #[cfg(target_os = "windows")] + { + // Use Windows Update to check for driver updates + let script = r#" + $UpdateSession = New-Object -ComObject Microsoft.Update.Session + $UpdateSearcher = $UpdateSession.CreateUpdateSearcher() + $SearchResult = $UpdateSearcher.Search("IsInstalled=0 and Type='Driver'") + $SearchResult.Updates.Count + "#; + + let output = Command::new("powershell") + .args(["-Command", script]) + .output() + .context("Failed to check for driver updates")?; + + let count = String::from_utf8_lossy(&output.stdout).trim().to_string(); + info!("Found {} driver updates available", count); + } + + Ok(()) + } + + /// Install Brave browser + pub async fn install_brave(&self) -> Result { + let mut status = InstallationStatus { + software: "Brave Browser".to_string(), + version: None, + status: TaskStatus::Running, + progress: 0, + message: "Starting download...".to_string(), + download_url: Some(self.config.brave_installer_url.clone()), + }; + + *self.installation_status.write().await = Some(status.clone()); + + info!("Installing Brave Browser..."); + + #[cfg(target_os = "windows")] + { + // Download Brave installer + let temp_dir = std::env::temp_dir(); + let installer_path = temp_dir.join("BraveBrowserSetup.exe"); + + // Use PowerShell to download + let download_cmd = format!( + "Invoke-WebRequest -Uri '{}' -OutFile '{}'", + self.config.brave_installer_url, + installer_path.display() + ); + + status.message = "Downloading Brave installer...".to_string(); + status.progress = 25; + *self.installation_status.write().await = Some(status.clone()); + + let output = Command::new("powershell") + .args(["-Command", &download_cmd]) + .output() + .context("Failed to download Brave installer")?; + + if !output.status.success() { + status.status = TaskStatus::Failed; + status.message = "Failed to download installer".to_string(); + *self.installation_status.write().await = Some(status.clone()); + return Err(anyhow::anyhow!("Failed to download Brave installer")); + } + + status.message = "Running installer...".to_string(); + status.progress = 50; + *self.installation_status.write().await = Some(status.clone()); + + // Run installer silently + let output = Command::new(&installer_path) + .args(["/silent", "/install"]) + .output() + .context("Failed to run Brave installer")?; + + // Clean up installer + let _ = std::fs::remove_file(&installer_path); + + if output.status.success() { + status.status = TaskStatus::Completed; + status.progress = 100; + status.message = "Brave Browser installed successfully!".to_string(); + info!("Brave Browser installed successfully"); + } else { + status.status = TaskStatus::Failed; + status.message = "Installation failed".to_string(); + } + } + + #[cfg(target_os = "linux")] + { + // Try apt first (Debian/Ubuntu) + let output = Command::new("sh") + .args(["-c", r#" + curl -fsSLo /usr/share/keyrings/brave-browser-archive-keyring.gpg https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg + echo "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg] https://brave-browser-apt-release.s3.brave.com/ stable main" | tee /etc/apt/sources.list.d/brave-browser-release.list + apt update && apt install -y brave-browser + "#]) + .output(); + + match output { + Ok(output) if output.status.success() => { + status.status = TaskStatus::Completed; + status.progress = 100; + status.message = "Brave Browser installed successfully!".to_string(); + } + _ => { + // Try snap as fallback + let snap_output = Command::new("snap").args(["install", "brave"]).output(); + + match snap_output { + Ok(output) if output.status.success() => { + status.status = TaskStatus::Completed; + status.progress = 100; + status.message = "Brave Browser installed via Snap!".to_string(); + } + _ => { + status.status = TaskStatus::Failed; + status.message = "Failed to install Brave Browser".to_string(); + } + } + } + } + } + + #[cfg(target_os = "macos")] + { + // Use Homebrew + let output = Command::new("brew") + .args(["install", "--cask", "brave-browser"]) + .output(); + + match output { + Ok(output) if output.status.success() => { + status.status = TaskStatus::Completed; + status.progress = 100; + status.message = "Brave Browser installed successfully!".to_string(); + } + _ => { + status.status = TaskStatus::Failed; + status.message = + "Failed to install. Please install Homebrew first.".to_string(); + } + } + } + + *self.installation_status.write().await = Some(status.clone()); + Ok(status) + } + + /// Run external optimization script + pub async fn run_optimization_script(&self) -> Result<()> { + info!( + "Running optimization script from: {}", + self.config.optimization_script_url + ); + + #[cfg(target_os = "windows")] + { + let temp_dir = std::env::temp_dir(); + let script_dir = temp_dir.join("OptimizationWindowsV1"); + + // Clone the repository + let clone_cmd = format!( + "git clone {} {}", + self.config.optimization_script_url, + script_dir.display() + ); + + let output = Command::new("cmd") + .args(["/C", &clone_cmd]) + .output() + .context("Failed to clone optimization script")?; + + if !output.status.success() { + return Err(anyhow::anyhow!("Failed to clone optimization repository")); + } + + // Find and run the main script + let script_path = script_dir.join("optimize.bat"); + if script_path.exists() { + let _ = Command::new("cmd") + .args(["/C", &script_path.to_string_lossy().to_string()]) + .spawn(); + + info!("Optimization script started"); + } else { + warn!("Optimization script not found at expected location"); + } + + // Cleanup + let _ = std::fs::remove_dir_all(&script_dir); + } + + Ok(()) + } + + /// Get disk space information + pub async fn get_disk_info(&self) -> Result> { + let mut disks = Vec::new(); + + #[cfg(target_os = "windows")] + { + let output = Command::new("powershell") + .args([ + "-Command", + "Get-PSDrive -PSProvider FileSystem | Select-Object Name, Used, Free | ConvertTo-Json", + ]) + .output() + .context("Failed to get disk info")?; + + if output.status.success() { + let json = String::from_utf8_lossy(&output.stdout); + if let Ok(drives) = serde_json::from_str::>(&json) { + for drive in drives { + disks.push(DiskInfo { + name: format!("{}:", drive["Name"].as_str().unwrap_or("?")), + total_bytes: drive["Used"].as_u64().unwrap_or(0) + + drive["Free"].as_u64().unwrap_or(0), + free_bytes: drive["Free"].as_u64().unwrap_or(0), + used_bytes: drive["Used"].as_u64().unwrap_or(0), + }); + } + } + } + } + + #[cfg(unix)] + { + let output = Command::new("df") + .args(["-B1", "--output=source,size,used,avail"]) + .output() + .context("Failed to get disk info")?; + + if output.status.success() { + let stdout = String::from_utf8_lossy(&output.stdout); + for line in stdout.lines().skip(1) { + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() >= 4 { + disks.push(DiskInfo { + name: parts[0].to_string(), + total_bytes: parts[1].parse().unwrap_or(0), + used_bytes: parts[2].parse().unwrap_or(0), + free_bytes: parts[3].parse().unwrap_or(0), + }); + } + } + } + } + + Ok(disks) + } + + /// Get cleanup stats + pub async fn get_cleanup_stats(&self) -> CleanupStats { + self.cleanup_stats.read().await.clone() + } + + /// Get optimization status + pub async fn get_optimization_status(&self) -> Option { + self.optimization_status.read().await.clone() + } + + /// Get installation status + pub async fn get_installation_status(&self) -> Option { + self.installation_status.read().await.clone() + } +} + +/// Disk information +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DiskInfo { + pub name: String, + pub total_bytes: u64, + pub free_bytes: u64, + pub used_bytes: u64, +} + +impl DiskInfo { + /// Get usage percentage + pub fn usage_percent(&self) -> f64 { + if self.total_bytes == 0 { + 0.0 + } else { + (self.used_bytes as f64 / self.total_bytes as f64) * 100.0 + } + } + + /// Format bytes to human readable + pub fn format_bytes(bytes: u64) -> String { + const KB: u64 = 1024; + const MB: u64 = KB * 1024; + const GB: u64 = MB * 1024; + const TB: u64 = GB * 1024; + + if bytes >= TB { + format!("{:.2} TB", bytes as f64 / TB as f64) + } else if bytes >= GB { + format!("{:.2} GB", bytes as f64 / GB as f64) + } else if bytes >= MB { + format!("{:.2} MB", bytes as f64 / MB as f64) + } else if bytes >= KB { + format!("{:.2} KB", bytes as f64 / KB as f64) + } else { + format!("{} B", bytes) + } + } +} + +/// API types for desktop tools +pub mod api { + use super::*; + + #[derive(Debug, Serialize, Deserialize)] + pub struct CleanupRequest { + pub categories: Vec, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct CleanupResponse { + pub success: bool, + pub stats: CleanupStats, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct OptimizeRequest { + pub tasks: Vec, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct OptimizeResponse { + pub success: bool, + pub message: String, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct DiskInfoResponse { + pub disks: Vec, + pub total_free_bytes: u64, + pub low_space_warning: bool, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_format_bytes() { + assert_eq!(DiskInfo::format_bytes(500), "500 B"); + assert_eq!(DiskInfo::format_bytes(1024), "1.00 KB"); + assert_eq!(DiskInfo::format_bytes(1048576), "1.00 MB"); + assert_eq!(DiskInfo::format_bytes(1073741824), "1.00 GB"); + } + + #[test] + fn test_disk_info_usage_percent() { + let disk = DiskInfo { + name: "C:".to_string(), + total_bytes: 100, + free_bytes: 25, + used_bytes: 75, + }; + assert_eq!(disk.usage_percent(), 75.0); + } + + #[test] + fn test_default_config() { + let config = DesktopToolsConfig::default(); + assert!(!config.temp_paths.is_empty()); + assert!(!config.browser_cache_paths.is_empty()); + } +} diff --git a/src/security/antivirus.rs b/src/security/antivirus.rs new file mode 100644 index 00000000..18de12f3 --- /dev/null +++ b/src/security/antivirus.rs @@ -0,0 +1,849 @@ +//! Antivirus Module +//! +//! This module provides antivirus and security scanning functionality including: +//! - Integration with ClamAV (open source antivirus) +//! - Windows Defender management (enable/disable) +//! - Threat detection and reporting +//! - Vulnerability scanning +//! - Real-time protection status + +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::sync::Arc; +use tokio::sync::RwLock; +use tracing::{error, info, warn}; + +/// Threat severity levels +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum ThreatSeverity { + Low, + Medium, + High, + Critical, +} + +/// Threat status +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum ThreatStatus { + Detected, + Quarantined, + Removed, + Allowed, + Failed, +} + +/// Detected threat information +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Threat { + pub id: String, + pub name: String, + pub threat_type: String, + pub severity: ThreatSeverity, + pub status: ThreatStatus, + pub file_path: Option, + pub detected_at: chrono::DateTime, + pub description: Option, + pub action_taken: Option, +} + +/// Vulnerability information +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Vulnerability { + pub id: String, + pub cve_id: Option, + pub name: String, + pub severity: ThreatSeverity, + pub affected_component: String, + pub description: String, + pub remediation: Option, + pub detected_at: chrono::DateTime, + pub is_patched: bool, +} + +/// Scan result +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ScanResult { + pub scan_id: String, + pub started_at: chrono::DateTime, + pub completed_at: Option>, + pub status: ScanStatus, + pub files_scanned: u64, + pub threats_found: Vec, + pub scan_type: ScanType, + pub target_path: Option, + pub error_message: Option, +} + +/// Scan status +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum ScanStatus { + Pending, + Running, + Completed, + Failed, + Cancelled, +} + +/// Scan type +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ScanType { + Quick, + Full, + Custom, + Memory, + Rootkit, +} + +/// Protection status +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ProtectionStatus { + pub real_time_protection: bool, + pub windows_defender_enabled: bool, + pub general_bots_protection: bool, + pub last_scan: Option>, + pub last_definition_update: Option>, + pub threats_blocked_today: u32, + pub quarantined_items: u32, +} + +/// Antivirus configuration +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AntivirusConfig { + /// Path to ClamAV installation + pub clamav_path: Option, + /// Enable real-time protection + pub real_time_protection: bool, + /// Quarantine directory + pub quarantine_dir: PathBuf, + /// Log directory + pub log_dir: PathBuf, + /// Auto-quarantine threats + pub auto_quarantine: bool, + /// Excluded paths from scanning + pub excluded_paths: Vec, + /// Excluded file extensions + pub excluded_extensions: Vec, + /// Maximum file size to scan (in MB) + pub max_file_size_mb: u64, + /// Scan archives + pub scan_archives: bool, + /// Definition update URL + pub definition_update_url: Option, +} + +impl Default for AntivirusConfig { + fn default() -> Self { + Self { + clamav_path: None, + real_time_protection: true, + quarantine_dir: PathBuf::from("./data/quarantine"), + log_dir: PathBuf::from("./logs/antivirus"), + auto_quarantine: true, + excluded_paths: vec![], + excluded_extensions: vec![], + max_file_size_mb: 100, + scan_archives: true, + definition_update_url: None, + } + } +} + +/// Antivirus Manager +pub struct AntivirusManager { + config: AntivirusConfig, + threats: Arc>>, + vulnerabilities: Arc>>, + active_scans: Arc>>, + protection_status: Arc>, +} + +impl AntivirusManager { + /// Create a new antivirus manager + pub fn new(config: AntivirusConfig) -> Result { + // Ensure directories exist + std::fs::create_dir_all(&config.quarantine_dir) + .context("Failed to create quarantine directory")?; + std::fs::create_dir_all(&config.log_dir).context("Failed to create log directory")?; + + let protection_status = ProtectionStatus { + real_time_protection: config.real_time_protection, + windows_defender_enabled: Self::check_windows_defender_status(), + general_bots_protection: true, + last_scan: None, + last_definition_update: None, + threats_blocked_today: 0, + quarantined_items: 0, + }; + + Ok(Self { + config, + threats: Arc::new(RwLock::new(Vec::new())), + vulnerabilities: Arc::new(RwLock::new(Vec::new())), + active_scans: Arc::new(RwLock::new(HashMap::new())), + protection_status: Arc::new(RwLock::new(protection_status)), + }) + } + + /// Check if Windows Defender is enabled + #[cfg(target_os = "windows")] + fn check_windows_defender_status() -> bool { + let output = Command::new("powershell") + .args([ + "-Command", + "Get-MpPreference | Select-Object -ExpandProperty DisableRealtimeMonitoring", + ]) + .output(); + + match output { + Ok(output) => { + let result = String::from_utf8_lossy(&output.stdout); + !result.trim().eq_ignore_ascii_case("true") + } + Err(_) => false, + } + } + + #[cfg(not(target_os = "windows"))] + fn check_windows_defender_status() -> bool { + false + } + + /// Disable Windows Defender (requires admin privileges) + #[cfg(target_os = "windows")] + pub async fn disable_windows_defender(&self) -> Result { + info!("Attempting to disable Windows Defender..."); + + // This requires administrator privileges + let script = r#" + Set-MpPreference -DisableRealtimeMonitoring $true + Set-MpPreference -DisableBehaviorMonitoring $true + Set-MpPreference -DisableBlockAtFirstSeen $true + Set-MpPreference -DisableIOAVProtection $true + Set-MpPreference -DisablePrivacyMode $true + Set-MpPreference -SignatureDisableUpdateOnStartupWithoutEngine $true + Set-MpPreference -DisableArchiveScanning $true + Set-MpPreference -DisableIntrusionPreventionSystem $true + Set-MpPreference -DisableScriptScanning $true + "#; + + let output = Command::new("powershell") + .args(["-Command", script]) + .output() + .context("Failed to execute PowerShell command")?; + + if output.status.success() { + let mut status = self.protection_status.write().await; + status.windows_defender_enabled = false; + info!("Windows Defender disabled successfully"); + Ok(true) + } else { + let error = String::from_utf8_lossy(&output.stderr); + error!("Failed to disable Windows Defender: {}", error); + Err(anyhow::anyhow!( + "Failed to disable Windows Defender: {}", + error + )) + } + } + + #[cfg(not(target_os = "windows"))] + pub async fn disable_windows_defender(&self) -> Result { + warn!("Windows Defender management is only available on Windows"); + Ok(false) + } + + /// Enable Windows Defender (requires admin privileges) + #[cfg(target_os = "windows")] + pub async fn enable_windows_defender(&self) -> Result { + info!("Attempting to enable Windows Defender..."); + + let script = r#" + Set-MpPreference -DisableRealtimeMonitoring $false + Set-MpPreference -DisableBehaviorMonitoring $false + Set-MpPreference -DisableBlockAtFirstSeen $false + Set-MpPreference -DisableIOAVProtection $false + Set-MpPreference -DisableArchiveScanning $false + Set-MpPreference -DisableIntrusionPreventionSystem $false + Set-MpPreference -DisableScriptScanning $false + "#; + + let output = Command::new("powershell") + .args(["-Command", script]) + .output() + .context("Failed to execute PowerShell command")?; + + if output.status.success() { + let mut status = self.protection_status.write().await; + status.windows_defender_enabled = true; + info!("Windows Defender enabled successfully"); + Ok(true) + } else { + let error = String::from_utf8_lossy(&output.stderr); + error!("Failed to enable Windows Defender: {}", error); + Err(anyhow::anyhow!( + "Failed to enable Windows Defender: {}", + error + )) + } + } + + #[cfg(not(target_os = "windows"))] + pub async fn enable_windows_defender(&self) -> Result { + warn!("Windows Defender management is only available on Windows"); + Ok(false) + } + + /// Start a scan + pub async fn start_scan( + &self, + scan_type: ScanType, + target_path: Option<&str>, + ) -> Result { + let scan_id = uuid::Uuid::new_v4().to_string(); + let now = chrono::Utc::now(); + + let scan_result = ScanResult { + scan_id: scan_id.clone(), + started_at: now, + completed_at: None, + status: ScanStatus::Pending, + files_scanned: 0, + threats_found: vec![], + scan_type, + target_path: target_path.map(|s| s.to_string()), + error_message: None, + }; + + { + let mut scans = self.active_scans.write().await; + scans.insert(scan_id.clone(), scan_result); + } + + // Spawn scan task + let scan_id_clone = scan_id.clone(); + let scans = self.active_scans.clone(); + let threats = self.threats.clone(); + let config = self.config.clone(); + let target = target_path.map(|s| s.to_string()); + + tokio::spawn(async move { + Self::run_scan(scan_id_clone, scan_type, target, scans, threats, config).await; + }); + + info!("Started {:?} scan with ID: {}", scan_type, scan_id); + Ok(scan_id) + } + + /// Run the actual scan + async fn run_scan( + scan_id: String, + scan_type: ScanType, + target_path: Option, + scans: Arc>>, + threats: Arc>>, + config: AntivirusConfig, + ) { + // Update status to running + { + let mut scans_guard = scans.write().await; + if let Some(scan) = scans_guard.get_mut(&scan_id) { + scan.status = ScanStatus::Running; + } + } + + // Determine scan target + let scan_path = match scan_type { + ScanType::Quick => target_path.unwrap_or_else(|| { + if cfg!(target_os = "windows") { + "C:\\Users".to_string() + } else { + "/home".to_string() + } + }), + ScanType::Full => target_path.unwrap_or_else(|| { + if cfg!(target_os = "windows") { + "C:\\".to_string() + } else { + "/".to_string() + } + }), + ScanType::Custom => target_path.unwrap_or_else(|| ".".to_string()), + ScanType::Memory => "memory".to_string(), + ScanType::Rootkit => "/".to_string(), + }; + + // Try ClamAV scan + let result = Self::run_clamav_scan(&scan_path, &config).await; + + // Update scan results + let mut scans_guard = scans.write().await; + if let Some(scan) = scans_guard.get_mut(&scan_id) { + scan.completed_at = Some(chrono::Utc::now()); + + match result { + Ok((files_scanned, found_threats)) => { + scan.status = ScanStatus::Completed; + scan.files_scanned = files_scanned; + scan.threats_found = found_threats.clone(); + + // Add threats to global list + if !found_threats.is_empty() { + let mut threats_guard = threats.blocking_write(); + threats_guard.extend(found_threats); + } + } + Err(e) => { + scan.status = ScanStatus::Failed; + scan.error_message = Some(e.to_string()); + } + } + } + } + + /// Run ClamAV scan + async fn run_clamav_scan(path: &str, config: &AntivirusConfig) -> Result<(u64, Vec)> { + // Find clamscan executable + let clamscan = config + .clamav_path + .clone() + .map(|p| p.join("clamscan")) + .unwrap_or_else(|| { + if cfg!(target_os = "windows") { + PathBuf::from("C:\\Program Files\\ClamAV\\clamscan.exe") + } else { + PathBuf::from("/usr/bin/clamscan") + } + }); + + if !clamscan.exists() { + // Try system PATH + let output = Command::new("which") + .arg("clamscan") + .output() + .unwrap_or_else(|_| { + Command::new("where") + .arg("clamscan") + .output() + .unwrap_or_else(|_| std::process::Output { + status: std::process::ExitStatus::default(), + stdout: vec![], + stderr: vec![], + }) + }); + + if output.stdout.is_empty() { + return Err(anyhow::anyhow!( + "ClamAV not found. Please install ClamAV first." + )); + } + } + + let mut cmd = Command::new(&clamscan); + cmd.arg("-r") // Recursive + .arg("--infected") // Only show infected files + .arg("--no-summary"); + + if config.scan_archives { + cmd.arg("--scan-archive=yes"); + } + + // Add excluded paths + for excluded in &config.excluded_paths { + cmd.arg(format!("--exclude-dir={}", excluded.display())); + } + + cmd.arg(path); + + let output = cmd.output().context("Failed to run ClamAV scan")?; + + let stdout = String::from_utf8_lossy(&output.stdout); + let mut threats = Vec::new(); + let mut files_scanned: u64 = 0; + + // Parse ClamAV output + for line in stdout.lines() { + if line.contains("FOUND") { + let parts: Vec<&str> = line.split(':').collect(); + if parts.len() >= 2 { + let file_path = parts[0].trim(); + let threat_name = parts[1].trim().replace(" FOUND", ""); + + threats.push(Threat { + id: uuid::Uuid::new_v4().to_string(), + name: threat_name.clone(), + threat_type: Self::classify_threat(&threat_name), + severity: Self::assess_severity(&threat_name), + status: ThreatStatus::Detected, + file_path: Some(file_path.to_string()), + detected_at: chrono::Utc::now(), + description: Some(format!("Detected by ClamAV: {}", threat_name)), + action_taken: None, + }); + } + } + files_scanned += 1; + } + + Ok((files_scanned, threats)) + } + + /// Classify threat type + fn classify_threat(name: &str) -> String { + let name_lower = name.to_lowercase(); + if name_lower.contains("trojan") { + "Trojan".to_string() + } else if name_lower.contains("virus") { + "Virus".to_string() + } else if name_lower.contains("worm") { + "Worm".to_string() + } else if name_lower.contains("ransomware") { + "Ransomware".to_string() + } else if name_lower.contains("spyware") { + "Spyware".to_string() + } else if name_lower.contains("adware") { + "Adware".to_string() + } else if name_lower.contains("rootkit") { + "Rootkit".to_string() + } else if name_lower.contains("pup") || name_lower.contains("pua") { + "PUP".to_string() + } else { + "Malware".to_string() + } + } + + /// Assess threat severity + fn assess_severity(name: &str) -> ThreatSeverity { + let name_lower = name.to_lowercase(); + if name_lower.contains("ransomware") || name_lower.contains("rootkit") { + ThreatSeverity::Critical + } else if name_lower.contains("trojan") || name_lower.contains("backdoor") { + ThreatSeverity::High + } else if name_lower.contains("virus") || name_lower.contains("worm") { + ThreatSeverity::Medium + } else { + ThreatSeverity::Low + } + } + + /// Quarantine a file + pub async fn quarantine_file(&self, file_path: &Path) -> Result<()> { + if !file_path.exists() { + return Err(anyhow::anyhow!("File not found: {:?}", file_path)); + } + + let file_name = file_path + .file_name() + .unwrap_or_default() + .to_string_lossy() + .to_string(); + + let quarantine_path = self.config.quarantine_dir.join(format!( + "{}_{}", + chrono::Utc::now().timestamp(), + file_name + )); + + std::fs::rename(file_path, &quarantine_path) + .context("Failed to move file to quarantine")?; + + info!("File quarantined: {:?} -> {:?}", file_path, quarantine_path); + + // Update protection status + let mut status = self.protection_status.write().await; + status.quarantined_items += 1; + + Ok(()) + } + + /// Remove a threat + pub async fn remove_threat(&self, threat_id: &str) -> Result<()> { + let mut threats = self.threats.write().await; + + if let Some(pos) = threats.iter().position(|t| t.id == threat_id) { + let threat = &threats[pos]; + + if let Some(ref file_path) = threat.file_path { + let path = Path::new(file_path); + if path.exists() { + std::fs::remove_file(path).context("Failed to remove infected file")?; + info!("Removed infected file: {}", file_path); + } + } + + threats[pos].status = ThreatStatus::Removed; + threats[pos].action_taken = Some("File removed".to_string()); + } + + Ok(()) + } + + /// Get all detected threats + pub async fn get_threats(&self) -> Vec { + self.threats.read().await.clone() + } + + /// Get threats by status + pub async fn get_threats_by_status(&self, status: ThreatStatus) -> Vec { + self.threats + .read() + .await + .iter() + .filter(|t| t.status == status) + .cloned() + .collect() + } + + /// Get all vulnerabilities + pub async fn get_vulnerabilities(&self) -> Vec { + self.vulnerabilities.read().await.clone() + } + + /// Scan for vulnerabilities + pub async fn scan_vulnerabilities(&self) -> Result> { + let mut vulnerabilities = Vec::new(); + + // Check for common vulnerabilities + + // 1. Check for outdated software (Windows) + #[cfg(target_os = "windows")] + { + // Check Windows Update status + let output = Command::new("powershell") + .args([ + "-Command", + "Get-HotFix | Sort-Object -Property InstalledOn -Descending | Select-Object -First 1", + ]) + .output(); + + if let Ok(output) = output { + let result = String::from_utf8_lossy(&output.stdout); + // Check if updates are old + if result.is_empty() { + vulnerabilities.push(Vulnerability { + id: uuid::Uuid::new_v4().to_string(), + cve_id: None, + name: "Missing Windows Updates".to_string(), + severity: ThreatSeverity::High, + affected_component: "Windows Update".to_string(), + description: "System may be missing critical security updates".to_string(), + remediation: Some( + "Run Windows Update to install latest patches".to_string(), + ), + detected_at: chrono::Utc::now(), + is_patched: false, + }); + } + } + } + + // 2. Check for weak file permissions + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + + let sensitive_paths = vec!["/etc/passwd", "/etc/shadow", "/etc/ssh/sshd_config"]; + + for path_str in sensitive_paths { + let path = Path::new(path_str); + if path.exists() { + if let Ok(metadata) = std::fs::metadata(path) { + let mode = metadata.permissions().mode(); + if mode & 0o002 != 0 { + // World writable + vulnerabilities.push(Vulnerability { + id: uuid::Uuid::new_v4().to_string(), + cve_id: None, + name: format!("Weak permissions on {}", path_str), + severity: ThreatSeverity::High, + affected_component: path_str.to_string(), + description: "Sensitive file has world-writable permissions" + .to_string(), + remediation: Some(format!("chmod o-w {}", path_str)), + detected_at: chrono::Utc::now(), + is_patched: false, + }); + } + } + } + } + } + + // Store vulnerabilities + { + let mut vulns = self.vulnerabilities.write().await; + vulns.extend(vulnerabilities.clone()); + } + + Ok(vulnerabilities) + } + + /// Get protection status + pub async fn get_protection_status(&self) -> ProtectionStatus { + self.protection_status.read().await.clone() + } + + /// Get scan result by ID + pub async fn get_scan_result(&self, scan_id: &str) -> Option { + self.active_scans.read().await.get(scan_id).cloned() + } + + /// Get all scans + pub async fn get_all_scans(&self) -> Vec { + self.active_scans.read().await.values().cloned().collect() + } + + /// Cancel a scan + pub async fn cancel_scan(&self, scan_id: &str) -> Result<()> { + let mut scans = self.active_scans.write().await; + if let Some(scan) = scans.get_mut(scan_id) { + if scan.status == ScanStatus::Running || scan.status == ScanStatus::Pending { + scan.status = ScanStatus::Cancelled; + scan.completed_at = Some(chrono::Utc::now()); + info!("Scan {} cancelled", scan_id); + Ok(()) + } else { + Err(anyhow::anyhow!("Scan is not running")) + } + } else { + Err(anyhow::anyhow!("Scan not found")) + } + } + + /// Update virus definitions + pub async fn update_definitions(&self) -> Result<()> { + info!("Updating virus definitions..."); + + // Try freshclam (ClamAV updater) + let freshclam = if cfg!(target_os = "windows") { + "freshclam.exe" + } else { + "freshclam" + }; + + let output = Command::new(freshclam) + .output() + .context("Failed to run freshclam")?; + + if output.status.success() { + let mut status = self.protection_status.write().await; + status.last_definition_update = Some(chrono::Utc::now()); + info!("Virus definitions updated successfully"); + Ok(()) + } else { + let error = String::from_utf8_lossy(&output.stderr); + Err(anyhow::anyhow!("Failed to update definitions: {}", error)) + } + } + + /// Set real-time protection + pub async fn set_realtime_protection(&self, enabled: bool) -> Result<()> { + let mut status = self.protection_status.write().await; + status.real_time_protection = enabled; + info!("Real-time protection set to: {}", enabled); + Ok(()) + } +} + +/// API response types for the security endpoints +pub mod api { + use super::*; + + #[derive(Debug, Serialize, Deserialize)] + pub struct ThreatsResponse { + pub threats: Vec, + pub total: usize, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct VulnerabilitiesResponse { + pub vulnerabilities: Vec, + pub total: usize, + pub critical: usize, + pub high: usize, + pub medium: usize, + pub low: usize, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct ScanRequest { + pub scan_type: ScanType, + pub target_path: Option, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct ScanResponse { + pub scan_id: String, + pub status: ScanStatus, + pub message: String, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct ActionResponse { + pub success: bool, + pub message: String, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct DefenderStatusRequest { + pub enabled: bool, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_classify_threat() { + assert_eq!( + AntivirusManager::classify_threat("Win.Trojan.Generic"), + "Trojan" + ); + assert_eq!( + AntivirusManager::classify_threat("Ransomware.WannaCry"), + "Ransomware" + ); + assert_eq!( + AntivirusManager::classify_threat("PUP.Optional.Adware"), + "PUP" + ); + assert_eq!( + AntivirusManager::classify_threat("Unknown.Malware"), + "Malware" + ); + } + + #[test] + fn test_assess_severity() { + assert_eq!( + AntivirusManager::assess_severity("Ransomware.Test"), + ThreatSeverity::Critical + ); + assert_eq!( + AntivirusManager::assess_severity("Trojan.Generic"), + ThreatSeverity::High + ); + assert_eq!( + AntivirusManager::assess_severity("Virus.Test"), + ThreatSeverity::Medium + ); + assert_eq!( + AntivirusManager::assess_severity("PUP.Adware"), + ThreatSeverity::Low + ); + } + + #[tokio::test] + async fn test_antivirus_manager_creation() { + let config = AntivirusConfig::default(); + let manager = AntivirusManager::new(config); + assert!(manager.is_ok()); + } +} diff --git a/src/security/mod.rs b/src/security/mod.rs index e3fd1d49..8f3d0480 100644 --- a/src/security/mod.rs +++ b/src/security/mod.rs @@ -6,12 +6,19 @@ //! - Internal Certificate Authority (CA) management //! - Certificate lifecycle management //! - Security utilities and helpers +//! - Antivirus and threat detection (ClamAV integration) +//! - Windows Defender management +pub mod antivirus; pub mod ca; pub mod integration; pub mod mutual_tls; pub mod tls; +pub use antivirus::{ + AntivirusConfig, AntivirusManager, ProtectionStatus, ScanResult, ScanStatus, ScanType, Threat, + ThreatSeverity, ThreatStatus, Vulnerability, +}; pub use ca::{CaConfig, CaManager, CertificateRequest, CertificateResponse}; pub use integration::{ create_https_client, get_tls_integration, init_tls_integration, to_secure_url, TlsIntegration, diff --git a/templates/tools.html b/templates/tools.html new file mode 100644 index 00000000..8c8683e3 --- /dev/null +++ b/templates/tools.html @@ -0,0 +1,1019 @@ + + + +
+ + + + +
+

+ ๐Ÿงน + Drive Cleaner +

+

+ Remove temporary files, browser cache, and other junk to free up disk space. +

+ + +
+
+

Disk Space

+ +
+
+

Click "Analyze" to scan your disks

+
+
+ + +
+

Select items to clean:

+
+ + + + + + +
+
+ + + + + +
+ + +
+ + +
+
+ + +
+

+ ๐Ÿ›ก๏ธ + Antivirus +

+

+ Scan your system for threats using ClamAV open-source antivirus engine. +

+ + +
+
Loading protection status...
+
+ + +
+
+
๐ŸชŸ Use General Bots Instead of Windows Defender
+
+ Disable Windows Defender and use General Bots protection (requires admin) +
+
+
+ +
+
+ + + + + +
+
+ + +
+ + + +
+
+ + +
+

+ โšก + Optimize Windows +

+

+ Optimize your Windows system for better performance using + OptimizationWindowsV1. +

+ + +
+
+
+ ๐Ÿ’พ +
+ Defragment Disk + Optimize disk for faster access +
+ +
+
+ ๐Ÿง  +
+ Clear Memory + Free up RAM for better performance +
+ +
+
+ ๐Ÿš€ +
+ Optimize Services + Disable unnecessary services +
+ +
+
+ ๐ŸŽฏ +
+ Startup Programs + Manage startup applications +
+ +
+
+
+ + +
+ + +
+
+ + +
+

+ ๐Ÿ“ฆ + Install Software +

+

+ Quickly install recommended software on your system. +

+ + +
+
+
๐Ÿฆ
+
+ Brave Browser + Privacy-focused browser with built-in ad blocker +
+ +
+
+ + + +
+ + +
+
+ + + + diff --git a/ui/suite/editor.html b/ui/suite/editor.html new file mode 100644 index 00000000..6897ac7b --- /dev/null +++ b/ui/suite/editor.html @@ -0,0 +1,519 @@ + + + + + + Editor - General Bots + + + + + +
+ +
+
+ ๐Ÿ“ +
+
+ Untitled + +
+
+
+ +
+ +
+ + +
+
+ + +
+ +
+ + +
+ +
+ +
+ + +
+ + +
+ +
+
+ 1 +
+ +
+ + + +
+ + +
+
+ + ๐Ÿ“„ Plain Text + + UTF-8 + + Saving... + +
+
+ + Ln 1, Col 1 + +
+
+ + + +
+ + +
+ + + +