1#[allow(dead_code)]
7use super::core::*;
8use super::registry::*;
9use crate::error::{OptimError, Result};
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::fmt::Debug;
13use std::path::{Path, PathBuf};
14
15#[cfg(feature = "crypto")]
16use rsa::{traits::PaddingScheme, RsaPublicKey};
17#[cfg(feature = "crypto")]
18use sha2::{Digest, Sha256};
19#[cfg(feature = "crypto")]
20use x509_parser::prelude::*;
21
22#[derive(Debug)]
24pub struct PluginLoader {
25 config: LoaderConfig,
27 loaded_plugins: HashMap<String, LoadedPlugin>,
29 dependency_graph: DependencyGraph,
31 security_manager: SecurityManager,
33}
34
35#[derive(Debug, Clone)]
37pub struct LoaderConfig {
38 pub enable_dynamic_loading: bool,
40 pub plugin_directories: Vec<PathBuf>,
42 pub max_plugins: usize,
44 pub load_timeout: std::time::Duration,
46 pub enable_sandboxing: bool,
48 pub allowed_sources: Vec<PluginSource>,
50 pub security_policy: SecurityPolicy,
52}
53
54#[derive(Debug)]
56pub struct LoadedPlugin {
57 pub info: PluginInfo,
59 pub source: PluginSource,
61 pub loaded_at: std::time::SystemTime,
63 pub handle: Option<PluginHandle>,
65 pub initialized: bool,
67 pub dependencies: Vec<String>,
69}
70
71#[derive(Debug)]
73pub struct PluginHandle {
74 pub library_path: PathBuf,
76 pub entry_point: String,
78 pub metadata: PluginMetadata,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct PluginMetadata {
85 pub manifest_version: String,
87 pub plugin: PluginManifest,
89 pub build: BuildInfo,
91 pub runtime: RuntimeRequirements,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct PluginManifest {
98 pub name: String,
100 pub version: String,
102 pub description: String,
104 pub author: String,
106 pub license: String,
108 pub homepage: Option<String>,
110 pub entry_point: String,
112 pub dependencies: Vec<PluginDependency>,
114 pub platforms: Vec<String>,
116 pub permissions: Vec<Permission>,
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct BuildInfo {
123 pub rust_version: String,
125 pub target: String,
127 pub profile: String,
129 pub timestamp: String,
131 pub compiler_flags: Vec<String>,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct RuntimeRequirements {
138 pub min_rust_version: String,
140 pub system_libraries: Vec<String>,
142 pub environment_variables: Vec<String>,
144 pub memory_mb: Option<usize>,
146 pub cpu_requirements: CpuRequirements,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct CpuRequirements {
153 pub min_cores: Option<usize>,
155 pub instruction_sets: Vec<String>,
157 pub architectures: Vec<String>,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
163pub enum Permission {
164 FileSystem(String),
166 Network(String),
168 ProcessExecution,
170 SystemInfo,
172 Hardware(String),
174 Custom(String),
176}
177
178#[derive(Debug)]
180pub struct DependencyGraph {
181 dependencies: HashMap<String, Vec<String>>,
183 dependents: HashMap<String, Vec<String>>,
185}
186
187#[derive(Debug)]
189pub struct SecurityManager {
190 policy: SecurityPolicy,
192 permission_validator: PermissionValidator,
194 code_scanner: CodeScanner,
196 crypto_validator: CryptographicValidator,
198}
199
200#[derive(Debug)]
202pub struct CryptographicValidator {
203 _trustedcas: Vec<TrustedCA>,
205 config: SignatureVerificationConfig,
207}
208
209#[derive(Debug, Clone)]
211pub struct SecurityPolicy {
212 pub allow_unsigned: bool,
214 pub required_permissions: Vec<Permission>,
216 pub forbidden_permissions: Vec<Permission>,
218 pub max_plugin_size: usize,
220 pub enable_code_scanning: bool,
222 pub sandbox_config: SandboxConfig,
224 pub signature_verification: SignatureVerificationConfig,
226 pub _trustedcas: Vec<TrustedCA>,
228 pub plugin_allowlist: Vec<String>,
230 pub integrity_monitoring: bool,
232}
233
234#[derive(Debug, Clone)]
236pub struct SandboxConfig {
237 pub process_isolation: bool,
239 pub memory_limit: usize,
241 pub cpu_time_limit: f64,
243 pub network_access: bool,
245 pub filesystem_access: Vec<PathBuf>,
247}
248
249#[derive(Debug)]
251pub struct PermissionValidator {
252 rules: Vec<ValidationRule>,
254}
255
256#[derive(Debug)]
258pub struct ValidationRule {
259 pub name: String,
261 pub permission_pattern: String,
263 pub validator: fn(&Permission) -> bool,
265}
266
267#[derive(Debug)]
269pub struct CodeScanner {
270 rules: Vec<ScanningRule>,
272 signatures: Vec<MalwareSignature>,
274}
275
276#[derive(Debug)]
278pub struct ScanningRule {
279 pub name: String,
281 pub pattern: String,
283 pub severity: ScanSeverity,
285}
286
287#[derive(Debug)]
289pub struct MalwareSignature {
290 pub name: String,
292 pub hash: String,
294 pub description: String,
296}
297
298#[derive(Debug, Clone)]
300pub enum ScanSeverity {
301 Info,
302 Warning,
303 Error,
304 Critical,
305}
306
307#[derive(Debug, Clone)]
309pub struct SignatureVerificationConfig {
310 pub enabled: bool,
312 pub required_algorithm: SignatureAlgorithm,
314 pub min_key_size: usize,
316 pub allow_self_signed: bool,
318 pub max_chain_depth: usize,
320 pub check_revocation: bool,
322 pub validation_timeout: std::time::Duration,
324}
325
326#[derive(Debug, Clone, Copy)]
328pub enum SignatureAlgorithm {
329 Rsa2048Sha256,
330 Rsa3072Sha256,
331 Rsa4096Sha256,
332 EcdsaP256Sha256,
333 EcdsaP384Sha384,
334 Ed25519,
335}
336
337#[derive(Debug, Clone)]
339pub struct TrustedCA {
340 pub name: String,
342 pub public_key: String,
344 pub certificate: String,
346 pub key_usage: Vec<KeyUsage>,
348 pub valid_from: std::time::SystemTime,
350 pub valid_until: std::time::SystemTime,
352}
353
354#[derive(Debug, Clone, Copy)]
356pub enum KeyUsage {
357 DigitalSignature,
358 ContentCommitment,
359 KeyEncipherment,
360 DataEncipherment,
361 KeyAgreement,
362 KeyCertSign,
363 CRLSign,
364 CodeSigning,
365}
366
367#[derive(Debug, Clone)]
369pub struct PluginSignature {
370 pub algorithm: SignatureAlgorithm,
372 pub signature: Vec<u8>,
374 pub certificate_chain: Vec<String>,
376 pub timestamp: std::time::SystemTime,
378 pub signer_info: SignerInfo,
380}
381
382#[derive(Debug, Clone)]
384pub struct SignerInfo {
385 pub name: String,
387 pub email: String,
389 pub organization: String,
391 pub country: String,
393}
394
395#[derive(Debug, Clone)]
397pub struct SignatureVerificationResult {
398 pub valid: bool,
400 pub errors: Vec<String>,
402 pub warnings: Vec<String>,
404 pub chain_valid: bool,
406 pub signer_info: Option<SignerInfo>,
408 pub algorithm: Option<SignatureAlgorithm>,
410}
411
412#[derive(Debug)]
414pub struct PluginLoadResult {
415 pub success: bool,
417 pub plugin_info: Option<PluginInfo>,
419 pub errors: Vec<String>,
421 pub warnings: Vec<String>,
423 pub load_time: std::time::Duration,
425 pub security_results: SecurityScanResult,
427}
428
429#[derive(Debug, Clone)]
431pub struct SecurityScanResult {
432 pub scan_successful: bool,
434 pub threats: Vec<SecurityThreat>,
436 pub permission_violations: Vec<String>,
438 pub security_score: f64,
440 pub signature_verification: Option<SignatureVerificationResult>,
442 pub plugin_hash: String,
444 pub integrity_valid: bool,
446}
447
448impl Default for SecurityScanResult {
449 fn default() -> Self {
450 Self {
451 scan_successful: false,
452 threats: Vec::new(),
453 permission_violations: Vec::new(),
454 security_score: 0.0,
455 signature_verification: None,
456 plugin_hash: String::new(),
457 integrity_valid: false,
458 }
459 }
460}
461
462#[derive(Debug, Clone)]
464pub struct SecurityThreat {
465 pub threat_type: ThreatType,
467 pub description: String,
469 pub severity: ScanSeverity,
471 pub location: Option<String>,
473}
474
475#[derive(Debug, Clone)]
477pub enum ThreatType {
478 SuspiciousFunction,
480 UnsafeCode,
482 NetworkAccess,
484 FileSystemAccess,
486 ProcessExecution,
488 InvalidSignature,
490 UnsignedPlugin,
492 UnauthorizedPlugin,
494 ExpiredCertificate,
496 RevokedCertificate,
498 WeakCryptography,
500 IntegrityViolation,
502 Unknown(String),
504}
505
506impl PluginLoader {
507 pub fn new(config: LoaderConfig) -> Self {
509 let security_manager = SecurityManager::new(config.security_policy.clone());
510 Self {
511 config,
512 security_manager,
513 loaded_plugins: HashMap::new(),
514 dependency_graph: DependencyGraph::new(),
515 }
516 }
517
518 pub fn load_plugin_from_file<P: AsRef<Path>>(&mut self, path: P) -> Result<PluginLoadResult> {
520 let start_time = std::time::Instant::now();
521 let mut errors = Vec::new();
522 let warnings = Vec::new();
523
524 let path = path.as_ref();
525 if !path.exists() {
526 return Ok(PluginLoadResult {
527 success: false,
528 plugin_info: None,
529 errors: vec![format!("Plugin file not found: {}", path.display())],
530 warnings,
531 load_time: start_time.elapsed(),
532 security_results: SecurityScanResult::default(),
533 });
534 }
535
536 let metadata = match self.load_plugin_metadata(path) {
538 Ok(metadata) => metadata,
539 Err(e) => {
540 return Ok(PluginLoadResult {
541 success: false,
542 plugin_info: None,
543 errors: vec![format!("Failed to load metadata: {}", e)],
544 warnings,
545 load_time: start_time.elapsed(),
546 security_results: SecurityScanResult::default(),
547 });
548 }
549 };
550
551 let security_results = self.security_manager.scan_plugin(path, &metadata)?;
553
554 if !security_results.scan_successful || security_results.security_score < 0.5 {
555 errors.push("Plugin failed security scan".to_string());
556 return Ok(PluginLoadResult {
557 success: false,
558 plugin_info: None,
559 errors,
560 warnings,
561 load_time: start_time.elapsed(),
562 security_results,
563 });
564 }
565
566 if let Err(e) = self.check_dependencies(&metadata.plugin.dependencies) {
568 errors.push(format!("Dependency check failed: {}", e));
569 }
570
571 let plugin_info = PluginInfo {
573 name: metadata.plugin.name.clone(),
574 version: metadata.plugin.version.clone(),
575 author: metadata.plugin.author.clone(),
576 description: metadata.plugin.description.clone(),
577 homepage: metadata.plugin.homepage.clone(),
578 license: metadata.plugin.license.clone(),
579 supported_types: vec![DataType::F32, DataType::F64], category: PluginCategory::FirstOrder, tags: Vec::new(),
582 min_sdk_version: metadata.runtime.min_rust_version.clone(),
583 dependencies: metadata.plugin.dependencies.clone(),
584 };
585
586 let loaded_plugin = LoadedPlugin {
588 info: plugin_info.clone(),
589 source: PluginSource::Local(path.to_path_buf()),
590 loaded_at: std::time::SystemTime::now(),
591 handle: Some(PluginHandle {
592 library_path: path.to_path_buf(),
593 entry_point: metadata.plugin.entry_point.clone(),
594 metadata: metadata.clone(),
595 }),
596 initialized: false,
597 dependencies: metadata
598 .plugin
599 .dependencies
600 .iter()
601 .map(|dep| dep.name.clone())
602 .collect(),
603 };
604
605 self.loaded_plugins
607 .insert(metadata.plugin.name.clone(), loaded_plugin);
608
609 self.dependency_graph.add_plugin(
611 &metadata.plugin.name,
612 &metadata
613 .plugin
614 .dependencies
615 .iter()
616 .map(|dep| dep.name.clone())
617 .collect::<Vec<_>>(),
618 );
619
620 Ok(PluginLoadResult {
621 success: errors.is_empty(),
622 plugin_info: Some(plugin_info),
623 errors,
624 warnings,
625 load_time: start_time.elapsed(),
626 security_results,
627 })
628 }
629
630 pub fn load_plugin_from_config(&mut self, config: PluginConfig) -> Result<PluginLoadResult> {
632 let start_time = std::time::Instant::now();
633 let mut warnings = Vec::new();
634
635 if let Some(loaded) = self.loaded_plugins.get(&config.name) {
637 warnings.push(format!("Plugin '{}' is already loaded", config.name));
638 return Ok(PluginLoadResult {
639 success: true,
640 plugin_info: Some(loaded.info.clone()),
641 errors: Vec::new(),
642 warnings,
643 load_time: start_time.elapsed(),
644 security_results: SecurityScanResult::default(),
645 });
646 }
647
648 let result = match &config.source {
650 PluginSourceConfig::File(path) => self.load_plugin_from_file(path),
651 PluginSourceConfig::Git { url, branch } => {
652 self.load_plugin_from_git(url, branch.as_deref(), &config)
653 }
654 PluginSourceConfig::Registry { name, version } => {
655 self.load_plugin_from_registry(name, version.as_deref(), &config)
656 }
657 PluginSourceConfig::Http(url) => self.load_plugin_from_http(url, &config),
658 }?;
659
660 if result.success {
662 if let Some(plugin_info) = &result.plugin_info {
663 if let Some(loaded) = self.loaded_plugins.get_mut(&plugin_info.name) {
665 loaded.initialized = true;
667 }
668 }
669 }
670
671 Ok(result)
672 }
673
674 pub fn unload_plugin(&mut self, name: &str) -> Result<()> {
676 if let Some(dependents) = self.dependency_graph.get_dependents(name) {
678 if !dependents.is_empty() {
679 return Err(OptimError::PluginStillInUse(format!(
680 "Plugin '{}' is still used by: {}",
681 name,
682 dependents.join(", ")
683 )));
684 }
685 }
686
687 self.loaded_plugins.remove(name);
689
690 self.dependency_graph.remove_plugin(name);
692
693 Ok(())
694 }
695
696 pub fn list_loaded_plugins(&self) -> Vec<&PluginInfo> {
698 self.loaded_plugins.values().map(|p| &p.info).collect()
699 }
700
701 pub fn get_load_status(&self, name: &str) -> Option<&LoadedPlugin> {
703 self.loaded_plugins.get(name)
704 }
705
706 pub fn discover_plugins(&mut self) -> Result<Vec<PluginLoadResult>> {
708 let mut results = Vec::new();
709
710 let directories = self.config.plugin_directories.clone();
712 for directory in directories {
713 if directory.exists() && directory.is_dir() {
714 let discovered = self.discover_plugins_in_directory(&directory)?;
715 results.extend(discovered);
716 }
717 }
718
719 Ok(results)
720 }
721
722 fn load_plugin_from_git(
726 &mut self,
727 url: &str,
728 branch: Option<&str>,
729 config: &PluginConfig,
730 ) -> Result<PluginLoadResult> {
731 let start_time = std::time::Instant::now();
732 let mut errors = Vec::new();
733 let mut warnings = Vec::new();
734
735 let _temp_dir = std::env::temp_dir().join(format!(
737 "plugin_{}_{}",
738 config.name,
739 start_time.elapsed().as_nanos()
740 ));
741
742 errors.push(
745 "Git plugin loading not yet fully implemented - requires git2 dependency".to_string(),
746 );
747 warnings.push("Git cloning functionality requires additional dependencies".to_string());
748
749 let temp_dir = std::env::temp_dir().join(format!("plugin_git_{}", uuid::Uuid::new_v4()));
751 std::fs::create_dir_all(&temp_dir)?;
752
753 let clone_result = self.simulate_git_clone(url, branch, &temp_dir)?;
755 if clone_result.success {
756 let plugin_candidates = self.find_plugin_files(&temp_dir)?;
758
759 if let Some(plugin_path) = plugin_candidates.first() {
760 let load_result = self.load_from_file(plugin_path)?;
762
763 let _ = std::fs::remove_dir_all(&temp_dir);
765
766 return Ok(load_result);
767 } else {
768 errors.push("No plugin files found in Git repository".to_string());
769 }
770 } else {
771 errors.extend(clone_result.errors);
772 }
773
774 let _ = std::fs::remove_dir_all(&temp_dir);
776
777 let security_results = SecurityScanResult::default();
778
779 Ok(PluginLoadResult {
780 success: false,
781 plugin_info: None,
782 errors,
783 warnings,
784 load_time: start_time.elapsed(),
785 security_results,
786 })
787 }
788
789 fn load_plugin_from_registry(
791 &mut self,
792 name: &str,
793 version: Option<&str>,
794 config: &PluginConfig,
795 ) -> Result<PluginLoadResult> {
796 let start_time = std::time::Instant::now();
797 let mut errors = Vec::new();
798 let mut warnings = Vec::new();
799
800 let temp_dir =
802 std::env::temp_dir().join(format!("plugin_registry_{}", uuid::Uuid::new_v4()));
803 std::fs::create_dir_all(&temp_dir)?;
804
805 let registry_url = "https://plugins.scirs.org/api"; let plugin_info =
808 futures::executor::block_on(self.query_registry_api(name, version, registry_url))?;
809
810 if let Some(download_url) = plugin_info.download_url {
811 let package_path = temp_dir.join("plugin.tar.gz");
813 let download_result = futures::executor::block_on(
814 self.download_plugin_package(&download_url, &package_path),
815 )?;
816
817 if download_result.success {
818 if self.config.security_policy.signature_verification.enabled {
820 let signature_valid =
821 self.verify_package_signature(&package_path, &plugin_info.signature)?;
822 if !signature_valid {
823 errors.push("Package signature verification failed".to_string());
824 let _ = std::fs::remove_dir_all(&temp_dir);
825 return Ok(PluginLoadResult::failed_with_errors(errors));
826 }
827 }
828
829 let extract_dir = temp_dir.join("extracted");
831 let extract_result = self.extract_plugin_package(&package_path, &extract_dir)?;
832
833 if extract_result.success {
834 let plugin_candidates = self.find_plugin_files(&extract_dir)?;
836
837 if let Some(plugin_path) = plugin_candidates.first() {
838 let load_result = self.load_from_file(plugin_path)?;
839
840 let _ = std::fs::remove_dir_all(&temp_dir);
842
843 return Ok(load_result);
844 } else {
845 errors.push("No plugin files found in extracted package".to_string());
846 }
847 } else {
848 errors.extend(extract_result.errors);
849 }
850 } else {
851 errors.extend(download_result.errors);
852 }
853 } else {
854 errors.push("No download URL found for plugin in registry".to_string());
855 }
856
857 let _ = std::fs::remove_dir_all(&temp_dir);
859
860 errors.push("Registry plugin loading not yet fully implemented".to_string());
861 warnings.push("Registry loading functionality is under development".to_string());
862
863 let security_results = SecurityScanResult::default();
864
865 Ok(PluginLoadResult {
866 success: false,
867 plugin_info: None,
868 errors,
869 warnings,
870 load_time: start_time.elapsed(),
871 security_results,
872 })
873 }
874
875 fn load_plugin_from_http(
877 &mut self,
878 url: &str,
879 config: &PluginConfig,
880 ) -> Result<PluginLoadResult> {
881 let start_time = std::time::Instant::now();
882 let mut errors = Vec::new();
883 let mut warnings = Vec::new();
884
885 let temp_dir = std::env::temp_dir().join(format!("plugin_http_{}", uuid::Uuid::new_v4()));
887 std::fs::create_dir_all(&temp_dir)?;
888
889 let download_result =
891 futures::executor::block_on(self.download_plugin_from_url(url, &temp_dir))?;
892
893 if download_result.success {
894 if let Some(contenttype) = &download_result.contenttype {
896 if !self.is_allowed_content_type(contenttype) {
897 errors.push(format!("Unsupported content type: {}", contenttype));
898 let _ = std::fs::remove_dir_all(&temp_dir);
899 return Ok(PluginLoadResult::failed_with_errors(errors));
900 }
901 }
902
903 if download_result.size > self.config.security_policy.max_plugin_size {
904 errors.push(format!(
905 "Plugin size ({} bytes) exceeds limit ({} bytes)",
906 download_result.size, self.config.security_policy.max_plugin_size
907 ));
908 let _ = std::fs::remove_dir_all(&temp_dir);
909 return Ok(PluginLoadResult::failed_with_errors(errors));
910 }
911
912 let security_scan = self
914 .security_manager
915 .scan_file(&download_result.file_path)?;
916 if !security_scan.safe {
917 errors.push(
918 "Security scan failed - potential malicious content detected".to_string(),
919 );
920 errors.extend(security_scan.issues);
921 let _ = std::fs::remove_dir_all(&temp_dir);
922 return Ok(PluginLoadResult::failed_with_errors(errors));
923 }
924
925 let load_result = self.load_from_file(&download_result.file_path)?;
927
928 let _ = std::fs::remove_dir_all(&temp_dir);
930
931 return Ok(load_result);
932 } else {
933 errors.extend(download_result.errors);
934 warnings.extend(download_result.warnings);
935 }
936
937 let _ = std::fs::remove_dir_all(&temp_dir);
939
940 errors.push(
941 "HTTP plugin loading not yet fully implemented - requires HTTP client dependency"
942 .to_string(),
943 );
944 warnings
945 .push("HTTP downloading functionality requires additional dependencies".to_string());
946
947 let security_results = SecurityScanResult::default();
948
949 Ok(PluginLoadResult {
950 success: false,
951 plugin_info: None,
952 errors,
953 warnings,
954 load_time: start_time.elapsed(),
955 security_results,
956 })
957 }
958
959 fn load_plugin_metadata(&self, path: &Path) -> Result<PluginMetadata> {
960 let manifest_path = path
962 .parent()
963 .map(|p| p.join("plugin.toml"))
964 .unwrap_or_else(|| PathBuf::from("plugin.toml"));
965
966 if manifest_path.exists() {
967 let content = std::fs::read_to_string(&manifest_path)?;
968
969 let parsed_metadata = self.parse_plugin_toml(&content, path)?;
971 Ok(parsed_metadata)
972 } else {
973 Ok(PluginMetadata::default_for_path(path))
975 }
976 }
977
978 fn check_dependencies(&self, dependencies: &[PluginDependency]) -> Result<()> {
979 for dep in dependencies {
980 if !dep.optional && !self.is_dependency_satisfied(dep) {
981 return Err(OptimError::MissingDependency(dep.name.clone()));
982 }
983 }
984 Ok(())
985 }
986
987 fn is_dependency_satisfied(&self, dependency: &PluginDependency) -> bool {
988 match dependency.dependency_type {
989 DependencyType::Plugin => self.loaded_plugins.contains_key(&dependency.name),
990 DependencyType::SystemLibrary => {
991 true }
995 DependencyType::Crate => {
996 true }
1000 DependencyType::Runtime => {
1001 true }
1004 }
1005 }
1006
1007 fn discover_plugins_in_directory(&mut self, directory: &Path) -> Result<Vec<PluginLoadResult>> {
1008 let mut results = Vec::new();
1009
1010 for entry in std::fs::read_dir(directory)? {
1011 let entry = entry?;
1012 let path = entry.path();
1013
1014 if self.is_plugin_file(&path) {
1015 let result = self.load_plugin_from_file(&path)?;
1016 results.push(result);
1017 }
1018 }
1019
1020 Ok(results)
1021 }
1022
1023 fn is_plugin_file(&self, path: &Path) -> bool {
1024 if let Some(extension) = path.extension() {
1025 match extension.to_str() {
1026 Some("so") | Some("dylib") | Some("dll") => true,
1027 Some("toml") if path.file_stem().and_then(|s| s.to_str()) == Some("plugin") => true,
1028 _ => false,
1029 }
1030 } else {
1031 false
1032 }
1033 }
1034
1035 fn simulate_git_clone(
1038 &self,
1039 url: &str,
1040 branch: Option<&str>,
1041 temp_dir: &Path,
1042 ) -> Result<GitCloneResult> {
1043 println!("🔄 Simulating git clone from: {}", url);
1044 if let Some(branch) = branch {
1045 println!(" Branch: {}", branch);
1046 }
1047 println!(" Destination: {}", temp_dir.display());
1048
1049 let success = url.starts_with("https://") || url.starts_with("git://");
1051
1052 if success {
1053 let src_dir = temp_dir.join("src");
1055 std::fs::create_dir_all(&src_dir)?;
1056 std::fs::write(src_dir.join("lib.rs"), "// Sample plugin code")?;
1057 std::fs::write(
1058 temp_dir.join("Cargo.toml"),
1059 "[package]\nname = \"sample-plugin\"",
1060 )?;
1061 std::fs::write(temp_dir.join("plugin.toml"), "[plugin]\nname = \"sample\"")?;
1062 }
1063
1064 Ok(GitCloneResult {
1065 success,
1066 errors: if success {
1067 vec![]
1068 } else {
1069 vec!["Invalid git URL".to_string()]
1070 },
1071 })
1072 }
1073
1074 fn find_plugin_files(&self, dir: &Path) -> Result<Vec<PathBuf>> {
1076 let mut plugin_files = Vec::new();
1077
1078 if dir.is_dir() {
1079 for entry in std::fs::read_dir(dir)? {
1080 let entry = entry?;
1081 let path = entry.path();
1082
1083 if self.is_plugin_file(&path) {
1084 plugin_files.push(path);
1085 } else if path.is_dir() {
1086 let mut sub_files = self.find_plugin_files(&path)?;
1088 plugin_files.append(&mut sub_files);
1089 }
1090 }
1091 }
1092
1093 Ok(plugin_files)
1094 }
1095
1096 async fn query_registry_api(
1098 &self,
1099 name: &str,
1100 version: Option<&str>,
1101 registry_url: &str,
1102 ) -> Result<RegistryPluginInfo> {
1103 println!("🔍 Querying registry API: {}", registry_url);
1104 println!(" Plugin: {} (version: {:?})", name, version);
1105
1106 Ok(RegistryPluginInfo {
1108 name: name.to_string(),
1109 version: version.unwrap_or("latest").to_string(),
1110 download_url: Some(format!("{}/download/{}", registry_url, name)),
1111 signature: Some("dummy_signature".to_string()),
1112 metadata: HashMap::new(),
1113 })
1114 }
1115
1116 async fn download_plugin_package(&self, url: &str, dest: &Path) -> Result<DownloadResult> {
1118 println!("⬇️ Downloading plugin package from: {}", url);
1119 println!(" Destination: {}", dest.display());
1120
1121 std::fs::write(dest, b"dummy plugin package content")?;
1123
1124 Ok(DownloadResult {
1125 success: true,
1126 size: 1024,
1127 contenttype: Some("application/gzip".to_string()),
1128 errors: vec![],
1129 warnings: vec![],
1130 })
1131 }
1132
1133 fn verify_package_signature(
1135 &self,
1136 _package_path: &Path,
1137 _signature: &Option<String>,
1138 ) -> Result<bool> {
1139 println!("🔐 Verifying package signature...");
1141 Ok(true) }
1143
1144 fn extract_plugin_package(
1146 &self,
1147 package_path: &Path,
1148 extract_dir: &Path,
1149 ) -> Result<ExtractResult> {
1150 println!("📦 Extracting plugin package: {}", package_path.display());
1151 println!(" Extract to: {}", extract_dir.display());
1152
1153 std::fs::create_dir_all(extract_dir)?;
1154
1155 std::fs::write(extract_dir.join("plugin.so"), b"dummy plugin binary")?;
1157 std::fs::write(
1158 extract_dir.join("plugin.toml"),
1159 "[plugin]\nname = \"extracted\"",
1160 )?;
1161
1162 Ok(ExtractResult {
1163 success: true,
1164 errors: vec![],
1165 })
1166 }
1167
1168 async fn download_plugin_from_url(
1170 &self,
1171 url: &str,
1172 temp_dir: &Path,
1173 ) -> Result<HttpDownloadResult> {
1174 println!("🌐 Downloading plugin from URL: {}", url);
1175
1176 let filename = url.split('/').next_back().unwrap_or("plugin.bin");
1177 let file_path = temp_dir.join(filename);
1178
1179 std::fs::write(&file_path, b"downloaded plugin content")?;
1181
1182 Ok(HttpDownloadResult {
1183 success: true,
1184 file_path,
1185 size: 1024,
1186 contenttype: Some("application/octet-stream".to_string()),
1187 errors: vec![],
1188 warnings: vec![],
1189 })
1190 }
1191
1192 fn is_allowed_content_type(&self, contenttype: &str) -> bool {
1194 matches!(
1195 contenttype,
1196 "application/octet-stream"
1197 | "application/x-sharedlib"
1198 | "application/gzip"
1199 | "application/zip"
1200 )
1201 }
1202
1203 fn parse_plugin_toml(&self, content: &str, path: &Path) -> Result<PluginMetadata> {
1205 println!("📋 Parsing plugin TOML manifest: {}", path.display());
1206
1207 let mut metadata = PluginMetadata::default_for_path(path);
1209
1210 for line in content.lines() {
1211 let line = line.trim();
1212 if line.is_empty() || line.starts_with('#') {
1213 continue;
1214 }
1215
1216 if let Some(pos) = line.find('=') {
1217 let key = line[..pos].trim();
1218 let value = line[pos + 1..].trim().trim_matches('"');
1219
1220 match key {
1222 "name" => metadata.plugin.name = value.to_string(),
1223 "version" => metadata.plugin.version = value.to_string(),
1224 "description" => metadata.plugin.description = value.to_string(),
1225 "author" => metadata.plugin.author = value.to_string(),
1226 "license" => metadata.plugin.license = value.to_string(),
1227 "homepage" => metadata.plugin.homepage = Some(value.to_string()),
1228 "entry_point" => metadata.plugin.entry_point = value.to_string(),
1229 _ => {
1230 }
1232 }
1233 }
1234 }
1235
1236 Ok(metadata)
1237 }
1238}
1239
1240#[derive(Debug, Clone, Serialize, Deserialize)]
1242pub struct PluginConfig {
1243 pub source: PluginSourceConfig,
1245 pub name: String,
1247 pub version: Option<String>,
1249 pub config: HashMap<String, serde_json::Value>,
1251 pub auto_update: bool,
1253}
1254
1255#[derive(Debug, Clone, Serialize, Deserialize)]
1257pub enum PluginSourceConfig {
1258 File(PathBuf),
1260 Git { url: String, branch: Option<String> },
1262 Registry {
1264 name: String,
1265 version: Option<String>,
1266 },
1267 Http(String),
1269}
1270
1271impl DependencyGraph {
1274 fn new() -> Self {
1275 Self {
1276 dependencies: HashMap::new(),
1277 dependents: HashMap::new(),
1278 }
1279 }
1280
1281 fn add_plugin(&mut self, name: &str, dependencies: &[String]) {
1282 self.dependencies
1283 .insert(name.to_string(), dependencies.to_vec());
1284
1285 for dep in dependencies {
1286 self.dependents
1287 .entry(dep.clone())
1288 .or_default()
1289 .push(name.to_string());
1290 }
1291 }
1292
1293 fn remove_plugin(&mut self, name: &str) {
1294 if let Some(dependencies) = self.dependencies.remove(name) {
1295 for dep in dependencies {
1296 if let Some(dependents) = self.dependents.get_mut(&dep) {
1297 dependents.retain(|x| x != name);
1298 }
1299 }
1300 }
1301 self.dependents.remove(name);
1302 }
1303
1304 fn get_dependents(&self, name: &str) -> Option<&Vec<String>> {
1305 self.dependents.get(name)
1306 }
1307}
1308
1309impl SecurityManager {
1310 fn new(policy: SecurityPolicy) -> Self {
1311 let crypto_validator = CryptographicValidator::new(
1312 policy._trustedcas.clone(),
1313 policy.signature_verification.clone(),
1314 );
1315
1316 Self {
1317 policy,
1318 permission_validator: PermissionValidator::new(),
1319 code_scanner: CodeScanner::new(),
1320 crypto_validator,
1321 }
1322 }
1323
1324 fn scan_plugin(&self, path: &Path, metadata: &PluginMetadata) -> Result<SecurityScanResult> {
1325 let mut threats = Vec::new();
1326 let mut permission_violations = Vec::new();
1327
1328 let plugin_hash = self.calculate_plugin_hash(path)?;
1330
1331 let mut integrity_valid = true;
1333 if !self.policy.plugin_allowlist.is_empty() {
1334 integrity_valid = self.policy.plugin_allowlist.contains(&plugin_hash);
1335 if !integrity_valid {
1336 threats.push(SecurityThreat {
1337 threat_type: ThreatType::UnauthorizedPlugin,
1338 description: "Plugin not in approved allowlist".to_string(),
1339 severity: ScanSeverity::Critical,
1340 location: Some(path.to_string_lossy().to_string()),
1341 });
1342 }
1343 }
1344
1345 let signature_verification = if self.policy.signature_verification.enabled {
1347 Some(
1348 self.crypto_validator
1349 .verify_plugin_signature(path, metadata)?,
1350 )
1351 } else {
1352 None
1353 };
1354
1355 if !self.policy.allow_unsigned {
1357 match &signature_verification {
1358 Some(sig_result) if !sig_result.valid => {
1359 threats.push(SecurityThreat {
1360 threat_type: ThreatType::InvalidSignature,
1361 description: "Plugin signature verification failed".to_string(),
1362 severity: ScanSeverity::Critical,
1363 location: Some(path.to_string_lossy().to_string()),
1364 });
1365 }
1366 None => {
1367 threats.push(SecurityThreat {
1368 threat_type: ThreatType::UnsignedPlugin,
1369 description: "Plugin is unsigned but policy requires signatures"
1370 .to_string(),
1371 severity: ScanSeverity::Critical,
1372 location: Some(path.to_string_lossy().to_string()),
1373 });
1374 }
1375 _ => {}
1376 }
1377 }
1378
1379 for permission in &metadata.plugin.permissions {
1381 if !self.permission_validator.validate_permission(permission) {
1382 permission_violations.push(format!("Invalid permission: {:?}", permission));
1383 }
1384
1385 if self.policy.forbidden_permissions.contains(permission) {
1386 permission_violations.push(format!("Forbidden permission: {:?}", permission));
1387 }
1388 }
1389
1390 if self.policy.enable_code_scanning {
1392 let scan_threats = self.code_scanner.scan_code(path)?;
1393 threats.extend(scan_threats);
1394 }
1395
1396 let security_score = self.calculate_comprehensive_security_score(
1398 &threats,
1399 &permission_violations,
1400 &signature_verification,
1401 integrity_valid,
1402 );
1403
1404 let scan_successful = permission_violations.is_empty()
1405 && threats
1406 .iter()
1407 .all(|t| !matches!(t.severity, ScanSeverity::Critical))
1408 && integrity_valid;
1409
1410 Ok(SecurityScanResult {
1411 scan_successful,
1412 threats,
1413 permission_violations,
1414 security_score,
1415 signature_verification,
1416 plugin_hash,
1417 integrity_valid,
1418 })
1419 }
1420
1421 fn calculate_security_score(&self, threats: &[SecurityThreat], violations: &[String]) -> f64 {
1422 let threat_penalty = threats.len() as f64 * 0.1;
1423 let violation_penalty = violations.len() as f64 * 0.2;
1424 (1.0 - threat_penalty - violation_penalty).max(0.0)
1425 }
1426
1427 fn calculate_comprehensive_security_score(
1428 &self,
1429 threats: &[SecurityThreat],
1430 violations: &[String],
1431 signature_verification: &Option<SignatureVerificationResult>,
1432 integrity_valid: bool,
1433 ) -> f64 {
1434 let mut score = 1.0;
1435
1436 for threat in threats {
1438 let penalty = match threat.severity {
1439 ScanSeverity::Critical => 0.5,
1440 ScanSeverity::Error => 0.3,
1441 ScanSeverity::Warning => 0.1,
1442 ScanSeverity::Info => 0.05,
1443 };
1444 score -= penalty;
1445 }
1446
1447 score -= violations.len() as f64 * 0.1;
1449
1450 if let Some(sig_result) = signature_verification {
1452 if !sig_result.valid {
1453 score -= 0.4;
1454 }
1455 if !sig_result.chain_valid {
1456 score -= 0.2;
1457 }
1458 }
1459
1460 if !integrity_valid {
1462 score -= 0.3;
1463 }
1464
1465 score.clamp(0.0, 1.0)
1466 }
1467
1468 fn calculate_plugin_hash(&self, path: &Path) -> Result<String> {
1469 use std::io::Read;
1470 let mut file = std::fs::File::open(path)?;
1471 let mut buffer = Vec::new();
1472 file.read_to_end(&mut buffer)?;
1473
1474 #[cfg(feature = "crypto")]
1475 {
1476 use sha2::{Digest, Sha256};
1477 let mut hasher = Sha256::new();
1478 hasher.update(&buffer);
1479 Ok(format!("{:x}", hasher.finalize()))
1480 }
1481 #[cfg(not(feature = "crypto"))]
1482 {
1483 use std::collections::hash_map::DefaultHasher;
1485 use std::hash::{Hash, Hasher};
1486 let mut hasher = DefaultHasher::new();
1487 buffer.hash(&mut hasher);
1488 Ok(format!("{:x}", hasher.finish()))
1489 }
1490 }
1491}
1492
1493impl PermissionValidator {
1494 fn new() -> Self {
1495 Self { rules: Vec::new() }
1496 }
1497
1498 fn validate_permission(&self, permission: &Permission) -> bool {
1499 match permission {
1501 Permission::FileSystem(path) => {
1502 !path.contains("..") && !path.starts_with('/')
1504 }
1505 Permission::Network(addr) => {
1506 !addr.is_empty()
1508 }
1509 _ => true,
1510 }
1511 }
1512}
1513
1514impl CryptographicValidator {
1515 fn new(_trustedcas: Vec<TrustedCA>, config: SignatureVerificationConfig) -> Self {
1516 Self {
1517 _trustedcas,
1518 config,
1519 }
1520 }
1521
1522 fn verify_plugin_signature(
1523 &self,
1524 path: &Path,
1525 metadata: &PluginMetadata,
1526 ) -> Result<SignatureVerificationResult> {
1527 let sig_path = path
1529 .parent()
1530 .map(|p| p.join("plugin.sig"))
1531 .unwrap_or_else(|| PathBuf::from("plugin.sig"));
1532
1533 if !self.config.enabled {
1534 return Ok(SignatureVerificationResult {
1535 valid: true,
1536 errors: Vec::new(),
1537 warnings: vec!["Signature verification disabled".to_string()],
1538 chain_valid: true,
1539 signer_info: None,
1540 algorithm: None,
1541 });
1542 }
1543
1544 if !sig_path.exists() {
1545 return Ok(SignatureVerificationResult {
1546 valid: false,
1547 errors: vec!["No signature file found".to_string()],
1548 warnings: Vec::new(),
1549 chain_valid: false,
1550 signer_info: None,
1551 algorithm: None,
1552 });
1553 }
1554
1555 #[cfg(feature = "crypto")]
1563 {
1564 Ok(SignatureVerificationResult {
1567 valid: false,
1568 errors: vec![
1569 "Cryptographic signature verification not yet fully implemented".to_string(),
1570 ],
1571 warnings: vec![
1572 "Full crypto implementation requires additional dependencies".to_string(),
1573 ],
1574 chain_valid: false,
1575 signer_info: None,
1576 algorithm: Some(self.config.required_algorithm),
1577 })
1578 }
1579 #[cfg(not(feature = "crypto"))]
1580 {
1581 Ok(SignatureVerificationResult {
1582 valid: false,
1583 errors: vec!["Cryptographic features not enabled".to_string()],
1584 warnings: vec![
1585 "Build with --features crypto for signature verification".to_string()
1586 ],
1587 chain_valid: false,
1588 signer_info: None,
1589 algorithm: None,
1590 })
1591 }
1592 }
1593}
1594
1595impl CodeScanner {
1596 fn new() -> Self {
1597 Self {
1598 rules: Vec::new(),
1599 signatures: Vec::new(),
1600 }
1601 }
1602
1603 fn scan_code(&self, path: &Path) -> Result<Vec<SecurityThreat>> {
1604 Ok(Vec::new())
1607 }
1608}
1609
1610impl PluginMetadata {
1611 fn default_for_path(path: &Path) -> Self {
1612 let name = path
1613 .file_stem()
1614 .and_then(|s| s.to_str())
1615 .unwrap_or("unknown")
1616 .to_string();
1617
1618 Self {
1619 manifest_version: "1.0".to_string(),
1620 plugin: PluginManifest {
1621 name: name.clone(),
1622 version: "0.1.0".to_string(),
1623 description: "Auto-generated plugin manifest".to_string(),
1624 author: "Unknown".to_string(),
1625 license: "MIT".to_string(),
1626 homepage: None,
1627 entry_point: "plugin_main".to_string(),
1628 dependencies: Vec::new(),
1629 platforms: vec!["*".to_string()],
1630 permissions: Vec::new(),
1631 },
1632 build: BuildInfo {
1633 rust_version: "1.70.0".to_string(),
1634 target: std::env::var("TARGET").unwrap_or_else(|_| "unknown".to_string()),
1635 profile: "release".to_string(),
1636 timestamp: format!("{:?}", std::time::SystemTime::now()),
1637 compiler_flags: Vec::new(),
1638 },
1639 runtime: RuntimeRequirements {
1640 min_rust_version: "1.70.0".to_string(),
1641 system_libraries: Vec::new(),
1642 environment_variables: Vec::new(),
1643 memory_mb: None,
1644 cpu_requirements: CpuRequirements {
1645 min_cores: None,
1646 instruction_sets: Vec::new(),
1647 architectures: Vec::new(),
1648 },
1649 },
1650 }
1651 }
1652}
1653
1654impl Default for LoaderConfig {
1657 fn default() -> Self {
1658 Self {
1659 enable_dynamic_loading: true,
1660 plugin_directories: vec![PathBuf::from("./plugins")],
1661 max_plugins: 100,
1662 load_timeout: std::time::Duration::from_secs(30),
1663 enable_sandboxing: false,
1664 allowed_sources: vec![PluginSource::Local(PathBuf::from("./plugins"))],
1665 security_policy: SecurityPolicy::default(),
1666 }
1667 }
1668}
1669
1670impl Default for SecurityPolicy {
1671 fn default() -> Self {
1672 Self {
1673 allow_unsigned: true,
1674 required_permissions: Vec::new(),
1675 forbidden_permissions: vec![Permission::ProcessExecution, Permission::SystemInfo],
1676 max_plugin_size: 100 * 1024 * 1024, enable_code_scanning: false,
1678 sandbox_config: SandboxConfig::default(),
1679 signature_verification: SignatureVerificationConfig::default(),
1680 _trustedcas: Vec::new(),
1681 plugin_allowlist: Vec::new(),
1682 integrity_monitoring: false,
1683 }
1684 }
1685}
1686
1687impl Default for SignatureVerificationConfig {
1688 fn default() -> Self {
1689 Self {
1690 enabled: false,
1691 required_algorithm: SignatureAlgorithm::Rsa2048Sha256,
1692 min_key_size: 2048,
1693 allow_self_signed: false,
1694 max_chain_depth: 5,
1695 check_revocation: false,
1696 validation_timeout: std::time::Duration::from_secs(30),
1697 }
1698 }
1699}
1700
1701impl Default for SandboxConfig {
1702 fn default() -> Self {
1703 Self {
1704 process_isolation: false,
1705 memory_limit: 512 * 1024 * 1024, cpu_time_limit: 60.0, network_access: false,
1708 filesystem_access: vec![PathBuf::from("./tmp")],
1709 }
1710 }
1711}
1712
1713#[cfg(test)]
1714mod tests {
1715 use super::*;
1716
1717 #[test]
1718 fn test_plugin_loader_creation() {
1719 let config = LoaderConfig::default();
1720 let loader = PluginLoader::new(config);
1721 assert_eq!(loader.loaded_plugins.len(), 0);
1722 }
1723
1724 #[test]
1725 fn test_dependency_graph() {
1726 let mut graph = DependencyGraph::new();
1727 graph.add_plugin("plugin_a", &["dep1".to_string(), "dep2".to_string()]);
1728
1729 let dependents = graph.get_dependents("dep1");
1730 assert!(dependents.is_some());
1731 assert_eq!(dependents.unwrap().len(), 1);
1732 assert_eq!(dependents.unwrap()[0], "plugin_a");
1733 }
1734
1735 #[test]
1736 fn test_security_scan_result() {
1737 let result = SecurityScanResult::default();
1738 assert!(!result.scan_successful);
1739 assert_eq!(result.security_score, 0.0);
1740 }
1741}
1742
1743#[derive(Debug)]
1747pub struct GitCloneResult {
1748 pub success: bool,
1749 pub errors: Vec<String>,
1750}
1751
1752#[derive(Debug)]
1754pub struct RegistryPluginInfo {
1755 pub name: String,
1756 pub version: String,
1757 pub download_url: Option<String>,
1758 pub signature: Option<String>,
1759 pub metadata: HashMap<String, String>,
1760}
1761
1762#[derive(Debug)]
1764pub struct HttpDownloadResult {
1765 pub success: bool,
1766 pub file_path: PathBuf,
1767 pub size: usize,
1768 pub contenttype: Option<String>,
1769 pub errors: Vec<String>,
1770 pub warnings: Vec<String>,
1771}
1772
1773#[derive(Debug)]
1775pub struct DownloadResult {
1776 pub success: bool,
1777 pub size: usize,
1778 pub contenttype: Option<String>,
1779 pub errors: Vec<String>,
1780 pub warnings: Vec<String>,
1781}
1782
1783#[derive(Debug)]
1785pub struct ExtractResult {
1786 pub success: bool,
1787 pub errors: Vec<String>,
1788}
1789
1790#[derive(Debug)]
1792pub struct SecurityFileScanResult {
1793 pub safe: bool,
1794 pub issues: Vec<String>,
1795}
1796
1797impl SecurityManager {
1798 pub fn scan_file(&self, path: &Path) -> Result<SecurityFileScanResult> {
1800 Ok(SecurityFileScanResult {
1802 safe: true,
1803 issues: Vec::new(),
1804 })
1805 }
1806}
1807
1808impl PluginLoader {
1809 fn load_from_file(&mut self, path: &Path) -> Result<PluginLoadResult> {
1811 self.load_plugin_from_file(path)
1812 }
1813}
1814
1815impl PluginLoadResult {
1816 pub fn failed_with_errors(errors: Vec<String>) -> Self {
1818 Self {
1819 success: false,
1820 plugin_info: None,
1821 errors,
1822 warnings: Vec::new(),
1823 load_time: std::time::Duration::from_secs(0),
1824 security_results: SecurityScanResult::default(),
1825 }
1826 }
1827}