vanguard-plugin 0.1.1

Plugin system for the Vanguard version manager
Documentation
use std::path::Path;
use thiserror::Error;

use crate::PluginMetadata;

/// Errors that can occur during plugin validation
#[derive(Error, Debug)]
pub enum ValidationError {
    /// Plugin signature verification failed
    #[error("Signature verification failed: {0}")]
    SignatureInvalid(String),

    /// Plugin is not compatible with current platform
    #[error("Platform not supported: {0}")]
    PlatformIncompatible(String),

    /// Plugin exceeds resource limits
    #[error("Resource limit exceeded: {0}")]
    ResourceLimitExceeded(String),

    /// Plugin failed sandbox constraints
    #[error("Sandbox constraint violation: {0}")]
    SandboxViolation(String),

    /// Plugin metadata is invalid
    #[error("Invalid metadata: {0}")]
    InvalidMetadata(String),
}

/// Result of plugin validation
pub type ValidationResult = Result<(), ValidationError>;

/// Plugin validator for security and compatibility checks
#[derive(Debug)]
pub struct PluginValidator {
    /// Maximum memory usage in bytes
    pub max_memory: usize,
    /// Whether to enforce strict signature checks
    pub strict_signatures: bool,
    /// Whether to enforce sandboxing
    pub enforce_sandbox: bool,
}

impl Default for PluginValidator {
    fn default() -> Self {
        Self::new()
    }
}

impl PluginValidator {
    /// Create a new plugin validator with default settings
    pub fn new() -> Self {
        Self {
            max_memory: 100 * 1024 * 1024, // 100MB
            strict_signatures: true,
            enforce_sandbox: true,
        }
    }

    /// Set maximum allowed memory usage
    pub fn with_max_memory(mut self, max_bytes: usize) -> Self {
        self.max_memory = max_bytes;
        self
    }

    /// Set whether to enforce strict signature verification
    pub fn with_strict_signatures(mut self, strict: bool) -> Self {
        self.strict_signatures = strict;
        self
    }

    /// Set whether to enforce sandbox constraints
    pub fn with_sandbox(mut self, enforce: bool) -> Self {
        self.enforce_sandbox = enforce;
        self
    }

    /// Validate a plugin binary and its metadata
    pub async fn validate_plugin(
        &self,
        binary_path: &Path,
        metadata: &PluginMetadata,
    ) -> ValidationResult {
        // Validate metadata first
        self.validate_metadata(metadata)?;

        // Verify plugin signature if enabled
        if self.strict_signatures {
            self.verify_signature(binary_path)?;
        }

        // Check platform compatibility
        self.check_platform_compatibility(metadata)?;

        // Validate resource usage
        self.validate_resources(binary_path)?;

        // Check sandbox constraints
        if self.enforce_sandbox {
            self.check_sandbox_constraints(binary_path)?;
        }

        Ok(())
    }

    /// Validate plugin metadata
    fn validate_metadata(&self, metadata: &PluginMetadata) -> ValidationResult {
        // For now just check required fields are present
        if metadata.name.is_empty() {
            return Err(ValidationError::InvalidMetadata("name is required".into()));
        }
        if metadata.version.is_empty() {
            return Err(ValidationError::InvalidMetadata(
                "version is required".into(),
            ));
        }
        Ok(())
    }

    /// Verify plugin binary signature
    fn verify_signature(&self, _binary_path: &Path) -> ValidationResult {
        // TODO: Implement actual signature verification
        // For now just return Ok
        Ok(())
    }

    /// Check if plugin is compatible with current platform
    fn check_platform_compatibility(&self, _metadata: &PluginMetadata) -> ValidationResult {
        // TODO: Implement actual platform checks
        // For now just return Ok
        Ok(())
    }

    /// Validate plugin resource usage
    fn validate_resources(&self, _binary_path: &Path) -> ValidationResult {
        // TODO: Implement actual resource validation
        // For now just return Ok
        Ok(())
    }

    /// Check plugin sandbox constraints
    fn check_sandbox_constraints(&self, _binary_path: &Path) -> ValidationResult {
        // TODO: Implement actual sandbox checks
        // For now just return Ok
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::path::PathBuf;

    fn create_test_metadata() -> PluginMetadata {
        PluginMetadata {
            name: "test-plugin".to_string(),
            version: "1.0.0".to_string(),
            description: "Test Plugin".to_string(),
            author: "Test Author".to_string(),
            min_vanguard_version: Some("0.1.0".to_string()),
            max_vanguard_version: Some("2.0.0".to_string()),
            dependencies: vec![],
        }
    }

    #[tokio::test]
    async fn test_validator_configuration() {
        let validator = PluginValidator::new()
            .with_max_memory(50 * 1024 * 1024)
            .with_strict_signatures(false)
            .with_sandbox(true);

        assert_eq!(validator.max_memory, 50 * 1024 * 1024);
        assert!(!validator.strict_signatures);
        assert!(validator.enforce_sandbox);
    }

    #[tokio::test]
    async fn test_metadata_validation() {
        let validator = PluginValidator::new();
        let metadata = create_test_metadata();

        assert!(validator.validate_metadata(&metadata).is_ok());

        // Test invalid metadata
        let invalid_metadata = PluginMetadata {
            name: "".to_string(),
            ..metadata.clone()
        };
        assert!(matches!(
            validator.validate_metadata(&invalid_metadata),
            Err(ValidationError::InvalidMetadata(_))
        ));
    }

    #[tokio::test]
    async fn test_full_validation() {
        let validator = PluginValidator::new();
        let metadata = create_test_metadata();
        let binary_path = PathBuf::from("test-binary");

        let result = validator.validate_plugin(&binary_path, &metadata).await;
        assert!(result.is_ok());
    }
}