- Add rust_xlsxwriter for Excel export with formatting support - Add docx-rs for Word document import/export with HTML conversion - Add PPTX export support with slides, shapes, and text elements - Refactor sheet module into 7 files (types, formulas, handlers, etc) - Refactor docs module into 6 files (types, handlers, storage, etc) - Refactor slides module into 6 files (types, handlers, storage, etc) - Fix collaboration modules (borrow issues, rand compatibility) - Add ooxmlsdk dependency for future Office 2021 features - Fix type mismatches in slides storage - Update security protection API router type Features: - Excel: Read xlsx/xlsm/xls, write xlsx with styles - Word: Read/write docx with formatting preservation - PowerPoint: Write pptx with slides, shapes, text - Real-time collaboration via WebSocket (already working) - Theme-aware UI with --sentient-* CSS variables
329 lines
10 KiB
Rust
329 lines
10 KiB
Rust
use crate::security::protection::{ProtectionManager, ProtectionTool};
|
|
use crate::security::protection::manager::ProtectionConfig;
|
|
use crate::shared::state::AppState;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::sync::Arc;
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SecurityToolResult {
|
|
pub tool: String,
|
|
pub success: bool,
|
|
pub installed: bool,
|
|
pub version: Option<String>,
|
|
pub running: Option<bool>,
|
|
pub message: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SecurityScanResult {
|
|
pub tool: String,
|
|
pub success: bool,
|
|
pub status: String,
|
|
pub findings_count: usize,
|
|
pub warnings_count: usize,
|
|
pub score: Option<i32>,
|
|
pub report_path: Option<String>,
|
|
}
|
|
|
|
pub async fn security_tool_status(
|
|
_state: Arc<AppState>,
|
|
tool_name: &str,
|
|
) -> Result<SecurityToolResult, String> {
|
|
let tool = parse_tool_name(tool_name)?;
|
|
let manager = ProtectionManager::new(ProtectionConfig::default());
|
|
|
|
match manager.check_tool_status(tool).await {
|
|
Ok(status) => Ok(SecurityToolResult {
|
|
tool: tool_name.to_lowercase(),
|
|
success: true,
|
|
installed: status.installed,
|
|
version: status.version,
|
|
running: status.service_running,
|
|
message: if status.installed {
|
|
"Tool is installed".to_string()
|
|
} else {
|
|
"Tool is not installed".to_string()
|
|
},
|
|
}),
|
|
Err(e) => Ok(SecurityToolResult {
|
|
tool: tool_name.to_lowercase(),
|
|
success: false,
|
|
installed: false,
|
|
version: None,
|
|
running: None,
|
|
message: format!("Failed to check status: {e}"),
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub async fn security_run_scan(
|
|
_state: Arc<AppState>,
|
|
tool_name: &str,
|
|
) -> Result<SecurityScanResult, String> {
|
|
let tool = parse_tool_name(tool_name)?;
|
|
let manager = ProtectionManager::new(ProtectionConfig::default());
|
|
|
|
match manager.run_scan(tool).await {
|
|
Ok(result) => Ok(SecurityScanResult {
|
|
tool: tool_name.to_lowercase(),
|
|
success: true,
|
|
status: format!("{:?}", result.status),
|
|
findings_count: result.findings.len(),
|
|
warnings_count: result.warnings as usize,
|
|
score: None,
|
|
report_path: result.report_path,
|
|
}),
|
|
Err(error) => {
|
|
log::error!("Security scan failed for {tool_name}: {error}");
|
|
Ok(SecurityScanResult {
|
|
tool: tool_name.to_lowercase(),
|
|
success: false,
|
|
status: format!("error: {error}"),
|
|
findings_count: 0,
|
|
warnings_count: 0,
|
|
score: None,
|
|
report_path: None,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn security_get_report(
|
|
_state: Arc<AppState>,
|
|
tool_name: &str,
|
|
) -> Result<String, String> {
|
|
let tool = parse_tool_name(tool_name)?;
|
|
let manager = ProtectionManager::new(ProtectionConfig::default());
|
|
|
|
manager
|
|
.get_report(tool)
|
|
.await
|
|
.map_err(|e| format!("Failed to get report: {e}"))
|
|
}
|
|
|
|
pub async fn security_update_definitions(
|
|
_state: Arc<AppState>,
|
|
tool_name: &str,
|
|
) -> Result<SecurityToolResult, String> {
|
|
let tool = parse_tool_name(tool_name)?;
|
|
let manager = ProtectionManager::new(ProtectionConfig::default());
|
|
|
|
match manager.update_definitions(tool).await {
|
|
Ok(()) => Ok(SecurityToolResult {
|
|
tool: tool_name.to_lowercase(),
|
|
success: true,
|
|
installed: true,
|
|
version: None,
|
|
running: None,
|
|
message: "Definitions updated successfully".to_string(),
|
|
}),
|
|
Err(e) => Ok(SecurityToolResult {
|
|
tool: tool_name.to_lowercase(),
|
|
success: false,
|
|
installed: true,
|
|
version: None,
|
|
running: None,
|
|
message: format!("Failed to update definitions: {e}"),
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub async fn security_start_service(
|
|
_state: Arc<AppState>,
|
|
tool_name: &str,
|
|
) -> Result<SecurityToolResult, String> {
|
|
let tool = parse_tool_name(tool_name)?;
|
|
let manager = ProtectionManager::new(ProtectionConfig::default());
|
|
|
|
match manager.start_service(tool).await {
|
|
Ok(()) => Ok(SecurityToolResult {
|
|
tool: tool_name.to_lowercase(),
|
|
success: true,
|
|
installed: true,
|
|
version: None,
|
|
running: Some(true),
|
|
message: "Service started successfully".to_string(),
|
|
}),
|
|
Err(e) => Ok(SecurityToolResult {
|
|
tool: tool_name.to_lowercase(),
|
|
success: false,
|
|
installed: true,
|
|
version: None,
|
|
running: Some(false),
|
|
message: format!("Failed to start service: {e}"),
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub async fn security_stop_service(
|
|
_state: Arc<AppState>,
|
|
tool_name: &str,
|
|
) -> Result<SecurityToolResult, String> {
|
|
let tool = parse_tool_name(tool_name)?;
|
|
let manager = ProtectionManager::new(ProtectionConfig::default());
|
|
|
|
match manager.stop_service(tool).await {
|
|
Ok(()) => Ok(SecurityToolResult {
|
|
tool: tool_name.to_lowercase(),
|
|
success: true,
|
|
installed: true,
|
|
version: None,
|
|
running: Some(false),
|
|
message: "Service stopped successfully".to_string(),
|
|
}),
|
|
Err(e) => Ok(SecurityToolResult {
|
|
tool: tool_name.to_lowercase(),
|
|
success: false,
|
|
installed: true,
|
|
version: None,
|
|
running: None,
|
|
message: format!("Failed to stop service: {e}"),
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub async fn security_install_tool(
|
|
_state: Arc<AppState>,
|
|
tool_name: &str,
|
|
) -> Result<SecurityToolResult, String> {
|
|
let tool = parse_tool_name(tool_name)?;
|
|
let manager = ProtectionManager::new(ProtectionConfig::default());
|
|
|
|
match manager.install_tool(tool).await {
|
|
Ok(()) => Ok(SecurityToolResult {
|
|
tool: tool_name.to_lowercase(),
|
|
success: true,
|
|
installed: true,
|
|
version: None,
|
|
running: None,
|
|
message: "Tool installed successfully".to_string(),
|
|
}),
|
|
Err(e) => Ok(SecurityToolResult {
|
|
tool: tool_name.to_lowercase(),
|
|
success: false,
|
|
installed: false,
|
|
version: None,
|
|
running: None,
|
|
message: format!("Failed to install tool: {e}"),
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub async fn security_hardening_score(_state: Arc<AppState>) -> Result<i32, String> {
|
|
let manager = ProtectionManager::new(ProtectionConfig::default());
|
|
|
|
match manager.run_scan(ProtectionTool::Lynis).await {
|
|
Ok(_result) => Ok(0),
|
|
Err(e) => Err(format!("Failed to get hardening score: {e}")),
|
|
}
|
|
}
|
|
|
|
pub fn security_tool_is_installed(status: &SecurityToolResult) -> bool {
|
|
status.installed
|
|
}
|
|
|
|
pub fn security_service_is_running(status: &SecurityToolResult) -> bool {
|
|
status.running.unwrap_or(false)
|
|
}
|
|
|
|
fn parse_tool_name(name: &str) -> Result<ProtectionTool, String> {
|
|
ProtectionTool::from_str(name)
|
|
.ok_or_else(|| format!("Unknown security tool: {name}. Valid tools: lynis, rkhunter, chkrootkit, suricata, lmd, clamav"))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_parse_tool_name_valid() {
|
|
assert!(parse_tool_name("lynis").is_ok());
|
|
assert!(parse_tool_name("LYNIS").is_ok());
|
|
assert!(parse_tool_name("Lynis").is_ok());
|
|
assert!(parse_tool_name("rkhunter").is_ok());
|
|
assert!(parse_tool_name("chkrootkit").is_ok());
|
|
assert!(parse_tool_name("suricata").is_ok());
|
|
assert!(parse_tool_name("lmd").is_ok());
|
|
assert!(parse_tool_name("clamav").is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_tool_name_invalid() {
|
|
assert!(parse_tool_name("unknown").is_err());
|
|
assert!(parse_tool_name("").is_err());
|
|
assert!(parse_tool_name("invalid_tool").is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_security_tool_is_installed() {
|
|
let installed = SecurityToolResult {
|
|
tool: "lynis".to_string(),
|
|
success: true,
|
|
installed: true,
|
|
version: Some("3.0.9".to_string()),
|
|
running: None,
|
|
message: "Tool is installed".to_string(),
|
|
};
|
|
assert!(security_tool_is_installed(&installed));
|
|
|
|
let not_installed = SecurityToolResult {
|
|
tool: "lynis".to_string(),
|
|
success: true,
|
|
installed: false,
|
|
version: None,
|
|
running: None,
|
|
message: "Tool is not installed".to_string(),
|
|
};
|
|
assert!(!security_tool_is_installed(¬_installed));
|
|
}
|
|
|
|
#[test]
|
|
fn test_security_service_is_running() {
|
|
let running = SecurityToolResult {
|
|
tool: "suricata".to_string(),
|
|
success: true,
|
|
installed: true,
|
|
version: None,
|
|
running: Some(true),
|
|
message: "Service running".to_string(),
|
|
};
|
|
assert!(security_service_is_running(&running));
|
|
|
|
let stopped = SecurityToolResult {
|
|
tool: "suricata".to_string(),
|
|
success: true,
|
|
installed: true,
|
|
version: None,
|
|
running: Some(false),
|
|
message: "Service stopped".to_string(),
|
|
};
|
|
assert!(!security_service_is_running(&stopped));
|
|
|
|
let unknown = SecurityToolResult {
|
|
tool: "lynis".to_string(),
|
|
success: true,
|
|
installed: true,
|
|
version: None,
|
|
running: None,
|
|
message: "No service".to_string(),
|
|
};
|
|
assert!(!security_service_is_running(&unknown));
|
|
}
|
|
|
|
#[test]
|
|
fn test_security_scan_result_serialization() {
|
|
let result = SecurityScanResult {
|
|
tool: "lynis".to_string(),
|
|
success: true,
|
|
status: "completed".to_string(),
|
|
findings_count: 5,
|
|
warnings_count: 12,
|
|
score: Some(78),
|
|
report_path: Some("/var/log/lynis-report.dat".to_string()),
|
|
};
|
|
|
|
let json = serde_json::to_string(&result).expect("Failed to serialize");
|
|
assert!(json.contains("\"tool\":\"lynis\""));
|
|
assert!(json.contains("\"score\":78"));
|
|
}
|
|
}
|