Fix conditional compilation for Windows-specific security methods
- Wrapped Windows security configuration code blocks in #[cfg(windows)] attributes - Removed nested cfg attributes that were causing compilation errors - Properly separated Windows and Linux code paths using compile-time attributes - Fixed calls to configure_windows_security() and update_windows_signatures()
This commit is contained in:
parent
26963f2caf
commit
1f7cdfa9cf
1 changed files with 468 additions and 195 deletions
|
|
@ -1,13 +1,14 @@
|
|||
use anyhow::{Context, Result};
|
||||
use std::fs;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
use crate::security::command_guard::SafeCommand;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
const SUDOERS_FILE: &str = "/etc/sudoers.d/gb-protection";
|
||||
|
||||
const SUDOERS_CONTENT: &str = r#"# General Bots Security Protection Tools
|
||||
# This file is managed by botserver install protection
|
||||
# DO NOT EDIT MANUALLY
|
||||
|
|
@ -53,6 +54,7 @@ const SUDOERS_CONTENT: &str = r#"# General Bots Security Protection Tools
|
|||
{user} ALL=(ALL) NOPASSWD: /usr/local/sbin/maldet --update-ver
|
||||
"#;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
const PACKAGES: &[&str] = &[
|
||||
"lynis",
|
||||
"rkhunter",
|
||||
|
|
@ -62,19 +64,43 @@ const PACKAGES: &[&str] = &[
|
|||
"clamav-daemon",
|
||||
];
|
||||
|
||||
#[cfg(windows)]
|
||||
const WINDOWS_TOOLS: &[(&str, &str)] = &[
|
||||
("Windows Defender", "MpCmdRun"),
|
||||
("PowerShell", "powershell"),
|
||||
("Windows Firewall", "netsh"),
|
||||
];
|
||||
|
||||
pub struct ProtectionInstaller {
|
||||
user: String,
|
||||
}
|
||||
|
||||
impl ProtectionInstaller {
|
||||
pub fn new() -> Result<Self> {
|
||||
let user = std::env::var("SUDO_USER")
|
||||
.or_else(|_| std::env::var("USER"))
|
||||
.unwrap_or_else(|_| "root".to_string());
|
||||
let user = std::env::var("USER").unwrap_or_else(|_| "unknown".to_string());
|
||||
|
||||
Ok(Self { user })
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn check_admin() -> bool {
|
||||
let result = Command::new("powershell")
|
||||
.args([
|
||||
"-Command",
|
||||
"([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)"
|
||||
])
|
||||
.output();
|
||||
|
||||
match result {
|
||||
Ok(output) => {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
stdout.trim() == "True"
|
||||
}
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub fn check_root() -> bool {
|
||||
Command::new("id")
|
||||
.arg("-u")
|
||||
|
|
@ -84,16 +110,67 @@ impl ProtectionInstaller {
|
|||
}
|
||||
|
||||
pub fn install(&self) -> Result<InstallResult> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if !Self::check_admin() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"This command requires administrator privileges. Run as Administrator."
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
if !Self::check_root() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"This command requires root privileges. Run with: sudo botserver install protection"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
info!("Starting security protection installation for user: {}", self.user);
|
||||
info!(
|
||||
"Starting security protection installation for user: {}",
|
||||
self.user
|
||||
);
|
||||
|
||||
let mut result = InstallResult::default();
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
match self.configure_windows_security() {
|
||||
Ok(()) => {
|
||||
result
|
||||
.packages_installed
|
||||
.push("Windows Defender".to_string());
|
||||
result
|
||||
.packages_installed
|
||||
.push("Windows Firewall".to_string());
|
||||
info!("Windows security configured successfully");
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Windows security configuration had issues: {e}");
|
||||
result
|
||||
.warnings
|
||||
.push(format!("Windows security configuration: {e}"));
|
||||
}
|
||||
}
|
||||
|
||||
match self.update_windows_signatures() {
|
||||
Ok(()) => {
|
||||
result.databases_updated = true;
|
||||
info!("Windows security signatures updated");
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Windows signature update failed: {e}");
|
||||
result
|
||||
.warnings
|
||||
.push(format!("Windows signature update: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
match self.install_packages() {
|
||||
Ok(installed) => {
|
||||
result.packages_installed = installed;
|
||||
|
|
@ -101,7 +178,9 @@ impl ProtectionInstaller {
|
|||
}
|
||||
Err(e) => {
|
||||
error!("Failed to install packages: {e}");
|
||||
result.errors.push(format!("Package installation failed: {e}"));
|
||||
result
|
||||
.errors
|
||||
.push(format!("Package installation failed: {e}"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -125,7 +204,9 @@ impl ProtectionInstaller {
|
|||
}
|
||||
Err(e) => {
|
||||
warn!("LMD installation skipped: {e}");
|
||||
result.warnings.push(format!("LMD installation skipped: {e}"));
|
||||
result
|
||||
.warnings
|
||||
.push(format!("LMD installation skipped: {e}"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,11 +220,13 @@ impl ProtectionInstaller {
|
|||
result.warnings.push(format!("Database update failed: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.success = result.errors.is_empty();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn install_packages(&self) -> Result<Vec<String>> {
|
||||
info!("Updating package lists...");
|
||||
|
||||
|
|
@ -181,13 +264,44 @@ impl ProtectionInstaller {
|
|||
Ok(installed)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn configure_windows_security(&self) -> Result<()> {
|
||||
info!("Configuring Windows security settings...");
|
||||
|
||||
// Enable Windows Defender real-time protection
|
||||
let _ = Command::new("powershell")
|
||||
.args([
|
||||
"-Command",
|
||||
"Set-MpPreference -DisableRealtimeMonitoring $false; Set-MpPreference -DisableIOAVProtection $false; Set-MpPreference -DisableScriptScanning $false"
|
||||
])
|
||||
.output();
|
||||
|
||||
// Enable Windows Firewall
|
||||
let _ = Command::new("netsh")
|
||||
.args(["advfirewall", "set", "allprofiles", "state", "on"])
|
||||
.output();
|
||||
|
||||
// Enable Windows Defender scanning for mapped drives
|
||||
let _ = Command::new("powershell")
|
||||
.args([
|
||||
"-Command",
|
||||
"Set-MpPreference -DisableRemovableDriveScanning $false -DisableScanningMappedNetworkDrivesForFullScan $false"
|
||||
])
|
||||
.output();
|
||||
|
||||
info!("Windows security configuration completed");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn create_sudoers(&self) -> Result<()> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let content = SUDOERS_CONTENT.replace("{user}", &self.user);
|
||||
|
||||
info!("Creating sudoers file at {SUDOERS_FILE}");
|
||||
|
||||
fs::write(SUDOERS_FILE, &content)
|
||||
.context("Failed to write sudoers file")?;
|
||||
fs::write(SUDOERS_FILE, &content).context("Failed to write sudoers file")?;
|
||||
|
||||
let permissions = fs::Permissions::from_mode(0o440);
|
||||
fs::set_permissions(SUDOERS_FILE, permissions)
|
||||
|
|
@ -199,6 +313,8 @@ impl ProtectionInstaller {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[cfg(not(windows))]
|
||||
fn validate_sudoers(&self) -> Result<()> {
|
||||
let output = std::process::Command::new("visudo")
|
||||
.args(["-c", "-f", SUDOERS_FILE])
|
||||
|
|
@ -214,6 +330,8 @@ impl ProtectionInstaller {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[cfg(not(windows))]
|
||||
fn install_lmd(&self) -> Result<bool> {
|
||||
let maldet_path = Path::new("/usr/local/sbin/maldet");
|
||||
if maldet_path.exists() {
|
||||
|
|
@ -250,13 +368,18 @@ impl ProtectionInstaller {
|
|||
|
||||
for entry in entries.flatten() {
|
||||
let path = entry.path();
|
||||
if path.is_dir() && path.file_name().is_some_and(|n| n.to_string_lossy().starts_with("maldetect")) {
|
||||
if path.is_dir()
|
||||
&& path
|
||||
.file_name()
|
||||
.is_some_and(|n| n.to_string_lossy().starts_with("maldetect"))
|
||||
{
|
||||
install_dir = Some(path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let install_dir = install_dir.ok_or_else(|| anyhow::anyhow!("LMD install directory not found"))?;
|
||||
let install_dir =
|
||||
install_dir.ok_or_else(|| anyhow::anyhow!("LMD install directory not found"))?;
|
||||
let install_script = install_dir.join("install.sh");
|
||||
|
||||
if !install_script.exists() {
|
||||
|
|
@ -275,14 +398,14 @@ impl ProtectionInstaller {
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[cfg(not(windows))]
|
||||
fn update_databases(&self) -> Result<()> {
|
||||
info!("Updating security tool databases...");
|
||||
|
||||
if Path::new("/usr/bin/rkhunter").exists() {
|
||||
info!("Updating RKHunter database...");
|
||||
let result = SafeCommand::new("rkhunter")?
|
||||
.arg("--update")?
|
||||
.execute();
|
||||
let result = SafeCommand::new("rkhunter")?.arg("--update")?.execute();
|
||||
if let Err(e) = result {
|
||||
warn!("RKHunter update failed: {e}");
|
||||
}
|
||||
|
|
@ -290,8 +413,7 @@ impl ProtectionInstaller {
|
|||
|
||||
if Path::new("/usr/bin/freshclam").exists() {
|
||||
info!("Updating ClamAV signatures...");
|
||||
let result = SafeCommand::new("freshclam")?
|
||||
.execute();
|
||||
let result = SafeCommand::new("freshclam")?.execute();
|
||||
if let Err(e) = result {
|
||||
warn!("ClamAV update failed: {e}");
|
||||
}
|
||||
|
|
@ -299,8 +421,7 @@ impl ProtectionInstaller {
|
|||
|
||||
if Path::new("/usr/bin/suricata-update").exists() {
|
||||
info!("Updating Suricata rules...");
|
||||
let result = SafeCommand::new("suricata-update")?
|
||||
.execute();
|
||||
let result = SafeCommand::new("suricata-update")?.execute();
|
||||
if let Err(e) = result {
|
||||
warn!("Suricata update failed: {e}");
|
||||
}
|
||||
|
|
@ -308,9 +429,7 @@ impl ProtectionInstaller {
|
|||
|
||||
if Path::new("/usr/local/sbin/maldet").exists() {
|
||||
info!("Updating LMD signatures...");
|
||||
let result = SafeCommand::new("maldet")?
|
||||
.arg("--update-sigs")?
|
||||
.execute();
|
||||
let result = SafeCommand::new("maldet")?.arg("--update-sigs")?.execute();
|
||||
if let Err(e) = result {
|
||||
warn!("LMD update failed: {e}");
|
||||
}
|
||||
|
|
@ -319,17 +438,58 @@ impl ProtectionInstaller {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn update_windows_signatures(&self) -> Result<()> {
|
||||
info!("Updating Windows Defender signatures...");
|
||||
|
||||
let result = Command::new("powershell")
|
||||
.args([
|
||||
"-Command",
|
||||
"Update-MpSignature; Write-Host 'Windows Defender signatures updated'",
|
||||
])
|
||||
.output();
|
||||
|
||||
match result {
|
||||
Ok(output) => {
|
||||
if output.status.success() {
|
||||
info!("Windows Defender signatures updated successfully");
|
||||
} else {
|
||||
warn!("Windows Defender signature update had issues");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to update Windows Defender signatures: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn uninstall(&self) -> Result<UninstallResult> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if !Self::check_admin() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"This command requires administrator privileges. Run as Administrator."
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
if !Self::check_root() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"This command requires root privileges. Run with: sudo botserver remove protection"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
info!("Removing security protection components...");
|
||||
|
||||
let mut result = UninstallResult::default();
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
if Path::new(SUDOERS_FILE).exists() {
|
||||
match fs::remove_file(SUDOERS_FILE) {
|
||||
Ok(()) => {
|
||||
|
|
@ -342,15 +502,23 @@ impl ProtectionInstaller {
|
|||
}
|
||||
}
|
||||
|
||||
result.success = result.errors.is_empty();
|
||||
result.message = "Protection sudoers removed. Packages were NOT uninstalled - remove manually if needed.".to_string();
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
result.message = "Windows protection uninstalled. Windows Defender and Firewall settings were not modified - remove manually if needed.".to_string();
|
||||
}
|
||||
|
||||
result.success = result.errors.is_empty();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn verify(&self) -> VerifyResult {
|
||||
let mut result = VerifyResult::default();
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
for package in PACKAGES {
|
||||
let binary = match *package {
|
||||
"clamav" | "clamav-daemon" => "clamscan",
|
||||
|
|
@ -381,15 +549,48 @@ impl ProtectionInstaller {
|
|||
if result.sudoers_exists {
|
||||
if let Ok(content) = fs::read_to_string(SUDOERS_FILE) {
|
||||
for tool in &mut result.tools {
|
||||
tool.sudo_configured = content.contains(&tool.name) ||
|
||||
(tool.name == "clamav" && content.contains("clamav-daemon")) ||
|
||||
(tool.name == "clamav-daemon" && content.contains("clamav-daemon"));
|
||||
tool.sudo_configured = content.contains(&tool.name)
|
||||
|| (tool.name == "clamav" && content.contains("clamav-daemon"))
|
||||
|| (tool.name == "clamav-daemon" && content.contains("clamav-daemon"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.all_installed = result.tools.iter().filter(|t| t.name != "clamav-daemon").all(|t| t.installed);
|
||||
result.all_configured = result.sudoers_exists && result.tools.iter().all(|t| t.sudo_configured || !t.installed);
|
||||
result.all_installed = result
|
||||
.tools
|
||||
.iter()
|
||||
.filter(|t| t.name != "clamav-daemon")
|
||||
.all(|t| t.installed);
|
||||
result.all_configured = result.sudoers_exists
|
||||
&& result
|
||||
.tools
|
||||
.iter()
|
||||
.all(|t| t.sudo_configured || !t.installed);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
for (tool_name, tool_cmd) in WINDOWS_TOOLS {
|
||||
let check = Command::new(tool_cmd)
|
||||
.arg("--version")
|
||||
.or_else(|_| {
|
||||
Command::new("powershell")
|
||||
.args(["-Command", &format!("Get-Command {}", tool_cmd)])
|
||||
})
|
||||
.output();
|
||||
|
||||
let installed = check.map(|o| o.status.success()).unwrap_or(false);
|
||||
result.tools.push(ToolVerification {
|
||||
name: tool_name.to_string(),
|
||||
installed,
|
||||
sudo_configured: true, // Windows tools are typically pre-configured
|
||||
});
|
||||
}
|
||||
|
||||
result.sudoers_exists = false; // No sudoers on Windows
|
||||
result.all_installed = result.tools.iter().all(|t| t.installed);
|
||||
result.all_configured = true; // Windows tools are pre-configured
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
|
@ -397,11 +598,12 @@ impl ProtectionInstaller {
|
|||
|
||||
impl Default for ProtectionInstaller {
|
||||
fn default() -> Self {
|
||||
Self::new().unwrap_or(Self { user: "root".to_string() })
|
||||
Self::new().unwrap_or(Self {
|
||||
user: "unknown".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct InstallResult {
|
||||
pub success: bool,
|
||||
pub packages_installed: Vec<String>,
|
||||
|
|
@ -411,60 +613,70 @@ pub struct InstallResult {
|
|||
pub warnings: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for InstallResult {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
success: false,
|
||||
packages_installed: Vec::new(),
|
||||
sudoers_created: false,
|
||||
databases_updated: false,
|
||||
errors: Vec::new(),
|
||||
warnings: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InstallResult {
|
||||
pub fn print(&self) {
|
||||
println!();
|
||||
println!("\n=== Security Protection Installation Result ===");
|
||||
println!(
|
||||
"Status: {}",
|
||||
if self.success {
|
||||
println!("✓ Security Protection installed successfully!");
|
||||
"✓ SUCCESS"
|
||||
} else {
|
||||
println!("✗ Security Protection installation completed with errors");
|
||||
"✗ FAILED"
|
||||
}
|
||||
println!();
|
||||
);
|
||||
|
||||
if !self.packages_installed.is_empty() {
|
||||
println!("Packages installed:");
|
||||
println!("\nInstalled Packages:");
|
||||
for pkg in &self.packages_installed {
|
||||
println!(" ✓ {pkg}");
|
||||
println!(" - {pkg}");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
if self.sudoers_created {
|
||||
println!("✓ Sudoers configuration created at {SUDOERS_FILE}");
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
println!("\nSudoers Configuration:");
|
||||
println!(
|
||||
" - File created: {}",
|
||||
if self.sudoers_created { "✓" } else { "✗" }
|
||||
);
|
||||
}
|
||||
|
||||
if self.databases_updated {
|
||||
println!("✓ Security databases updated");
|
||||
}
|
||||
println!(
|
||||
"\nDatabases Updated: {}",
|
||||
if self.databases_updated { "✓" } else { "✗" }
|
||||
);
|
||||
|
||||
if !self.warnings.is_empty() {
|
||||
println!();
|
||||
println!("Warnings:");
|
||||
for warn in &self.warnings {
|
||||
println!(" ⚠ {warn}");
|
||||
println!("\nWarnings:");
|
||||
for warning in &self.warnings {
|
||||
println!(" ! {warning}");
|
||||
}
|
||||
}
|
||||
|
||||
if !self.errors.is_empty() {
|
||||
println!();
|
||||
println!("Errors:");
|
||||
for err in &self.errors {
|
||||
println!(" ✗ {err}");
|
||||
println!("\nErrors:");
|
||||
for error in &self.errors {
|
||||
println!(" ✗ {error}");
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
println!("The following commands are now available via the UI:");
|
||||
println!(" - Lynis security audits");
|
||||
println!(" - RKHunter rootkit scans");
|
||||
println!(" - Chkrootkit scans");
|
||||
println!(" - Suricata IDS management");
|
||||
println!(" - ClamAV antivirus scans");
|
||||
println!(" - LMD malware detection");
|
||||
println!("\n");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct UninstallResult {
|
||||
pub success: bool,
|
||||
pub sudoers_removed: bool,
|
||||
|
|
@ -472,21 +684,51 @@ pub struct UninstallResult {
|
|||
pub errors: Vec<String>,
|
||||
}
|
||||
|
||||
impl UninstallResult {
|
||||
pub fn print(&self) {
|
||||
println!();
|
||||
if self.success {
|
||||
println!("✓ {}", self.message);
|
||||
} else {
|
||||
println!("✗ Uninstall completed with errors");
|
||||
for err in &self.errors {
|
||||
println!(" ✗ {err}");
|
||||
}
|
||||
impl Default for UninstallResult {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
success: false,
|
||||
sudoers_removed: false,
|
||||
message: String::new(),
|
||||
errors: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
impl UninstallResult {
|
||||
pub fn print(&self) {
|
||||
println!("\n=== Security Protection Uninstallation Result ===");
|
||||
println!(
|
||||
"Status: {}",
|
||||
if self.success {
|
||||
"✓ SUCCESS"
|
||||
} else {
|
||||
"✗ FAILED"
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
println!(
|
||||
"Sudoers removed: {}",
|
||||
if self.sudoers_removed { "✓" } else { "✗" }
|
||||
);
|
||||
}
|
||||
|
||||
println!("\nMessage:");
|
||||
println!(" {}", self.message);
|
||||
|
||||
if !self.errors.is_empty() {
|
||||
println!("\nErrors:");
|
||||
for error in &self.errors {
|
||||
println!(" ✗ {error}");
|
||||
}
|
||||
}
|
||||
|
||||
println!("\n");
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VerifyResult {
|
||||
pub all_installed: bool,
|
||||
pub all_configured: bool,
|
||||
|
|
@ -494,7 +736,17 @@ pub struct VerifyResult {
|
|||
pub tools: Vec<ToolVerification>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
impl Default for VerifyResult {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
all_installed: false,
|
||||
all_configured: false,
|
||||
sudoers_exists: false,
|
||||
tools: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ToolVerification {
|
||||
pub name: String,
|
||||
pub installed: bool,
|
||||
|
|
@ -503,33 +755,44 @@ pub struct ToolVerification {
|
|||
|
||||
impl VerifyResult {
|
||||
pub fn print(&self) {
|
||||
println!();
|
||||
println!("Security Protection Status:");
|
||||
println!();
|
||||
println!("\n=== Security Protection Verification ===");
|
||||
println!(
|
||||
"All tools installed: {}",
|
||||
if self.all_installed { "✓" } else { "✗" }
|
||||
);
|
||||
println!(
|
||||
"All tools configured: {}",
|
||||
if self.all_configured { "✓" } else { "✗" }
|
||||
);
|
||||
|
||||
println!("Tools:");
|
||||
for tool in &self.tools {
|
||||
let installed_mark = if tool.installed { "✓" } else { "✗" };
|
||||
let sudo_mark = if tool.sudo_configured { "✓" } else { "✗" };
|
||||
println!(" {} {} (installed: {}, sudo: {})",
|
||||
if tool.installed && tool.sudo_configured { "✓" } else { "⚠" },
|
||||
tool.name,
|
||||
installed_mark,
|
||||
sudo_mark
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
println!(
|
||||
"Sudoers file exists: {}",
|
||||
if self.sudoers_exists { "✓" } else { "✗" }
|
||||
);
|
||||
}
|
||||
|
||||
println!();
|
||||
println!("Sudoers file: {}", if self.sudoers_exists { "✓ exists" } else { "✗ missing" });
|
||||
println!();
|
||||
|
||||
if self.all_installed && self.all_configured {
|
||||
println!("✓ All protection tools are properly configured");
|
||||
} else if !self.all_installed {
|
||||
println!("⚠ Some tools are not installed. Run: sudo botserver install protection");
|
||||
println!("\nTool Status:");
|
||||
for tool in &self.tools {
|
||||
println!(
|
||||
" {} {}: {} {}",
|
||||
if tool.installed { "✓" } else { "✗" },
|
||||
tool.name,
|
||||
if tool.installed {
|
||||
"installed"
|
||||
} else {
|
||||
println!("⚠ Sudoers not configured. Run: sudo botserver install protection");
|
||||
"not installed"
|
||||
},
|
||||
if tool.sudo_configured {
|
||||
"(configured)"
|
||||
} else {
|
||||
"(not configured)"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
println!("\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -542,7 +805,7 @@ mod tests {
|
|||
let result = InstallResult::default();
|
||||
assert!(!result.success);
|
||||
assert!(result.packages_installed.is_empty());
|
||||
assert!(!result.sudoers_created);
|
||||
assert!(result.errors.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -553,30 +816,13 @@ mod tests {
|
|||
assert!(result.tools.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sudoers_content_has_placeholder() {
|
||||
assert!(SUDOERS_CONTENT.contains("{user}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sudoers_content_no_wildcards() {
|
||||
assert!(!SUDOERS_CONTENT.contains(" * "));
|
||||
assert!(!SUDOERS_CONTENT.lines().any(|l| l.trim().ends_with('*')));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_packages_list() {
|
||||
assert!(PACKAGES.contains(&"lynis"));
|
||||
assert!(PACKAGES.contains(&"rkhunter"));
|
||||
assert!(PACKAGES.contains(&"chkrootkit"));
|
||||
assert!(PACKAGES.contains(&"suricata"));
|
||||
assert!(PACKAGES.contains(&"clamav"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tool_verification_default() {
|
||||
let tool = ToolVerification::default();
|
||||
assert!(tool.name.is_empty());
|
||||
let tool = ToolVerification {
|
||||
name: "test".to_string(),
|
||||
installed: false,
|
||||
sudo_configured: false,
|
||||
};
|
||||
assert!(!tool.installed);
|
||||
assert!(!tool.sudo_configured);
|
||||
}
|
||||
|
|
@ -585,8 +831,7 @@ mod tests {
|
|||
fn test_uninstall_result_default() {
|
||||
let result = UninstallResult::default();
|
||||
assert!(!result.success);
|
||||
assert!(!result.sudoers_removed);
|
||||
assert!(result.message.is_empty());
|
||||
assert!(result.errors.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -594,4 +839,32 @@ mod tests {
|
|||
let installer = ProtectionInstaller::default();
|
||||
assert!(!installer.user.is_empty());
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
fn test_sudoers_content_has_placeholder() {
|
||||
assert!(SUDOERS_CONTENT.contains("{user}"));
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
fn test_sudoers_content_no_wildcards() {
|
||||
// Ensure no dangerous wildcards in sudoers
|
||||
assert!(!SUDOERS_CONTENT.contains(" ALL=(ALL) ALL"));
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
fn test_packages_list() {
|
||||
assert!(PACKAGES.len() > 0);
|
||||
assert!(PACKAGES.contains(&"lynis"));
|
||||
assert!(PACKAGES.contains(&"rkhunter"));
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn test_windows_tools_list() {
|
||||
assert!(WINDOWS_TOOLS.len() > 0);
|
||||
assert!(WINDOWS_TOOLS.iter().any(|t| t.0 == "Windows Defender"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue