1use crate::config::McpConfigProfile;
7use crate::monitoring::PerformanceMonitor;
8use crate::tools::dynamic_enablement::DynamicToolManager;
9use crate::CodePrismMcpServer;
10use anyhow::Result;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::path::{Path, PathBuf};
14use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
15use tracing::{debug, error, info, warn};
16
17#[derive(Debug)]
19pub struct SystemValidator {
20 config_profile: McpConfigProfile,
21 performance_monitor: Option<PerformanceMonitor>,
22 tool_manager: Option<DynamicToolManager>,
23 validation_cache: ValidationCache,
24}
25
26#[derive(Debug, Default)]
28struct ValidationCache {
29 path_validations: HashMap<PathBuf, PathValidationResult>,
30 last_system_check: Option<SystemTime>,
31 last_dependencies_check: Option<SystemTime>,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct ValidationResult {
37 pub status: ValidationStatus,
39 pub config_validation: ConfigValidationResult,
41 pub system_readiness: SystemReadinessResult,
43 pub security_validation: SecurityValidationResult,
45 pub performance_validation: PerformanceValidationResult,
47 pub dependency_validation: DependencyValidationResult,
49 pub timestamp: u64,
51 pub validation_duration_ms: u64,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
57pub enum ValidationStatus {
58 Valid,
60 ValidWithWarnings,
62 Invalid,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct ConfigValidationResult {
69 pub valid: bool,
70 pub errors: Vec<ConfigValidationError>,
71 pub warnings: Vec<String>,
72 pub profile_name: String,
73 pub validated_settings: ConfigValidationDetails,
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct ConfigValidationDetails {
79 pub memory_settings_valid: bool,
80 pub timeout_settings_valid: bool,
81 pub path_settings_valid: bool,
82 pub security_settings_valid: bool,
83 pub monitoring_settings_valid: bool,
84 pub caching_settings_valid: bool,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct ConfigValidationError {
90 pub field: String,
91 pub error_type: ConfigErrorType,
92 pub message: String,
93 pub suggested_fix: Option<String>,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
98pub enum ConfigErrorType {
99 InvalidValue,
100 MissingRequired,
101 IncompatibleSettings,
102 PathNotAccessible,
103 SecurityRisk,
104 PerformanceIssue,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct SystemReadinessResult {
110 pub ready: bool,
111 pub system_requirements: SystemRequirementsCheck,
112 pub runtime_environment: RuntimeEnvironmentCheck,
113 pub resource_availability: ResourceAvailabilityCheck,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct SystemRequirementsCheck {
119 pub minimum_memory_available: bool,
120 pub minimum_disk_space: bool,
121 pub required_permissions: bool,
122 pub system_architecture_supported: bool,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct RuntimeEnvironmentCheck {
128 pub rust_version_compatible: bool,
129 pub required_features_available: bool,
130 pub environment_variables_set: bool,
131 pub network_connectivity: bool,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct ResourceAvailabilityCheck {
137 pub available_memory_mb: usize,
138 pub available_disk_space_mb: usize,
139 pub cpu_cores_available: usize,
140 pub can_create_temp_files: bool,
141 pub can_bind_to_stdio: bool,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct SecurityValidationResult {
147 pub secure: bool,
148 pub vulnerabilities: Vec<SecurityVulnerability>,
149 pub recommendations: Vec<SecurityRecommendation>,
150 pub security_score: u32, }
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct SecurityVulnerability {
156 pub severity: SecuritySeverity,
157 pub category: SecurityCategory,
158 pub description: String,
159 pub mitigation: String,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct SecurityRecommendation {
165 pub priority: SecurityPriority,
166 pub description: String,
167 pub implementation: String,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
172pub enum SecuritySeverity {
173 Low,
174 Medium,
175 High,
176 Critical,
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
181pub enum SecurityCategory {
182 PathTraversal,
183 AccessControl,
184 DataExposure,
185 InputValidation,
186 Configuration,
187 NetworkSecurity,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
192pub enum SecurityPriority {
193 Low,
194 Medium,
195 High,
196 Critical,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct PerformanceValidationResult {
202 pub optimal: bool,
203 pub bottlenecks: Vec<PerformanceBottleneck>,
204 pub optimizations: Vec<PerformanceOptimization>,
205 pub performance_score: u32, }
207
208#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct PerformanceBottleneck {
211 pub component: String,
212 pub severity: BottleneckSeverity,
213 pub description: String,
214 pub impact: String,
215 pub solution: String,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct PerformanceOptimization {
221 pub component: String,
222 pub optimization_type: OptimizationType,
223 pub description: String,
224 pub expected_improvement: String,
225 pub implementation_effort: ImplementationEffort,
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize)]
230pub enum BottleneckSeverity {
231 Minor,
232 Moderate,
233 Significant,
234 Critical,
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
239pub enum OptimizationType {
240 Memory,
241 Cpu,
242 Disk,
243 Network,
244 Caching,
245 Concurrency,
246 Configuration,
247}
248
249#[derive(Debug, Clone, Serialize, Deserialize)]
251pub enum ImplementationEffort {
252 Low,
253 Medium,
254 High,
255}
256
257#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct DependencyValidationResult {
260 pub all_available: bool,
261 pub missing_dependencies: Vec<MissingDependency>,
262 pub version_conflicts: Vec<VersionConflict>,
263 pub optional_dependencies: Vec<OptionalDependency>,
264}
265
266#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct MissingDependency {
269 pub name: String,
270 pub dependency_type: DependencyType,
271 pub required_for: Vec<String>,
272 pub installation_hint: Option<String>,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct VersionConflict {
278 pub dependency: String,
279 pub required_version: String,
280 pub found_version: String,
281 pub impact: String,
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
286pub struct OptionalDependency {
287 pub name: String,
288 pub enables_features: Vec<String>,
289 pub installation_hint: String,
290}
291
292#[derive(Debug, Clone, Serialize, Deserialize)]
294pub enum DependencyType {
295 SystemLibrary,
296 RuntimeDependency,
297 ToolDependency,
298 LanguageParser,
299}
300
301#[derive(Debug, Clone)]
303struct PathValidationResult {
304 accessible: bool,
305 readable: bool,
306 writable: bool,
307 validated_at: SystemTime,
308}
309
310impl SystemValidator {
311 pub fn new(config_profile: McpConfigProfile) -> Self {
313 Self {
314 config_profile,
315 performance_monitor: None,
316 tool_manager: None,
317 validation_cache: ValidationCache::default(),
318 }
319 }
320
321 pub fn with_performance_monitor(mut self, monitor: PerformanceMonitor) -> Self {
323 self.performance_monitor = Some(monitor);
324 self
325 }
326
327 pub fn with_tool_manager(mut self, tool_manager: DynamicToolManager) -> Self {
329 self.tool_manager = Some(tool_manager);
330 self
331 }
332
333 pub async fn validate_system(&mut self) -> Result<ValidationResult> {
335 let start_time = Instant::now();
336
337 info!("Starting comprehensive system validation");
338
339 let config_validation = self.validate_configuration().await?;
341 let system_readiness = self.validate_system_readiness().await?;
342 let security_validation = self.validate_security().await?;
343 let performance_validation = self.validate_performance().await?;
344 let dependency_validation = self.validate_dependencies().await?;
345
346 let status = self.determine_overall_status(
348 &config_validation,
349 &system_readiness,
350 &security_validation,
351 &performance_validation,
352 &dependency_validation,
353 );
354
355 let validation_duration_ms = start_time.elapsed().as_millis() as u64;
356 let timestamp = SystemTime::now()
357 .duration_since(UNIX_EPOCH)
358 .unwrap_or_default()
359 .as_secs();
360
361 let result = ValidationResult {
362 status,
363 config_validation,
364 system_readiness,
365 security_validation,
366 performance_validation,
367 dependency_validation,
368 timestamp,
369 validation_duration_ms,
370 };
371
372 self.log_validation_summary(&result);
373
374 Ok(result)
375 }
376
377 async fn validate_configuration(&mut self) -> Result<ConfigValidationResult> {
379 debug!("Validating configuration settings");
380
381 let mut errors = Vec::new();
382 let mut warnings = Vec::new();
383 let profile_name = self.config_profile.name.clone();
384
385 let memory_valid = self.validate_memory_settings(&mut errors, &mut warnings);
387
388 let timeout_valid = self.validate_timeout_settings(&mut errors, &mut warnings);
390
391 let path_valid = self
393 .validate_path_settings(&mut errors, &mut warnings)
394 .await;
395
396 let security_valid = self.validate_security_settings(&mut errors, &mut warnings);
398
399 let monitoring_valid = self.validate_monitoring_settings(&mut errors, &mut warnings);
401
402 let caching_valid = self.validate_caching_settings(&mut errors, &mut warnings);
404
405 let valid = errors.is_empty();
406
407 Ok(ConfigValidationResult {
408 valid,
409 errors,
410 warnings,
411 profile_name,
412 validated_settings: ConfigValidationDetails {
413 memory_settings_valid: memory_valid,
414 timeout_settings_valid: timeout_valid,
415 path_settings_valid: path_valid,
416 security_settings_valid: security_valid,
417 monitoring_settings_valid: monitoring_valid,
418 caching_settings_valid: caching_valid,
419 },
420 })
421 }
422
423 fn validate_memory_settings(
425 &self,
426 errors: &mut Vec<ConfigValidationError>,
427 warnings: &mut Vec<String>,
428 ) -> bool {
429 let config = &self.config_profile.settings;
430 let mut valid = true;
431
432 if config.memory_limit_mb < 256 {
434 errors.push(ConfigValidationError {
435 field: "memory_limit_mb".to_string(),
436 error_type: ConfigErrorType::InvalidValue,
437 message: "Memory limit is too low, minimum 256MB required".to_string(),
438 suggested_fix: Some("Set memory_limit_mb to at least 256".to_string()),
439 });
440 valid = false;
441 }
442
443 if config.memory_limit_mb > 32768 {
445 warnings.push(
446 "Memory limit is very high (>32GB), ensure system has sufficient RAM".to_string(),
447 );
448 }
449
450 let estimated_memory_per_file = 2; let max_safe_batch = config.memory_limit_mb / estimated_memory_per_file;
453
454 if config.batch_size > max_safe_batch {
455 warnings.push(format!(
456 "Batch size ({}) may cause memory pressure, consider reducing to {}",
457 config.batch_size, max_safe_batch
458 ));
459 }
460
461 valid
462 }
463
464 fn validate_timeout_settings(
466 &self,
467 errors: &mut Vec<ConfigValidationError>,
468 warnings: &mut Vec<String>,
469 ) -> bool {
470 let config = &self.config_profile.settings;
471 let mut valid = true;
472
473 if config.default_timeout < Duration::from_secs(5) {
475 errors.push(ConfigValidationError {
476 field: "default_timeout".to_string(),
477 error_type: ConfigErrorType::InvalidValue,
478 message: "Default timeout is too low, minimum 5 seconds required".to_string(),
479 suggested_fix: Some("Increase default_timeout to at least 5 seconds".to_string()),
480 });
481 valid = false;
482 }
483
484 if config.default_timeout > Duration::from_secs(600) {
486 warnings.push(
487 "Default timeout is very high (>10 minutes), clients may disconnect".to_string(),
488 );
489 }
490
491 valid
492 }
493
494 async fn validate_path_settings(
496 &mut self,
497 errors: &mut Vec<ConfigValidationError>,
498 warnings: &mut Vec<String>,
499 ) -> bool {
500 let cache_enabled = self.config_profile.caching.enabled;
502 let cache_dir = self.config_profile.caching.cache_dir.clone();
503 let audit_log_path = self.config_profile.security.audit_log_path.clone();
504 let denied_paths = self.config_profile.security.denied_paths.clone();
505
506 let mut valid = true;
507
508 if cache_enabled {
510 if let Err(validation_error) = self.validate_path_access(&cache_dir, true, true).await {
511 errors.push(ConfigValidationError {
512 field: "caching.cache_dir".to_string(),
513 error_type: ConfigErrorType::PathNotAccessible,
514 message: format!("Cache directory not accessible: {}", validation_error),
515 suggested_fix: Some("Create directory or adjust permissions".to_string()),
516 });
517 valid = false;
518 }
519 }
520
521 if let Some(audit_path) = audit_log_path {
523 if let Some(parent) = audit_path.parent() {
524 if let Err(validation_error) = self.validate_path_access(parent, true, true).await {
525 errors.push(ConfigValidationError {
526 field: "security.audit_log_path".to_string(),
527 error_type: ConfigErrorType::PathNotAccessible,
528 message: format!(
529 "Audit log directory not accessible: {}",
530 validation_error
531 ),
532 suggested_fix: Some("Create directory or adjust permissions".to_string()),
533 });
534 valid = false;
535 }
536 }
537 }
538
539 for denied_path in &denied_paths {
541 if denied_path.starts_with("/") {
542 continue;
544 } else {
545 warnings.push(format!(
546 "Denied path '{}' is not absolute, may not provide expected security",
547 denied_path.display()
548 ));
549 }
550 }
551
552 valid
553 }
554
555 fn validate_security_settings(
557 &self,
558 errors: &mut Vec<ConfigValidationError>,
559 warnings: &mut Vec<String>,
560 ) -> bool {
561 let security_config = &self.config_profile.security;
562 let mut valid = true;
563
564 if !security_config.validate_paths {
566 errors.push(ConfigValidationError {
567 field: "security.validate_paths".to_string(),
568 error_type: ConfigErrorType::SecurityRisk,
569 message: "Path validation is disabled, security risk in production".to_string(),
570 suggested_fix: Some("Enable validate_paths for production deployment".to_string()),
571 });
572 valid = false;
573 }
574
575 if security_config.rate_limiting.enabled {
577 if security_config.rate_limiting.requests_per_minute > 1000 {
578 warnings
579 .push("Rate limit is very high, may not prevent abuse effectively".to_string());
580 }
581
582 if security_config.rate_limiting.max_concurrent > 50 {
583 warnings.push(
584 "Max concurrent requests is very high, may cause resource exhaustion"
585 .to_string(),
586 );
587 }
588 } else {
589 warnings
590 .push("Rate limiting is disabled, consider enabling for production".to_string());
591 }
592
593 if !security_config.enable_audit_log {
595 warnings
596 .push("Audit logging is disabled, enable for production compliance".to_string());
597 }
598
599 valid
600 }
601
602 fn validate_monitoring_settings(
604 &self,
605 _errors: &mut [ConfigValidationError],
606 warnings: &mut Vec<String>,
607 ) -> bool {
608 let monitoring_config = &self.config_profile.monitoring;
609
610 if !monitoring_config.enabled {
611 warnings.push(
612 "Performance monitoring is disabled, enable for production visibility".to_string(),
613 );
614 }
615
616 if monitoring_config.export_metrics && monitoring_config.metrics_export_path.is_none() {
617 warnings.push("Metrics export enabled but no export path configured".to_string());
618 }
619
620 true }
622
623 fn validate_caching_settings(
625 &self,
626 _errors: &mut [ConfigValidationError],
627 warnings: &mut Vec<String>,
628 ) -> bool {
629 let caching_config = &self.config_profile.caching;
630
631 if caching_config.enabled && caching_config.max_cache_size_mb > 10240 {
632 warnings
633 .push("Cache size is very large (>10GB), ensure sufficient disk space".to_string());
634 }
635
636 if caching_config.analysis_ttl > Duration::from_secs(86400) {
637 warnings.push("Analysis cache TTL is very long (>24h), may use stale data".to_string());
638 }
639
640 true
641 }
642
643 async fn validate_path_access(
645 &mut self,
646 path: &Path,
647 need_read: bool,
648 need_write: bool,
649 ) -> Result<()> {
650 if let Some(cached) = self.validation_cache.path_validations.get(path) {
652 if cached.validated_at.elapsed().unwrap_or_default() < Duration::from_secs(300) {
653 if !cached.accessible
654 || (need_read && !cached.readable)
655 || (need_write && !cached.writable)
656 {
657 return Err(anyhow::anyhow!("Path validation failed (cached)"));
658 }
659 return Ok(());
660 }
661 }
662
663 let accessible = path.exists() || path.parent().is_some_and(|p| p.exists());
665 let readable = accessible && std::fs::metadata(path).is_ok();
666 let writable = if accessible {
667 let test_file = path.join(".test_write_access");
669 std::fs::write(&test_file, "test").is_ok() && std::fs::remove_file(&test_file).is_ok()
670 } else {
671 false
672 };
673
674 self.validation_cache.path_validations.insert(
676 path.to_path_buf(),
677 PathValidationResult {
678 accessible,
679 readable,
680 writable,
681 validated_at: SystemTime::now(),
682 },
683 );
684
685 if !accessible || (need_read && !readable) || (need_write && !writable) {
687 return Err(anyhow::anyhow!(
688 "Path requirements not met: accessible={}, readable={}, writable={}",
689 accessible,
690 readable,
691 writable
692 ));
693 }
694
695 Ok(())
696 }
697
698 async fn validate_system_readiness(&mut self) -> Result<SystemReadinessResult> {
700 debug!("Validating system readiness");
701
702 let system_requirements = self.check_system_requirements().await;
703 let runtime_environment = self.check_runtime_environment().await;
704 let resource_availability = self.check_resource_availability().await;
705
706 let ready = system_requirements.minimum_memory_available
707 && system_requirements.minimum_disk_space
708 && runtime_environment.rust_version_compatible
709 && resource_availability.can_bind_to_stdio;
710
711 Ok(SystemReadinessResult {
712 ready,
713 system_requirements,
714 runtime_environment,
715 resource_availability,
716 })
717 }
718
719 async fn check_system_requirements(&self) -> SystemRequirementsCheck {
721 SystemRequirementsCheck {
723 minimum_memory_available: true, minimum_disk_space: true, required_permissions: true, system_architecture_supported: true, }
728 }
729
730 async fn check_runtime_environment(&mut self) -> RuntimeEnvironmentCheck {
732 if let Some(last_check) = self.validation_cache.last_system_check {
734 if last_check.elapsed().unwrap_or_default() < Duration::from_secs(300) {
735 return RuntimeEnvironmentCheck {
737 rust_version_compatible: true,
738 required_features_available: true,
739 environment_variables_set: true,
740 network_connectivity: true, };
742 }
743 }
744
745 self.validation_cache.last_system_check = Some(SystemTime::now());
746
747 RuntimeEnvironmentCheck {
748 rust_version_compatible: true, required_features_available: true, environment_variables_set: std::env::var("RUST_LOG").is_ok(), network_connectivity: true, }
753 }
754
755 async fn check_resource_availability(&self) -> ResourceAvailabilityCheck {
757 ResourceAvailabilityCheck {
758 available_memory_mb: 4096, available_disk_space_mb: 10240, cpu_cores_available: num_cpus::get(),
761 can_create_temp_files: std::env::temp_dir().exists(),
762 can_bind_to_stdio: true, }
764 }
765
766 async fn validate_security(&self) -> Result<SecurityValidationResult> {
768 debug!("Validating security configuration");
769
770 let mut vulnerabilities = Vec::new();
771 let mut recommendations = Vec::new();
772 let security_config = &self.config_profile.security;
773
774 if !security_config.validate_paths {
776 vulnerabilities.push(SecurityVulnerability {
777 severity: SecuritySeverity::High,
778 category: SecurityCategory::PathTraversal,
779 description:
780 "Path validation is disabled, allowing potential path traversal attacks"
781 .to_string(),
782 mitigation: "Enable validate_paths in security configuration".to_string(),
783 });
784 }
785
786 if security_config.allowed_paths.is_empty() && security_config.denied_paths.is_empty() {
788 recommendations.push(SecurityRecommendation {
789 priority: SecurityPriority::Medium,
790 description: "Configure allowed or denied paths for access control".to_string(),
791 implementation: "Add paths to allowed_paths or denied_paths in security config"
792 .to_string(),
793 });
794 }
795
796 if !security_config.enable_audit_log {
798 recommendations.push(SecurityRecommendation {
799 priority: SecurityPriority::Medium,
800 description: "Enable audit logging for security compliance".to_string(),
801 implementation: "Set enable_audit_log to true and configure audit_log_path"
802 .to_string(),
803 });
804 }
805
806 let security_score = self.calculate_security_score(&vulnerabilities, &recommendations);
808
809 Ok(SecurityValidationResult {
810 secure: vulnerabilities.iter().all(|v| {
811 !matches!(
812 v.severity,
813 SecuritySeverity::Critical | SecuritySeverity::High
814 )
815 }),
816 vulnerabilities,
817 recommendations,
818 security_score,
819 })
820 }
821
822 fn calculate_security_score(
824 &self,
825 vulnerabilities: &[SecurityVulnerability],
826 recommendations: &[SecurityRecommendation],
827 ) -> u32 {
828 let mut score = 100u32;
829
830 for vuln in vulnerabilities {
832 let deduction = match vuln.severity {
833 SecuritySeverity::Critical => 30,
834 SecuritySeverity::High => 20,
835 SecuritySeverity::Medium => 10,
836 SecuritySeverity::Low => 5,
837 };
838 score = score.saturating_sub(deduction);
839 }
840
841 for rec in recommendations {
843 let deduction = match rec.priority {
844 SecurityPriority::Critical => 15,
845 SecurityPriority::High => 10,
846 SecurityPriority::Medium => 5,
847 SecurityPriority::Low => 2,
848 };
849 score = score.saturating_sub(deduction);
850 }
851
852 score
853 }
854
855 async fn validate_performance(&self) -> Result<PerformanceValidationResult> {
857 debug!("Validating performance configuration");
858
859 let mut bottlenecks = Vec::new();
860 let mut optimizations = Vec::new();
861 let config = &self.config_profile.settings;
862
863 if config.memory_limit_mb < 1024 {
865 bottlenecks.push(PerformanceBottleneck {
866 component: "memory".to_string(),
867 severity: BottleneckSeverity::Significant,
868 description: "Memory limit is low, may cause frequent garbage collection"
869 .to_string(),
870 impact: "Increased latency and reduced throughput".to_string(),
871 solution: "Increase memory_limit_mb to at least 1024MB".to_string(),
872 });
873 }
874
875 if config.batch_size < 10 {
877 optimizations.push(PerformanceOptimization {
878 component: "indexing".to_string(),
879 optimization_type: OptimizationType::Concurrency,
880 description: "Small batch size may underutilize system resources".to_string(),
881 expected_improvement: "20-40% faster indexing".to_string(),
882 implementation_effort: ImplementationEffort::Low,
883 });
884 }
885
886 if !self.config_profile.caching.enabled {
888 optimizations.push(PerformanceOptimization {
889 component: "caching".to_string(),
890 optimization_type: OptimizationType::Caching,
891 description: "Enable caching to improve response times for repeated queries"
892 .to_string(),
893 expected_improvement: "50-80% faster repeated operations".to_string(),
894 implementation_effort: ImplementationEffort::Low,
895 });
896 }
897
898 let performance_score = self.calculate_performance_score(&bottlenecks, &optimizations);
899
900 Ok(PerformanceValidationResult {
901 optimal: bottlenecks.is_empty(),
902 bottlenecks,
903 optimizations,
904 performance_score,
905 })
906 }
907
908 fn calculate_performance_score(
910 &self,
911 bottlenecks: &[PerformanceBottleneck],
912 optimizations: &[PerformanceOptimization],
913 ) -> u32 {
914 let mut score = 100u32;
915
916 for bottleneck in bottlenecks {
918 let deduction = match bottleneck.severity {
919 BottleneckSeverity::Critical => 25,
920 BottleneckSeverity::Significant => 15,
921 BottleneckSeverity::Moderate => 10,
922 BottleneckSeverity::Minor => 5,
923 };
924 score = score.saturating_sub(deduction);
925 }
926
927 for optimization in optimizations {
929 let deduction = match optimization.implementation_effort {
930 ImplementationEffort::Low => 3,
931 ImplementationEffort::Medium => 2,
932 ImplementationEffort::High => 1,
933 };
934 score = score.saturating_sub(deduction);
935 }
936
937 score
938 }
939
940 async fn validate_dependencies(&mut self) -> Result<DependencyValidationResult> {
942 debug!("Validating dependencies");
943
944 if let Some(last_check) = self.validation_cache.last_dependencies_check {
946 if last_check.elapsed().unwrap_or_default() < Duration::from_secs(600) {
947 return Ok(DependencyValidationResult {
949 all_available: true,
950 missing_dependencies: Vec::new(),
951 version_conflicts: Vec::new(),
952 optional_dependencies: Vec::new(),
953 });
954 }
955 }
956
957 self.validation_cache.last_dependencies_check = Some(SystemTime::now());
958
959 let mut missing_dependencies = Vec::new();
960 let version_conflicts = Vec::new(); let optional_dependencies = Vec::new(); if !self.check_git_available() {
965 missing_dependencies.push(MissingDependency {
966 name: "git".to_string(),
967 dependency_type: DependencyType::ToolDependency,
968 required_for: vec!["repository analysis".to_string()],
969 installation_hint: Some("Install git from https://git-scm.com/".to_string()),
970 });
971 }
972
973 Ok(DependencyValidationResult {
974 all_available: missing_dependencies.is_empty() && version_conflicts.is_empty(),
975 missing_dependencies,
976 version_conflicts,
977 optional_dependencies,
978 })
979 }
980
981 fn check_git_available(&self) -> bool {
983 std::process::Command::new("git")
984 .arg("--version")
985 .output()
986 .is_ok()
987 }
988
989 fn determine_overall_status(
991 &self,
992 config: &ConfigValidationResult,
993 system: &SystemReadinessResult,
994 security: &SecurityValidationResult,
995 performance: &PerformanceValidationResult,
996 dependencies: &DependencyValidationResult,
997 ) -> ValidationStatus {
998 if !config.valid || !system.ready || !security.secure || !dependencies.all_available {
1000 return ValidationStatus::Invalid;
1001 }
1002
1003 let has_warnings = !config.warnings.is_empty()
1005 || !security.vulnerabilities.is_empty()
1006 || !performance.bottlenecks.is_empty();
1007
1008 if has_warnings {
1009 ValidationStatus::ValidWithWarnings
1010 } else {
1011 ValidationStatus::Valid
1012 }
1013 }
1014
1015 fn log_validation_summary(&self, result: &ValidationResult) {
1017 match result.status {
1018 ValidationStatus::Valid => {
1019 info!(
1020 "✅ System validation completed successfully in {}ms",
1021 result.validation_duration_ms
1022 );
1023 }
1024 ValidationStatus::ValidWithWarnings => {
1025 warn!(
1026 "⚠️ System validation completed with warnings in {}ms",
1027 result.validation_duration_ms
1028 );
1029
1030 for warning in &result.config_validation.warnings {
1031 warn!("Config warning: {}", warning);
1032 }
1033
1034 for vuln in &result.security_validation.vulnerabilities {
1035 warn!("Security issue: {:?} - {}", vuln.category, vuln.description);
1036 }
1037 }
1038 ValidationStatus::Invalid => {
1039 error!(
1040 "❌ System validation failed in {}ms",
1041 result.validation_duration_ms
1042 );
1043
1044 for error in &result.config_validation.errors {
1045 error!("Config error: {} - {}", error.field, error.message);
1046 }
1047
1048 if !result.system_readiness.ready {
1049 error!("System readiness check failed");
1050 }
1051 }
1052 }
1053 }
1054
1055 pub async fn generate_startup_report(
1057 &mut self,
1058 _server: &CodePrismMcpServer,
1059 ) -> Result<StartupReport> {
1060 info!("Generating comprehensive startup report");
1061
1062 let validation_result = self.validate_system().await?;
1063
1064 let tool_status = self
1065 .tool_manager
1066 .as_ref()
1067 .map(|tool_manager| tool_manager.get_summary());
1068
1069 let system_info = self.collect_system_info().await;
1070
1071 Ok(StartupReport {
1072 validation_result,
1073 tool_status,
1074 system_info,
1075 server_version: env!("CARGO_PKG_VERSION").to_string(),
1076 startup_timestamp: SystemTime::now()
1077 .duration_since(UNIX_EPOCH)
1078 .unwrap_or_default()
1079 .as_secs(),
1080 })
1081 }
1082
1083 async fn collect_system_info(&self) -> SystemInfo {
1085 SystemInfo {
1086 os: std::env::consts::OS.to_string(),
1087 architecture: std::env::consts::ARCH.to_string(),
1088 cpu_cores: num_cpus::get(),
1089 rust_version: std::env::var("RUSTC_VERSION").unwrap_or_else(|_| "unknown".to_string()),
1090 build_timestamp: std::env::var("BUILD_TIMESTAMP")
1091 .unwrap_or_else(|_| "unknown".to_string()),
1092 features_enabled: self.get_enabled_features(),
1093 }
1094 }
1095
1096 fn get_enabled_features(&self) -> Vec<String> {
1098 let mut features = Vec::new();
1099
1100 if self.config_profile.monitoring.enabled {
1101 features.push("monitoring".to_string());
1102 }
1103
1104 if self.config_profile.caching.enabled {
1105 features.push("caching".to_string());
1106 }
1107
1108 if self.config_profile.security.enable_audit_log {
1109 features.push("audit_logging".to_string());
1110 }
1111
1112 if self.config_profile.security.rate_limiting.enabled {
1113 features.push("rate_limiting".to_string());
1114 }
1115
1116 features
1117 }
1118}
1119
1120#[derive(Debug, Clone, Serialize, Deserialize)]
1122pub struct StartupReport {
1123 pub validation_result: ValidationResult,
1124 pub tool_status: Option<crate::tools::dynamic_enablement::ToolEnablementSummary>,
1125 pub system_info: SystemInfo,
1126 pub server_version: String,
1127 pub startup_timestamp: u64,
1128}
1129
1130#[derive(Debug, Clone, Serialize, Deserialize)]
1132pub struct SystemInfo {
1133 pub os: String,
1134 pub architecture: String,
1135 pub cpu_cores: usize,
1136 pub rust_version: String,
1137 pub build_timestamp: String,
1138 pub features_enabled: Vec<String>,
1139}
1140
1141#[cfg(test)]
1142mod tests {
1143 use super::*;
1144 use crate::config::{
1145 CachingConfig, McpConfig, MonitoringConfig, SecurityConfig, ToolConfiguration,
1146 };
1147
1148 fn create_test_profile() -> McpConfigProfile {
1149 McpConfigProfile {
1150 name: "test".to_string(),
1151 description: "Test profile".to_string(),
1152 settings: McpConfig::default(),
1153 tool_config: ToolConfiguration {
1154 enabled_categories: vec![],
1155 disabled_tools: vec![],
1156 tool_configs: HashMap::new(),
1157 enablement_rules: vec![],
1158 },
1159 monitoring: MonitoringConfig::default(),
1160 security: SecurityConfig::default(),
1161 caching: CachingConfig::default(),
1162 }
1163 }
1164
1165 #[tokio::test]
1166 async fn test_system_validator_creation() {
1167 let profile = create_test_profile();
1168 let validator = SystemValidator::new(profile);
1169
1170 assert_eq!(validator.config_profile.name, "test");
1171 }
1172
1173 #[tokio::test]
1174 async fn test_configuration_validation() {
1175 let mut profile = create_test_profile();
1176 profile.settings.memory_limit_mb = 128; let mut validator = SystemValidator::new(profile);
1179 let config_result = validator.validate_configuration().await.unwrap();
1180
1181 assert!(!config_result.valid);
1182 assert!(!config_result.errors.is_empty());
1183 }
1184
1185 #[tokio::test]
1186 async fn test_security_validation() {
1187 let mut profile = create_test_profile();
1188 profile.security.validate_paths = false; let validator = SystemValidator::new(profile);
1191 let security_result = validator.validate_security().await.unwrap();
1192
1193 assert!(!security_result.secure);
1194 assert!(!security_result.vulnerabilities.is_empty());
1195 }
1196
1197 #[tokio::test]
1198 async fn test_performance_validation() {
1199 let mut profile = create_test_profile();
1200 profile.settings.memory_limit_mb = 512; let validator = SystemValidator::new(profile);
1203 let perf_result = validator.validate_performance().await.unwrap();
1204
1205 assert!(!perf_result.optimal);
1206 assert!(!perf_result.bottlenecks.is_empty());
1207 }
1208}