FEAT: Complete OCI integration with container image generation capabilities - Add comprehensive OCI module (src/oci.rs) with full specification compliance - Implement OciImageBuilder for OSTree commit to container image conversion - Add OciRegistry for push/pull operations with authentication support - Create OciUtils for image validation, inspection, and format conversion - Support both OCI and Docker image formats with proper content addressing - Add SHA256 digest calculation for all image components - Implement gzip compression for filesystem layers CLI: Add complete OCI command suite - apt-ostree oci build - Build OCI images from OSTree commits - apt-ostree oci push - Push images to container registries - apt-ostree oci pull - Pull images from registries - apt-ostree oci inspect - Inspect image information - apt-ostree oci validate - Validate image integrity - apt-ostree oci convert - Convert between image formats COMPOSE: Enhance compose workflow with OCI integration - apt-ostree compose build-image - Convert deployments to OCI images - apt-ostree compose container-encapsulate - Generate container images from commits - apt-ostree compose image - Generate container images from treefiles ARCH: Add OCI layer to project architecture - Integrate OCI manager into lib.rs and main.rs - Add proper error handling and recovery mechanisms - Include comprehensive testing and validation - Create test script for OCI functionality validation DEPS: Add sha256 crate for content addressing - Update Cargo.toml with sha256 dependency - Ensure proper async/await handling with tokio::process::Command - Fix borrow checker issues and lifetime management DOCS: Update project documentation - Add OCI integration summary documentation - Update todo.md with milestone 9 completion - Include usage examples and workflow documentation
667 lines
No EOL
21 KiB
Rust
667 lines
No EOL
21 KiB
Rust
//! Security Hardening for APT-OSTree
|
|
//!
|
|
//! This module provides comprehensive security features including input validation,
|
|
//! privilege escalation protection, secure communication, and security scanning.
|
|
|
|
use std::collections::HashMap;
|
|
use std::path::{Path, PathBuf};
|
|
use std::sync::Arc;
|
|
use tokio::sync::Mutex;
|
|
use serde::{Serialize, Deserialize};
|
|
use tracing::{warn, error, debug, instrument};
|
|
use regex::Regex;
|
|
use lazy_static::lazy_static;
|
|
use std::os::unix::fs::PermissionsExt;
|
|
|
|
use crate::error::{AptOstreeError, AptOstreeResult};
|
|
|
|
/// Security configuration
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SecurityConfig {
|
|
/// Enable input validation
|
|
pub enable_input_validation: bool,
|
|
/// Enable privilege escalation protection
|
|
pub enable_privilege_protection: bool,
|
|
/// Enable secure communication
|
|
pub enable_secure_communication: bool,
|
|
/// Enable security scanning
|
|
pub enable_security_scanning: bool,
|
|
/// Allowed file paths for operations
|
|
pub allowed_paths: Vec<String>,
|
|
/// Blocked file paths
|
|
pub blocked_paths: Vec<String>,
|
|
/// Allowed package sources
|
|
pub allowed_sources: Vec<String>,
|
|
/// Blocked package sources
|
|
pub blocked_sources: Vec<String>,
|
|
/// Maximum file size for operations (bytes)
|
|
pub max_file_size: u64,
|
|
/// Maximum package count per operation
|
|
pub max_package_count: u32,
|
|
/// Security scan timeout (seconds)
|
|
pub security_scan_timeout: u64,
|
|
}
|
|
|
|
impl Default for SecurityConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
enable_input_validation: true,
|
|
enable_privilege_protection: true,
|
|
enable_secure_communication: true,
|
|
enable_security_scanning: true,
|
|
allowed_paths: vec![
|
|
"/var/lib/apt-ostree".to_string(),
|
|
"/etc/apt-ostree".to_string(),
|
|
"/var/cache/apt-ostree".to_string(),
|
|
"/var/log/apt-ostree".to_string(),
|
|
],
|
|
blocked_paths: vec![
|
|
"/etc/shadow".to_string(),
|
|
"/etc/passwd".to_string(),
|
|
"/etc/sudoers".to_string(),
|
|
"/root".to_string(),
|
|
"/home".to_string(),
|
|
],
|
|
allowed_sources: vec![
|
|
"deb.debian.org".to_string(),
|
|
"archive.ubuntu.com".to_string(),
|
|
"security.ubuntu.com".to_string(),
|
|
],
|
|
blocked_sources: vec![
|
|
"malicious.example.com".to_string(),
|
|
],
|
|
max_file_size: 1024 * 1024 * 100, // 100MB
|
|
max_package_count: 1000,
|
|
security_scan_timeout: 300, // 5 minutes
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Security validation result
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SecurityValidationResult {
|
|
pub is_valid: bool,
|
|
pub warnings: Vec<String>,
|
|
pub errors: Vec<String>,
|
|
pub security_score: u8, // 0-100
|
|
}
|
|
|
|
/// Security scanner for packages and files
|
|
#[derive(Debug, Clone)]
|
|
pub struct SecurityScanner {
|
|
pub vulnerabilities: Vec<Vulnerability>,
|
|
pub malware_signatures: Vec<String>,
|
|
pub suspicious_patterns: Vec<Regex>,
|
|
}
|
|
|
|
/// Vulnerability information
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Vulnerability {
|
|
pub id: String,
|
|
pub severity: VulnerabilitySeverity,
|
|
pub description: String,
|
|
pub cve_id: Option<String>,
|
|
pub affected_packages: Vec<String>,
|
|
pub remediation: String,
|
|
}
|
|
|
|
/// Vulnerability severity levels
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
pub enum VulnerabilitySeverity {
|
|
Low,
|
|
Medium,
|
|
High,
|
|
Critical,
|
|
}
|
|
|
|
/// Security manager
|
|
pub struct SecurityManager {
|
|
config: SecurityConfig,
|
|
scanner: SecurityScanner,
|
|
validation_cache: Arc<Mutex<HashMap<String, SecurityValidationResult>>>,
|
|
}
|
|
|
|
impl SecurityManager {
|
|
/// Create a new security manager
|
|
pub fn new(config: SecurityConfig) -> Self {
|
|
let scanner = SecurityScanner::new();
|
|
Self {
|
|
config,
|
|
scanner,
|
|
validation_cache: Arc::new(Mutex::new(HashMap::new())),
|
|
}
|
|
}
|
|
|
|
/// Validate input parameters
|
|
#[instrument(skip(self))]
|
|
pub async fn validate_input(&self, input: &str, input_type: &str) -> AptOstreeResult<SecurityValidationResult> {
|
|
debug!("Validating input: type={}, value={}", input_type, input);
|
|
|
|
let mut result = SecurityValidationResult {
|
|
is_valid: true,
|
|
warnings: Vec::new(),
|
|
errors: Vec::new(),
|
|
security_score: 100,
|
|
};
|
|
|
|
if !self.config.enable_input_validation {
|
|
return Ok(result);
|
|
}
|
|
|
|
// Check for path traversal attempts
|
|
if self.contains_path_traversal(input) {
|
|
result.is_valid = false;
|
|
result.errors.push("Path traversal attempt detected".to_string());
|
|
result.security_score = 0;
|
|
}
|
|
|
|
// Check for command injection attempts
|
|
if self.contains_command_injection(input) {
|
|
result.is_valid = false;
|
|
result.errors.push("Command injection attempt detected".to_string());
|
|
result.security_score = 0;
|
|
}
|
|
|
|
// Check for SQL injection attempts
|
|
if self.contains_sql_injection(input) {
|
|
result.is_valid = false;
|
|
result.errors.push("SQL injection attempt detected".to_string());
|
|
result.security_score = 0;
|
|
}
|
|
|
|
// Check for XSS attempts
|
|
if self.contains_xss(input) {
|
|
result.is_valid = false;
|
|
result.errors.push("XSS attempt detected".to_string());
|
|
result.security_score = 0;
|
|
}
|
|
|
|
// Validate file paths
|
|
if input_type == "file_path" {
|
|
if let Err(e) = self.validate_file_path(input) {
|
|
result.is_valid = false;
|
|
result.errors.push(format!("Invalid file path: {}", e));
|
|
result.security_score = 0;
|
|
}
|
|
}
|
|
|
|
// Validate package names
|
|
if input_type == "package_name" {
|
|
if let Err(e) = self.validate_package_name(input) {
|
|
result.is_valid = false;
|
|
result.errors.push(format!("Invalid package name: {}", e));
|
|
result.security_score = 0;
|
|
}
|
|
}
|
|
|
|
// Cache validation result
|
|
let cache_key = format!("{}:{}", input_type, input);
|
|
{
|
|
let mut cache = self.validation_cache.lock().await;
|
|
cache.insert(cache_key, result.clone());
|
|
}
|
|
|
|
if !result.is_valid {
|
|
error!("Input validation failed: {:?}", result);
|
|
}
|
|
|
|
Ok(result)
|
|
}
|
|
|
|
/// Validate file path security
|
|
pub fn validate_file_path(&self, path: &str) -> AptOstreeResult<()> {
|
|
let path_buf = PathBuf::from(path);
|
|
|
|
// Check for absolute path
|
|
if path_buf.is_absolute() {
|
|
// Check if path is in blocked paths
|
|
for blocked_path in &self.config.blocked_paths {
|
|
if path.starts_with(blocked_path) {
|
|
return Err(AptOstreeError::Security(
|
|
format!("Access to blocked path: {}", blocked_path)
|
|
));
|
|
}
|
|
}
|
|
|
|
// Check if path is in allowed paths
|
|
let mut allowed = false;
|
|
for allowed_path in &self.config.allowed_paths {
|
|
if path.starts_with(allowed_path) {
|
|
allowed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if !allowed {
|
|
return Err(AptOstreeError::Security(
|
|
format!("Access to unauthorized path: {}", path)
|
|
));
|
|
}
|
|
}
|
|
|
|
// Check for path traversal
|
|
if path.contains("..") || path.contains("//") {
|
|
return Err(AptOstreeError::Security(
|
|
"Path traversal attempt detected".to_string()
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Validate package name security
|
|
pub fn validate_package_name(&self, package_name: &str) -> AptOstreeResult<()> {
|
|
lazy_static! {
|
|
static ref PACKAGE_NAME_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9][a-zA-Z0-9+.-]*$").unwrap();
|
|
}
|
|
|
|
if !PACKAGE_NAME_REGEX.is_match(package_name) {
|
|
return Err(AptOstreeError::Security(
|
|
format!("Invalid package name format: {}", package_name)
|
|
));
|
|
}
|
|
|
|
// Check for suspicious patterns
|
|
let suspicious_patterns = [
|
|
"..", "//", "\\", "|", "&", ";", "`", "$(", "eval", "exec",
|
|
];
|
|
|
|
for pattern in &suspicious_patterns {
|
|
if package_name.contains(pattern) {
|
|
return Err(AptOstreeError::Security(
|
|
format!("Suspicious pattern in package name: {}", pattern)
|
|
));
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Check for path traversal attempts
|
|
fn contains_path_traversal(&self, input: &str) -> bool {
|
|
let traversal_patterns = [
|
|
"..", "//", "\\", "~", "..\\", "../", "..\\",
|
|
];
|
|
|
|
for pattern in &traversal_patterns {
|
|
if input.contains(pattern) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
/// Check for command injection attempts
|
|
fn contains_command_injection(&self, input: &str) -> bool {
|
|
let injection_patterns = [
|
|
"|", "&", ";", "`", "$(", "eval", "exec", "system", "popen",
|
|
"shell_exec", "passthru", "proc_open", "pcntl_exec",
|
|
];
|
|
|
|
for pattern in &injection_patterns {
|
|
if input.contains(pattern) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
/// Check for SQL injection attempts
|
|
fn contains_sql_injection(&self, input: &str) -> bool {
|
|
let sql_patterns = [
|
|
"SELECT", "INSERT", "UPDATE", "DELETE", "DROP", "CREATE",
|
|
"UNION", "OR", "AND", "WHERE", "FROM", "JOIN",
|
|
];
|
|
|
|
for pattern in &sql_patterns {
|
|
if input.to_uppercase().contains(pattern) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
/// Check for XSS attempts
|
|
fn contains_xss(&self, input: &str) -> bool {
|
|
let xss_patterns = [
|
|
"<script", "javascript:", "onload=", "onerror=", "onclick=",
|
|
"onmouseover=", "onfocus=", "onblur=", "onchange=",
|
|
];
|
|
|
|
for pattern in &xss_patterns {
|
|
if input.to_lowercase().contains(pattern) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
/// Protect against privilege escalation
|
|
#[instrument(skip(self))]
|
|
pub async fn protect_privilege_escalation(&self) -> AptOstreeResult<()> {
|
|
if !self.config.enable_privilege_protection {
|
|
return Ok(());
|
|
}
|
|
|
|
debug!("Checking privilege escalation protection");
|
|
|
|
// Check if running as root
|
|
if unsafe { libc::geteuid() == 0 } {
|
|
// Verify we're not in a privileged context that could be exploited
|
|
if self.is_in_dangerous_context() {
|
|
return Err(AptOstreeError::Security(
|
|
"Running in potentially dangerous privileged context".to_string()
|
|
));
|
|
}
|
|
}
|
|
|
|
// Check for setuid binaries
|
|
if self.has_setuid_binaries() {
|
|
warn!("Setuid binaries detected - potential security risk");
|
|
}
|
|
|
|
// Check for world-writable directories
|
|
if self.has_world_writable_dirs() {
|
|
warn!("World-writable directories detected - potential security risk");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Check if running in dangerous context
|
|
fn is_in_dangerous_context(&self) -> bool {
|
|
// Check environment variables
|
|
let dangerous_vars = [
|
|
"LD_PRELOAD", "LD_LIBRARY_PATH", "PYTHONPATH", "PERL5LIB",
|
|
];
|
|
|
|
for var in &dangerous_vars {
|
|
if std::env::var(var).is_ok() {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Check if running in container
|
|
if self.is_container_environment() {
|
|
return true;
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
/// Check for setuid binaries
|
|
fn has_setuid_binaries(&self) -> bool {
|
|
let setuid_paths = [
|
|
"/usr/bin/sudo", "/usr/bin/su", "/usr/bin/passwd",
|
|
"/usr/bin/chsh", "/usr/bin/chfn", "/usr/bin/gpasswd",
|
|
];
|
|
|
|
for path in &setuid_paths {
|
|
if Path::new(path).exists() {
|
|
if let Ok(metadata) = std::fs::metadata(path) {
|
|
let mode = metadata.permissions().mode();
|
|
if (mode & 0o4000) != 0 {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
/// Check for world-writable directories
|
|
fn has_world_writable_dirs(&self) -> bool {
|
|
let world_writable_paths = [
|
|
"/tmp", "/var/tmp", "/dev/shm",
|
|
];
|
|
|
|
for path in &world_writable_paths {
|
|
if let Ok(metadata) = std::fs::metadata(path) {
|
|
let mode = metadata.permissions().mode();
|
|
if (mode & 0o0002) != 0 {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
/// Check if running in container environment
|
|
fn is_container_environment(&self) -> bool {
|
|
let container_indicators = [
|
|
"/.dockerenv",
|
|
"/proc/1/cgroup",
|
|
"/proc/self/cgroup",
|
|
];
|
|
|
|
for indicator in &container_indicators {
|
|
if Path::new(indicator).exists() {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Check cgroup for container indicators
|
|
if let Ok(content) = std::fs::read_to_string("/proc/self/cgroup") {
|
|
if content.contains("docker") || content.contains("lxc") || content.contains("systemd") {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
/// Scan package for security vulnerabilities
|
|
#[instrument(skip(self))]
|
|
pub async fn scan_package(&self, package_name: &str, package_path: &Path) -> AptOstreeResult<Vec<Vulnerability>> {
|
|
if !self.config.enable_security_scanning {
|
|
return Ok(Vec::new());
|
|
}
|
|
|
|
debug!("Scanning package for vulnerabilities: {}", package_name);
|
|
|
|
let mut vulnerabilities = Vec::new();
|
|
|
|
// Check file size
|
|
if let Ok(metadata) = std::fs::metadata(package_path) {
|
|
if metadata.len() > self.config.max_file_size {
|
|
vulnerabilities.push(Vulnerability {
|
|
id: "FILE_SIZE_EXCEEDED".to_string(),
|
|
severity: VulnerabilitySeverity::Medium,
|
|
description: format!("Package file size exceeds limit: {} bytes", metadata.len()),
|
|
cve_id: None,
|
|
affected_packages: vec![package_name.to_string()],
|
|
remediation: "Reduce package size or increase limit".to_string(),
|
|
});
|
|
}
|
|
}
|
|
|
|
// Check for known vulnerabilities (placeholder for real vulnerability database)
|
|
if let Some(vuln) = self.check_known_vulnerabilities(package_name).await {
|
|
vulnerabilities.push(vuln);
|
|
}
|
|
|
|
// Check for malware signatures
|
|
if let Some(vuln) = self.scan_for_malware(package_path).await {
|
|
vulnerabilities.push(vuln);
|
|
}
|
|
|
|
// Check for suspicious patterns
|
|
if let Some(vuln) = self.scan_for_suspicious_patterns(package_path).await {
|
|
vulnerabilities.push(vuln);
|
|
}
|
|
|
|
if !vulnerabilities.is_empty() {
|
|
warn!("Security vulnerabilities found in package {}: {:?}", package_name, vulnerabilities);
|
|
}
|
|
|
|
Ok(vulnerabilities)
|
|
}
|
|
|
|
/// Check for known vulnerabilities
|
|
async fn check_known_vulnerabilities(&self, package_name: &str) -> Option<Vulnerability> {
|
|
// This would integrate with a real vulnerability database
|
|
// For now, return None as placeholder
|
|
None
|
|
}
|
|
|
|
/// Scan for malware signatures
|
|
async fn scan_for_malware(&self, package_path: &Path) -> Option<Vulnerability> {
|
|
// This would integrate with malware scanning tools
|
|
// For now, return None as placeholder
|
|
None
|
|
}
|
|
|
|
/// Scan for suspicious patterns
|
|
async fn scan_for_suspicious_patterns(&self, package_path: &Path) -> Option<Vulnerability> {
|
|
// This would scan file contents for suspicious patterns
|
|
// For now, return None as placeholder
|
|
None
|
|
}
|
|
|
|
/// Validate secure communication
|
|
#[instrument(skip(self))]
|
|
pub async fn validate_secure_communication(&self, endpoint: &str) -> AptOstreeResult<()> {
|
|
if !self.config.enable_secure_communication {
|
|
return Ok(());
|
|
}
|
|
|
|
debug!("Validating secure communication to: {}", endpoint);
|
|
|
|
// Check for HTTPS
|
|
if !endpoint.starts_with("https://") {
|
|
return Err(AptOstreeError::Security(
|
|
"Non-HTTPS communication not allowed".to_string()
|
|
));
|
|
}
|
|
|
|
// Check for allowed sources
|
|
let mut allowed = false;
|
|
for allowed_source in &self.config.allowed_sources {
|
|
if endpoint.contains(allowed_source) {
|
|
allowed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if !allowed {
|
|
return Err(AptOstreeError::Security(
|
|
format!("Communication to unauthorized endpoint: {}", endpoint)
|
|
));
|
|
}
|
|
|
|
// Check for blocked sources
|
|
for blocked_source in &self.config.blocked_sources {
|
|
if endpoint.contains(blocked_source) {
|
|
return Err(AptOstreeError::Security(
|
|
format!("Communication to blocked endpoint: {}", blocked_source)
|
|
));
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Get security report
|
|
pub async fn get_security_report(&self) -> AptOstreeResult<String> {
|
|
let mut report = String::new();
|
|
report.push_str("=== APT-OSTree Security Report ===\n\n");
|
|
|
|
// System security status
|
|
report.push_str("System Security Status:\n");
|
|
report.push_str(&format!("- Running as root: {}\n", unsafe { libc::geteuid() == 0 }));
|
|
report.push_str(&format!("- Container environment: {}\n", self.is_container_environment()));
|
|
report.push_str(&format!("- Setuid binaries detected: {}\n", self.has_setuid_binaries()));
|
|
report.push_str(&format!("- World-writable directories: {}\n", self.has_world_writable_dirs()));
|
|
|
|
// Configuration status
|
|
report.push_str("\nSecurity Configuration:\n");
|
|
report.push_str(&format!("- Input validation: {}\n", self.config.enable_input_validation));
|
|
report.push_str(&format!("- Privilege protection: {}\n", self.config.enable_privilege_protection));
|
|
report.push_str(&format!("- Secure communication: {}\n", self.config.enable_secure_communication));
|
|
report.push_str(&format!("- Security scanning: {}\n", self.config.enable_security_scanning));
|
|
|
|
// Validation cache statistics
|
|
{
|
|
let cache = self.validation_cache.lock().await;
|
|
report.push_str(&format!("\nValidation Cache:\n"));
|
|
report.push_str(&format!("- Cached validations: {}\n", cache.len()));
|
|
}
|
|
|
|
Ok(report)
|
|
}
|
|
}
|
|
|
|
impl SecurityScanner {
|
|
/// Create a new security scanner
|
|
pub fn new() -> Self {
|
|
let suspicious_patterns = vec![
|
|
Regex::new(r"\.\./").unwrap(),
|
|
Regex::new(r"\.\.\\").unwrap(),
|
|
Regex::new(r"[|&;`$]").unwrap(),
|
|
Regex::new(r"eval\s*\(").unwrap(),
|
|
Regex::new(r"exec\s*\(").unwrap(),
|
|
];
|
|
|
|
Self {
|
|
vulnerabilities: Vec::new(),
|
|
malware_signatures: Vec::new(),
|
|
suspicious_patterns,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[tokio::test]
|
|
async fn test_input_validation() {
|
|
let config = SecurityConfig::default();
|
|
let security_manager = SecurityManager::new(config);
|
|
|
|
// Test valid input
|
|
let result = security_manager.validate_input("valid-package-name", "package_name").await.unwrap();
|
|
assert!(result.is_valid);
|
|
|
|
// Test path traversal
|
|
let result = security_manager.validate_input("../../../etc/passwd", "file_path").await.unwrap();
|
|
assert!(!result.is_valid);
|
|
|
|
// Test command injection
|
|
let result = security_manager.validate_input("package; rm -rf /", "package_name").await.unwrap();
|
|
assert!(!result.is_valid);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_file_path_validation() {
|
|
let config = SecurityConfig::default();
|
|
let security_manager = SecurityManager::new(config);
|
|
|
|
// Test allowed path
|
|
assert!(security_manager.validate_file_path("/var/lib/apt-ostree/test").is_ok());
|
|
|
|
// Test blocked path
|
|
assert!(security_manager.validate_file_path("/etc/shadow").is_err());
|
|
|
|
// Test path traversal
|
|
assert!(security_manager.validate_file_path("../../../etc/passwd").is_err());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_package_name_validation() {
|
|
let config = SecurityConfig::default();
|
|
let security_manager = SecurityManager::new(config);
|
|
|
|
// Test valid package name
|
|
assert!(security_manager.validate_package_name("valid-package").is_ok());
|
|
|
|
// Test invalid package name
|
|
assert!(security_manager.validate_package_name("package; rm -rf /").is_err());
|
|
}
|
|
}
|