1use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
17#[serde(rename_all = "snake_case")]
18pub enum Permission {
19 FileSystem,
20 Network,
21 ProcessCreation,
22 SystemCall,
23 EnvironmentAccess,
24 ThreadCreation,
25 MemoryAllocation,
26 TimerAccess,
27 SignalHandling,
28}
29
30impl Permission {
31 pub fn as_str(&self) -> &'static str {
32 match self {
33 Permission::FileSystem => "filesystem",
34 Permission::Network => "network",
35 Permission::ProcessCreation => "process_creation",
36 Permission::SystemCall => "system_call",
37 Permission::EnvironmentAccess => "environment_access",
38 Permission::ThreadCreation => "thread_creation",
39 Permission::MemoryAllocation => "memory_allocation",
40 Permission::TimerAccess => "timer_access",
41 Permission::SignalHandling => "signal_handling",
42 }
43 }
44
45 pub fn try_parse(s: &str) -> Option<Self> {
46 match s {
47 "filesystem" => Some(Permission::FileSystem),
48 "network" => Some(Permission::Network),
49 "process_creation" => Some(Permission::ProcessCreation),
50 "system_call" => Some(Permission::SystemCall),
51 "environment_access" => Some(Permission::EnvironmentAccess),
52 "thread_creation" => Some(Permission::ThreadCreation),
53 "memory_allocation" => Some(Permission::MemoryAllocation),
54 "timer_access" => Some(Permission::TimerAccess),
55 "signal_handling" => Some(Permission::SignalHandling),
56 _ => None,
57 }
58 }
59
60 pub fn description(&self) -> &'static str {
61 match self {
62 Permission::FileSystem => "Access to filesystem operations",
63 Permission::Network => "Network I/O and socket operations",
64 Permission::ProcessCreation => "Ability to spawn child processes",
65 Permission::SystemCall => "Low-level system call access",
66 Permission::EnvironmentAccess => "Access to environment variables",
67 Permission::ThreadCreation => "Ability to create threads",
68 Permission::MemoryAllocation => "Dynamic memory allocation",
69 Permission::TimerAccess => "Timer and clock access",
70 Permission::SignalHandling => "Signal handler registration",
71 }
72 }
73
74 pub fn severity(&self) -> u32 {
75 match self {
76 Permission::FileSystem => 7,
77 Permission::Network => 7,
78 Permission::ProcessCreation => 9,
79 Permission::SystemCall => 10,
80 Permission::EnvironmentAccess => 3,
81 Permission::ThreadCreation => 5,
82 Permission::MemoryAllocation => 4,
83 Permission::TimerAccess => 2,
84 Permission::SignalHandling => 8,
85 }
86 }
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct ResourceLimits {
92 pub max_memory_mb: u32,
93 pub max_cpu_time_ms: u32,
94 pub max_file_descriptors: u32,
95 pub max_threads: u32,
96 pub max_processes: u32,
97}
98
99impl Default for ResourceLimits {
100 fn default() -> Self {
101 Self {
102 max_memory_mb: 256,
103 max_cpu_time_ms: 30000,
104 max_file_descriptors: 1024,
105 max_threads: 16,
106 max_processes: 1,
107 }
108 }
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct PluginCapability {
114 pub name: String,
115 pub required_permissions: Vec<Permission>,
116 pub resource_requirement: Option<String>,
117 pub description: String,
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct SandboxCheckResult {
123 pub check_name: String,
124 pub passed: bool,
125 pub severity: SandboxSeverity,
126 pub message: String,
127 pub details: Option<String>,
128 pub remediation: Option<String>,
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
133#[serde(rename_all = "lowercase")]
134pub enum SandboxSeverity {
135 Info,
136 Warning,
137 Error,
138 Critical,
139}
140
141impl SandboxSeverity {
142 pub fn as_str(&self) -> &'static str {
143 match self {
144 SandboxSeverity::Info => "info",
145 SandboxSeverity::Warning => "warning",
146 SandboxSeverity::Error => "error",
147 SandboxSeverity::Critical => "critical",
148 }
149 }
150
151 pub fn try_parse(s: &str) -> Option<Self> {
152 match s.to_lowercase().as_str() {
153 "info" => Some(SandboxSeverity::Info),
154 "warning" => Some(SandboxSeverity::Warning),
155 "error" => Some(SandboxSeverity::Error),
156 "critical" => Some(SandboxSeverity::Critical),
157 _ => None,
158 }
159 }
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct SandboxVerificationReport {
165 pub plugin_id: String,
166 pub plugin_version: String,
167 pub verification_timestamp: String,
168 pub is_sandboxed: bool,
169 pub checks: Vec<SandboxCheckResult>,
170 pub requested_permissions: Vec<Permission>,
171 pub resource_limits: ResourceLimits,
172 pub capabilities: Vec<PluginCapability>,
173 pub high_risk_count: usize,
174 pub total_risk_score: u32,
175}
176
177impl SandboxVerificationReport {
178 pub fn is_compliant(&self) -> bool {
179 !self
180 .checks
181 .iter()
182 .any(|c| !c.passed && c.severity == SandboxSeverity::Critical)
183 }
184
185 pub fn risk_assessment(&self) -> SandboxRiskLevel {
186 match self.total_risk_score {
187 0..=10 => SandboxRiskLevel::Low,
188 11..=30 => SandboxRiskLevel::Medium,
189 31..=60 => SandboxRiskLevel::High,
190 _ => SandboxRiskLevel::Critical,
191 }
192 }
193
194 pub fn summary(&self) -> String {
195 let status = if self.is_compliant() {
196 "Compliant"
197 } else {
198 "Non-compliant"
199 };
200 format!(
201 "{}: Risk={:?}, Permissions={}, Score={}",
202 status,
203 self.risk_assessment(),
204 self.requested_permissions.len(),
205 self.total_risk_score
206 )
207 }
208}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
212#[serde(rename_all = "PascalCase")]
213pub enum SandboxRiskLevel {
214 Low,
215 Medium,
216 High,
217 Critical,
218}
219
220impl SandboxRiskLevel {
221 pub fn as_str(&self) -> &'static str {
222 match self {
223 SandboxRiskLevel::Low => "Low",
224 SandboxRiskLevel::Medium => "Medium",
225 SandboxRiskLevel::High => "High",
226 SandboxRiskLevel::Critical => "Critical",
227 }
228 }
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct SystemCallInfo {
234 pub name: String,
235 pub category: String,
236 pub whitelisted: bool,
237 pub risk_level: u32,
238}
239
240pub struct PluginSandboxVerifier {
242 whitelisted_syscalls: HashMap<String, SystemCallInfo>,
243 #[allow(dead_code)] required_permissions: HashMap<Permission, bool>,
245 resource_limits: ResourceLimits,
246}
247
248impl PluginSandboxVerifier {
249 pub fn new() -> Self {
251 Self {
252 whitelisted_syscalls: Self::default_syscall_whitelist(),
253 required_permissions: HashMap::new(),
254 resource_limits: ResourceLimits::default(),
255 }
256 }
257
258 fn default_syscall_whitelist() -> HashMap<String, SystemCallInfo> {
260 let mut whitelist = HashMap::new();
261
262 whitelist.insert(
264 "read".to_string(),
265 SystemCallInfo {
266 name: "read".to_string(),
267 category: "io".to_string(),
268 whitelisted: true,
269 risk_level: 1,
270 },
271 );
272
273 whitelist.insert(
274 "write".to_string(),
275 SystemCallInfo {
276 name: "write".to_string(),
277 category: "io".to_string(),
278 whitelisted: true,
279 risk_level: 1,
280 },
281 );
282
283 whitelist.insert(
285 "mmap".to_string(),
286 SystemCallInfo {
287 name: "mmap".to_string(),
288 category: "memory".to_string(),
289 whitelisted: true,
290 risk_level: 3,
291 },
292 );
293
294 whitelist.insert(
295 "brk".to_string(),
296 SystemCallInfo {
297 name: "brk".to_string(),
298 category: "memory".to_string(),
299 whitelisted: true,
300 risk_level: 2,
301 },
302 );
303
304 whitelist.insert(
306 "clone".to_string(),
307 SystemCallInfo {
308 name: "clone".to_string(),
309 category: "process".to_string(),
310 whitelisted: false,
311 risk_level: 9,
312 },
313 );
314
315 whitelist.insert(
317 "execve".to_string(),
318 SystemCallInfo {
319 name: "execve".to_string(),
320 category: "process".to_string(),
321 whitelisted: false,
322 risk_level: 10,
323 },
324 );
325
326 whitelist.insert(
327 "ptrace".to_string(),
328 SystemCallInfo {
329 name: "ptrace".to_string(),
330 category: "debug".to_string(),
331 whitelisted: false,
332 risk_level: 10,
333 },
334 );
335
336 whitelist
337 }
338
339 pub fn check_syscall_whitelist(&self, syscall_name: &str) -> SandboxCheckResult {
341 let is_whitelisted = self
342 .whitelisted_syscalls
343 .get(syscall_name)
344 .map(|s| s.whitelisted)
345 .unwrap_or(false);
346
347 SandboxCheckResult {
348 check_name: format!("Syscall: {}", syscall_name),
349 passed: is_whitelisted,
350 severity: if is_whitelisted {
351 SandboxSeverity::Info
352 } else {
353 SandboxSeverity::Error
354 },
355 message: if is_whitelisted {
356 format!("Syscall '{}' is whitelisted", syscall_name)
357 } else {
358 format!("Syscall '{}' is not allowed in sandbox", syscall_name)
359 },
360 details: self
361 .whitelisted_syscalls
362 .get(syscall_name)
363 .map(|s| format!("Category: {}, Risk: {}", s.category, s.risk_level)),
364 remediation: if !is_whitelisted {
365 Some("Remove use of this syscall or use sandboxed alternative".to_string())
366 } else {
367 None
368 },
369 }
370 }
371
372 pub fn check_permission_request(&self, permission: Permission) -> SandboxCheckResult {
374 SandboxCheckResult {
375 check_name: format!("Permission: {}", permission.as_str()),
376 passed: true,
377 severity: SandboxSeverity::Info,
378 message: format!(
379 "Permission '{}' requested: {}",
380 permission.as_str(),
381 permission.description()
382 ),
383 details: Some(format!("Risk severity: {}/10", permission.severity())),
384 remediation: None,
385 }
386 }
387
388 pub fn check_resource_limits(&self, requested: &ResourceLimits) -> SandboxCheckResult {
390 let memory_ok = requested.max_memory_mb <= self.resource_limits.max_memory_mb;
391 let cpu_ok = requested.max_cpu_time_ms <= self.resource_limits.max_cpu_time_ms;
392 let fds_ok = requested.max_file_descriptors <= self.resource_limits.max_file_descriptors;
393 let threads_ok = requested.max_threads <= self.resource_limits.max_threads;
394 let processes_ok = requested.max_processes <= self.resource_limits.max_processes;
395
396 let all_passed = memory_ok && cpu_ok && fds_ok && threads_ok && processes_ok;
397
398 let mut issues = Vec::new();
399 if !memory_ok {
400 issues.push(format!(
401 "Memory: {} MB > {} MB limit",
402 requested.max_memory_mb, self.resource_limits.max_memory_mb
403 ));
404 }
405 if !cpu_ok {
406 issues.push(format!(
407 "CPU: {} ms > {} ms limit",
408 requested.max_cpu_time_ms, self.resource_limits.max_cpu_time_ms
409 ));
410 }
411 if !fds_ok {
412 issues.push(format!(
413 "FDs: {} > {} limit",
414 requested.max_file_descriptors, self.resource_limits.max_file_descriptors
415 ));
416 }
417 if !threads_ok {
418 issues.push(format!(
419 "Threads: {} > {} limit",
420 requested.max_threads, self.resource_limits.max_threads
421 ));
422 }
423 if !processes_ok {
424 issues.push(format!(
425 "Processes: {} > {} limit",
426 requested.max_processes, self.resource_limits.max_processes
427 ));
428 }
429
430 SandboxCheckResult {
431 check_name: "Resource Limits".to_string(),
432 passed: all_passed,
433 severity: if all_passed {
434 SandboxSeverity::Info
435 } else {
436 SandboxSeverity::Warning
437 },
438 message: if all_passed {
439 "Resource limits within acceptable bounds".to_string()
440 } else {
441 format!("Resource limit violations: {}", issues.len())
442 },
443 details: if issues.is_empty() {
444 None
445 } else {
446 Some(issues.join("; "))
447 },
448 remediation: if !all_passed {
449 Some(
450 "Reduce requested resource limits to comply with sandbox constraints"
451 .to_string(),
452 )
453 } else {
454 None
455 },
456 }
457 }
458
459 pub fn check_capability_model(&self, capability: &PluginCapability) -> SandboxCheckResult {
461 let dangerous_perms = capability
462 .required_permissions
463 .iter()
464 .filter(|p| p.severity() >= 8)
465 .count();
466
467 SandboxCheckResult {
468 check_name: format!("Capability: {}", capability.name),
469 passed: dangerous_perms == 0,
470 severity: if dangerous_perms == 0 {
471 SandboxSeverity::Info
472 } else {
473 SandboxSeverity::Warning
474 },
475 message: format!(
476 "Capability '{}': {} permissions, {} high-risk",
477 capability.name,
478 capability.required_permissions.len(),
479 dangerous_perms
480 ),
481 details: Some(format!("Description: {}", capability.description)),
482 remediation: if dangerous_perms > 0 {
483 Some("Reduce high-risk permissions for this capability".to_string())
484 } else {
485 None
486 },
487 }
488 }
489
490 pub fn calculate_risk_score(permissions: &[Permission]) -> u32 {
492 permissions.iter().map(|p| p.severity()).sum()
493 }
494
495 pub fn analyze_risk(permissions: &[Permission]) -> (u32, usize, SandboxRiskLevel) {
497 let total_score = Self::calculate_risk_score(permissions);
498 let high_risk_count = permissions.iter().filter(|p| p.severity() >= 8).count();
499
500 let risk_level = match total_score {
501 0..=10 => SandboxRiskLevel::Low,
502 11..=30 => SandboxRiskLevel::Medium,
503 31..=60 => SandboxRiskLevel::High,
504 _ => SandboxRiskLevel::Critical,
505 };
506
507 (total_score, high_risk_count, risk_level)
508 }
509}
510
511impl Default for PluginSandboxVerifier {
512 fn default() -> Self {
513 Self::new()
514 }
515}
516
517#[cfg(test)]
518mod tests {
519 use super::*;
520
521 #[test]
522 fn test_permission_to_str() {
523 assert_eq!(Permission::FileSystem.as_str(), "filesystem");
524 assert_eq!(Permission::Network.as_str(), "network");
525 assert_eq!(Permission::ProcessCreation.as_str(), "process_creation");
526 assert_eq!(Permission::SystemCall.as_str(), "system_call");
527 }
528
529 #[test]
530 fn test_permission_try_parse() {
531 assert_eq!(
532 Permission::try_parse("filesystem"),
533 Some(Permission::FileSystem)
534 );
535 assert_eq!(Permission::try_parse("network"), Some(Permission::Network));
536 assert_eq!(Permission::try_parse("unknown"), None);
537 }
538
539 #[test]
540 fn test_permission_description() {
541 let desc = Permission::FileSystem.description();
542 assert!(!desc.is_empty());
543 }
544
545 #[test]
546 fn test_permission_severity() {
547 assert!(Permission::SystemCall.severity() > Permission::TimerAccess.severity());
548 assert!(Permission::ProcessCreation.severity() > Permission::EnvironmentAccess.severity());
549 }
550
551 #[test]
552 fn test_resource_limits_default() {
553 let limits = ResourceLimits::default();
554 assert!(limits.max_memory_mb > 0);
555 assert!(limits.max_cpu_time_ms > 0);
556 }
557
558 #[test]
559 fn test_sandbox_severity_to_str() {
560 assert_eq!(SandboxSeverity::Info.as_str(), "info");
561 assert_eq!(SandboxSeverity::Warning.as_str(), "warning");
562 assert_eq!(SandboxSeverity::Error.as_str(), "error");
563 assert_eq!(SandboxSeverity::Critical.as_str(), "critical");
564 }
565
566 #[test]
567 fn test_sandbox_severity_try_parse() {
568 assert_eq!(
569 SandboxSeverity::try_parse("info"),
570 Some(SandboxSeverity::Info)
571 );
572 assert_eq!(SandboxSeverity::try_parse("unknown"), None);
573 }
574
575 #[test]
576 fn test_sandbox_severity_ordering() {
577 assert!(SandboxSeverity::Info < SandboxSeverity::Warning);
578 assert!(SandboxSeverity::Warning < SandboxSeverity::Error);
579 assert!(SandboxSeverity::Error < SandboxSeverity::Critical);
580 }
581
582 #[test]
583 fn test_sandbox_risk_level_to_str() {
584 assert_eq!(SandboxRiskLevel::Low.as_str(), "Low");
585 assert_eq!(SandboxRiskLevel::Critical.as_str(), "Critical");
586 }
587
588 #[test]
589 fn test_verifier_creation() {
590 let verifier = PluginSandboxVerifier::new();
591 assert!(!verifier.whitelisted_syscalls.is_empty());
592 }
593
594 #[test]
595 fn test_verifier_default_syscalls() {
596 let verifier = PluginSandboxVerifier::new();
597 assert!(verifier.whitelisted_syscalls.contains_key("read"));
598 assert!(verifier.whitelisted_syscalls.contains_key("write"));
599 assert!(verifier.whitelisted_syscalls.contains_key("execve"));
600 }
601
602 #[test]
603 fn test_check_syscall_whitelisted() {
604 let verifier = PluginSandboxVerifier::new();
605 let result = verifier.check_syscall_whitelist("read");
606 assert!(result.passed);
607 }
608
609 #[test]
610 fn test_check_syscall_not_whitelisted() {
611 let verifier = PluginSandboxVerifier::new();
612 let result = verifier.check_syscall_whitelist("execve");
613 assert!(!result.passed);
614 }
615
616 #[test]
617 fn test_check_permission_request() {
618 let verifier = PluginSandboxVerifier::new();
619 let result = verifier.check_permission_request(Permission::FileSystem);
620 assert!(result.passed);
621 }
622
623 #[test]
624 fn test_check_resource_limits_compliant() {
625 let verifier = PluginSandboxVerifier::new();
626 let requested = ResourceLimits {
627 max_memory_mb: 100,
628 max_cpu_time_ms: 5000,
629 max_file_descriptors: 256,
630 max_threads: 8,
631 max_processes: 1,
632 };
633 let result = verifier.check_resource_limits(&requested);
634 assert!(result.passed);
635 }
636
637 #[test]
638 fn test_check_resource_limits_exceeds() {
639 let verifier = PluginSandboxVerifier::new();
640 let requested = ResourceLimits {
641 max_memory_mb: 1000,
642 max_cpu_time_ms: 60000,
643 max_file_descriptors: 256,
644 max_threads: 8,
645 max_processes: 1,
646 };
647 let result = verifier.check_resource_limits(&requested);
648 assert!(!result.passed);
649 }
650
651 #[test]
652 fn test_calculate_risk_score() {
653 let perms = vec![Permission::FileSystem, Permission::Network];
654 let score = PluginSandboxVerifier::calculate_risk_score(&perms);
655 assert!(score > 0);
656 }
657
658 #[test]
659 fn test_analyze_risk_low() {
660 let perms = vec![Permission::EnvironmentAccess, Permission::TimerAccess];
661 let (_score, _high_risk, level) = PluginSandboxVerifier::analyze_risk(&perms);
662 assert_eq!(level, SandboxRiskLevel::Low);
663 }
664
665 #[test]
666 fn test_analyze_risk_critical() {
667 let perms = vec![
669 Permission::SystemCall,
670 Permission::ProcessCreation,
671 Permission::SignalHandling,
672 Permission::FileSystem,
673 Permission::Network,
674 Permission::ThreadCreation,
675 Permission::MemoryAllocation,
676 Permission::EnvironmentAccess,
677 ];
678 let (score, high_risk, level) = PluginSandboxVerifier::analyze_risk(&perms);
679 assert!(score > 30);
681 assert!(level >= SandboxRiskLevel::High);
682 assert!(high_risk > 0);
683 }
684
685 #[test]
686 fn test_check_capability_model_safe() {
687 let verifier = PluginSandboxVerifier::new();
688 let cap = PluginCapability {
689 name: "safe_io".to_string(),
690 required_permissions: vec![Permission::FileSystem],
691 resource_requirement: None,
692 description: "Safe I/O operations".to_string(),
693 };
694 let result = verifier.check_capability_model(&cap);
695 assert!(result.passed);
696 }
697
698 #[test]
699 fn test_check_capability_model_dangerous() {
700 let verifier = PluginSandboxVerifier::new();
701 let cap = PluginCapability {
702 name: "dangerous_ops".to_string(),
703 required_permissions: vec![Permission::SystemCall, Permission::ProcessCreation],
704 resource_requirement: None,
705 description: "Dangerous operations".to_string(),
706 };
707 let result = verifier.check_capability_model(&cap);
708 assert!(!result.passed);
709 }
710
711 #[test]
712 fn test_sandbox_verification_report_is_compliant() {
713 let report = SandboxVerificationReport {
714 plugin_id: "test".to_string(),
715 plugin_version: "1.0.0".to_string(),
716 verification_timestamp: "2024-01-01T00:00:00Z".to_string(),
717 is_sandboxed: true,
718 checks: vec![],
719 requested_permissions: vec![],
720 resource_limits: ResourceLimits::default(),
721 capabilities: vec![],
722 high_risk_count: 0,
723 total_risk_score: 5,
724 };
725 assert!(report.is_compliant());
726 }
727
728 #[test]
729 fn test_sandbox_verification_report_not_compliant() {
730 let check = SandboxCheckResult {
731 check_name: "Test".to_string(),
732 passed: false,
733 severity: SandboxSeverity::Critical,
734 message: "Failed".to_string(),
735 details: None,
736 remediation: None,
737 };
738 let report = SandboxVerificationReport {
739 plugin_id: "test".to_string(),
740 plugin_version: "1.0.0".to_string(),
741 verification_timestamp: "2024-01-01T00:00:00Z".to_string(),
742 is_sandboxed: true,
743 checks: vec![check],
744 requested_permissions: vec![],
745 resource_limits: ResourceLimits::default(),
746 capabilities: vec![],
747 high_risk_count: 0,
748 total_risk_score: 5,
749 };
750 assert!(!report.is_compliant());
751 }
752
753 #[test]
754 fn test_sandbox_verification_report_risk_assessment() {
755 let report = SandboxVerificationReport {
756 plugin_id: "test".to_string(),
757 plugin_version: "1.0.0".to_string(),
758 verification_timestamp: "2024-01-01T00:00:00Z".to_string(),
759 is_sandboxed: true,
760 checks: vec![],
761 requested_permissions: vec![],
762 resource_limits: ResourceLimits::default(),
763 capabilities: vec![],
764 high_risk_count: 0,
765 total_risk_score: 50,
766 };
767 assert_eq!(report.risk_assessment(), SandboxRiskLevel::High);
768 }
769
770 #[test]
771 fn test_sandbox_verification_report_summary() {
772 let report = SandboxVerificationReport {
773 plugin_id: "test".to_string(),
774 plugin_version: "1.0.0".to_string(),
775 verification_timestamp: "2024-01-01T00:00:00Z".to_string(),
776 is_sandboxed: true,
777 checks: vec![],
778 requested_permissions: vec![Permission::FileSystem],
779 resource_limits: ResourceLimits::default(),
780 capabilities: vec![],
781 high_risk_count: 0,
782 total_risk_score: 15,
783 };
784 let summary = report.summary();
785 assert!(summary.contains("Compliant"));
786 }
787
788 #[test]
789 fn test_sandbox_check_result_serialization() {
790 let result = SandboxCheckResult {
791 check_name: "Test Check".to_string(),
792 passed: true,
793 severity: SandboxSeverity::Info,
794 message: "Test message".to_string(),
795 details: Some("Details".to_string()),
796 remediation: Some("Fix it".to_string()),
797 };
798
799 let json = serde_json::to_string(&result).unwrap();
800 let deserialized: SandboxCheckResult = serde_json::from_str(&json).unwrap();
801
802 assert_eq!(deserialized.check_name, result.check_name);
803 assert_eq!(deserialized.passed, result.passed);
804 }
805
806 #[test]
807 fn test_sandbox_verification_report_serialization() {
808 let report = SandboxVerificationReport {
809 plugin_id: "test-plugin".to_string(),
810 plugin_version: "1.0.0".to_string(),
811 verification_timestamp: "2024-01-01T00:00:00Z".to_string(),
812 is_sandboxed: true,
813 checks: vec![],
814 requested_permissions: vec![],
815 resource_limits: ResourceLimits::default(),
816 capabilities: vec![],
817 high_risk_count: 0,
818 total_risk_score: 0,
819 };
820
821 let json = serde_json::to_string(&report).unwrap();
822 let deserialized: SandboxVerificationReport = serde_json::from_str(&json).unwrap();
823
824 assert_eq!(deserialized.plugin_id, report.plugin_id);
825 }
826
827 #[test]
828 fn test_plugin_capability_serialization() {
829 let cap = PluginCapability {
830 name: "test".to_string(),
831 required_permissions: vec![Permission::FileSystem],
832 resource_requirement: Some("256MB".to_string()),
833 description: "Test capability".to_string(),
834 };
835
836 let json = serde_json::to_string(&cap).unwrap();
837 let deserialized: PluginCapability = serde_json::from_str(&json).unwrap();
838
839 assert_eq!(deserialized.name, cap.name);
840 }
841}