use super::core_traits::Plugin;
use super::security::{DigitalSignature, Permission, PublisherInfo, SecurityPolicy};
use super::types_config::PluginMetadata;
use crate::error::Result;
use std::collections::HashMap;
#[derive(Debug)]
pub struct PluginValidator {
security_policy: SecurityPolicy,
dependency_resolver: DependencyResolver,
#[allow(dead_code)]
code_analyzer: CodeAnalyzer,
trust_store: TrustStore,
}
impl PluginValidator {
pub fn new() -> Self {
Self {
security_policy: SecurityPolicy::default(),
dependency_resolver: DependencyResolver::new(),
code_analyzer: CodeAnalyzer::new(),
trust_store: TrustStore::new(),
}
}
pub fn with_security_policy(security_policy: SecurityPolicy) -> Self {
Self {
security_policy,
dependency_resolver: DependencyResolver::new(),
code_analyzer: CodeAnalyzer::new(),
trust_store: TrustStore::new(),
}
}
pub fn validate_comprehensive(
&self,
plugin: &dyn Plugin,
manifest: &PluginManifest,
) -> Result<ValidationReport> {
let mut report = ValidationReport::new();
self.validate_basic(plugin, &mut report)?;
self.validate_security(manifest, &mut report)?;
self.validate_dependencies(manifest, &mut report)?;
self.validate_code_safety(manifest, &mut report)?;
self.validate_trust(manifest, &mut report)?;
Ok(report)
}
fn validate_basic(&self, plugin: &dyn Plugin, report: &mut ValidationReport) -> Result<()> {
let metadata = plugin.metadata();
if metadata.name.is_empty() {
report.add_error(ValidationError::InvalidMetadata(
"Plugin name cannot be empty".to_string(),
));
}
if metadata.version.is_empty() {
report.add_error(ValidationError::InvalidMetadata(
"Plugin version cannot be empty".to_string(),
));
}
if metadata.author.is_empty() {
report.add_error(ValidationError::InvalidMetadata(
"Plugin author cannot be empty".to_string(),
));
}
if !self.is_valid_version(&metadata.version) {
report.add_error(ValidationError::InvalidVersion(metadata.version.clone()));
}
if !self.is_compatible_sdk_version(&metadata.min_sdk_version) {
report.add_error(ValidationError::IncompatibleSdkVersion(
metadata.min_sdk_version.clone(),
));
}
report.add_check(ValidationCheck::BasicMetadata, ValidationResult::Passed);
Ok(())
}
fn validate_security(
&self,
manifest: &PluginManifest,
report: &mut ValidationReport,
) -> Result<()> {
for permission in &manifest.permissions {
if self.security_policy.is_dangerous_permission(permission) {
report.add_warning(ValidationWarning::DangerousPermission(permission.clone()));
}
}
if let Some(ref api_usage) = manifest.api_usage {
for api_call in &api_usage.calls {
if self.security_policy.is_restricted_api(api_call) {
report.add_error(ValidationError::RestrictedApiUsage(api_call.clone()));
}
}
}
if manifest.contains_unsafe_code {
if !self.security_policy.allow_unsafe_code {
report.add_error(ValidationError::UnsafeCodeNotAllowed);
} else {
report.add_warning(ValidationWarning::UnsafeCodeDetected);
}
}
report.add_check(ValidationCheck::Security, ValidationResult::Passed);
Ok(())
}
fn validate_dependencies(
&self,
manifest: &PluginManifest,
report: &mut ValidationReport,
) -> Result<()> {
for dependency in &manifest.dependencies {
if !self.dependency_resolver.is_available(dependency) {
report.add_error(ValidationError::MissingDependency(dependency.clone()));
continue;
}
if !self.dependency_resolver.is_compatible_version(dependency) {
report.add_error(ValidationError::IncompatibleDependency(dependency.clone()));
continue;
}
if let Some(vulnerabilities) =
self.dependency_resolver.check_vulnerabilities(dependency)
{
for vuln in vulnerabilities {
report.add_error(ValidationError::VulnerableDependency(
dependency.clone(),
vuln,
));
}
}
}
report.add_check(ValidationCheck::Dependencies, ValidationResult::Passed);
Ok(())
}
fn validate_code_safety(
&self,
manifest: &PluginManifest,
report: &mut ValidationReport,
) -> Result<()> {
if let Some(ref code_info) = manifest.code_analysis {
if code_info.cyclomatic_complexity > self.security_policy.max_complexity as usize {
report.add_warning(ValidationWarning::HighComplexity(
code_info.cyclomatic_complexity,
));
}
for pattern in &code_info.suspicious_patterns {
report.add_warning(ValidationWarning::SuspiciousPattern(pattern.clone()));
}
if code_info.potential_memory_issues > 0 {
report.add_error(ValidationError::MemorySafetyIssue(
code_info.potential_memory_issues,
));
}
}
report.add_check(ValidationCheck::CodeSafety, ValidationResult::Passed);
Ok(())
}
fn validate_trust(
&self,
manifest: &PluginManifest,
report: &mut ValidationReport,
) -> Result<()> {
if let Some(ref signature) = manifest.signature {
match self
.trust_store
.verify_signature(&manifest.content_hash, signature)
{
Ok(true) => report.add_check(ValidationCheck::Signature, ValidationResult::Passed),
Ok(false) => report.add_error(ValidationError::InvalidSignature),
Err(e) => {
report.add_error(ValidationError::SignatureVerificationFailed(e.to_string()))
}
}
} else if self.security_policy.require_signatures {
report.add_error(ValidationError::MissingSignature);
}
let trust_level = self.trust_store.get_publisher_trust(&manifest.publisher);
if trust_level < self.security_policy.min_trust_level as f32 {
report.add_warning(ValidationWarning::LowTrustPublisher(trust_level));
}
report.add_check(ValidationCheck::Trust, ValidationResult::Passed);
Ok(())
}
fn is_valid_version(&self, version: &str) -> bool {
let parts: Vec<&str> = version.split('.').collect();
parts.len() >= 2 && parts.len() <= 3 && parts.iter().all(|p| p.parse::<u32>().is_ok())
}
fn is_compatible_sdk_version(&self, min_version: &str) -> bool {
!min_version.is_empty()
}
}
impl Default for PluginValidator {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PluginManifest {
pub metadata: PluginMetadata,
pub permissions: Vec<Permission>,
pub api_usage: Option<ApiUsageInfo>,
pub contains_unsafe_code: bool,
pub dependencies: Vec<Dependency>,
pub code_analysis: Option<CodeAnalysisInfo>,
pub signature: Option<DigitalSignature>,
pub content_hash: String,
pub publisher: PublisherInfo,
pub marketplace: MarketplaceInfo,
}
#[derive(Debug, Clone)]
pub struct ValidationReport {
pub errors: Vec<ValidationError>,
pub warnings: Vec<ValidationWarning>,
pub checks: HashMap<ValidationCheck, ValidationResult>,
pub status: ValidationStatus,
}
impl ValidationReport {
pub fn new() -> Self {
Self {
errors: Vec::new(),
warnings: Vec::new(),
checks: HashMap::new(),
status: ValidationStatus::Pending,
}
}
pub fn add_error(&mut self, error: ValidationError) {
self.errors.push(error);
self.status = ValidationStatus::Failed;
}
pub fn add_warning(&mut self, warning: ValidationWarning) {
self.warnings.push(warning);
}
pub fn add_check(&mut self, check: ValidationCheck, result: ValidationResult) {
self.checks.insert(check, result);
if self.status == ValidationStatus::Pending && self.errors.is_empty() {
self.status = ValidationStatus::Passed;
}
}
pub fn has_errors(&self) -> bool {
!self.errors.is_empty()
}
pub fn has_warnings(&self) -> bool {
!self.warnings.is_empty()
}
pub fn get_errors(&self) -> &[ValidationError] {
&self.errors
}
pub fn get_warnings(&self) -> &[ValidationWarning] {
&self.warnings
}
pub fn status(&self) -> ValidationStatus {
self.status
}
pub fn get_checks(&self) -> &HashMap<ValidationCheck, ValidationResult> {
&self.checks
}
}
#[derive(Debug, Clone)]
pub enum ValidationError {
InvalidMetadata(String),
InvalidVersion(String),
IncompatibleSdkVersion(String),
MissingDependency(Dependency),
IncompatibleDependency(Dependency),
VulnerableDependency(Dependency, String),
RestrictedApiUsage(String),
UnsafeCodeNotAllowed,
MemorySafetyIssue(usize),
InvalidSignature,
MissingSignature,
SignatureVerificationFailed(String),
}
#[derive(Debug, Clone)]
pub enum ValidationWarning {
DangerousPermission(Permission),
UnsafeCodeDetected,
HighComplexity(usize),
SuspiciousPattern(String),
LowTrustPublisher(f32),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ValidationCheck {
BasicMetadata,
Security,
Dependencies,
CodeSafety,
Trust,
Signature,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ValidationResult {
Passed,
Failed,
Skipped,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValidationStatus {
Pending,
Passed,
Failed,
}
#[derive(Debug, Clone)]
pub struct Vulnerability {
pub id: String,
pub severity: VulnerabilitySeverity,
pub description: String,
pub affected_versions: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VulnerabilitySeverity {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ApiUsageInfo {
pub calls: Vec<String>,
pub network_access: Vec<String>,
pub filesystem_access: Vec<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CodeAnalysisInfo {
pub cyclomatic_complexity: usize,
pub suspicious_patterns: Vec<String>,
pub potential_memory_issues: usize,
pub lines_of_code: usize,
pub test_coverage: f32,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Dependency {
pub name: String,
pub version: String,
pub optional: bool,
pub features: Vec<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct MarketplaceInfo {
pub url: String,
pub downloads: u64,
pub rating: f32,
pub reviews: u32,
pub last_updated: String,
}
#[derive(Debug)]
pub struct DependencyResolver {
available_deps: HashMap<String, String>,
}
impl Default for DependencyResolver {
fn default() -> Self {
Self::new()
}
}
impl DependencyResolver {
pub fn new() -> Self {
Self {
available_deps: HashMap::new(),
}
}
pub fn is_available(&self, dependency: &Dependency) -> bool {
self.available_deps.contains_key(&dependency.name)
}
pub fn is_compatible_version(&self, _dependency: &Dependency) -> bool {
true
}
pub fn check_vulnerabilities(&self, _dependency: &Dependency) -> Option<Vec<String>> {
None
}
}
#[derive(Debug)]
pub struct CodeAnalyzer {
#[allow(dead_code)]
rules: Vec<AnalysisRule>,
}
impl Default for CodeAnalyzer {
fn default() -> Self {
Self::new()
}
}
impl CodeAnalyzer {
pub fn new() -> Self {
Self { rules: Vec::new() }
}
}
#[derive(Debug)]
pub struct TrustStore {
trusted_publishers: HashMap<String, f32>,
}
impl Default for TrustStore {
fn default() -> Self {
Self::new()
}
}
impl TrustStore {
pub fn new() -> Self {
Self {
trusted_publishers: HashMap::new(),
}
}
pub fn verify_signature(
&self,
_content_hash: &str,
_signature: &DigitalSignature,
) -> Result<bool> {
Ok(true)
}
pub fn get_publisher_trust(&self, publisher: &PublisherInfo) -> f32 {
self.trusted_publishers
.get(&publisher.name)
.copied()
.unwrap_or(0.5)
}
}
#[derive(Debug, Clone)]
pub struct AnalysisRule {
pub name: String,
pub description: String,
pub pattern: String,
}
impl Default for ValidationReport {
fn default() -> Self {
Self::new()
}
}