1use std::collections::HashSet;
2
3use crate::redaction::Redactor;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16#[non_exhaustive]
17pub enum PrivacyProfile {
18 Observe,
21 Test,
25 #[default]
27 FullControl,
28}
29
30impl std::fmt::Display for PrivacyProfile {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 Self::Observe => write!(f, "observe"),
34 Self::Test => write!(f, "test"),
35 Self::FullControl => write!(f, "full_control"),
36 }
37 }
38}
39
40#[derive(Default)]
50#[non_exhaustive]
51pub struct PrivacyConfig {
52 pub profile: PrivacyProfile,
54 pub command_allowlist: Option<HashSet<String>>,
56 pub command_blocklist: HashSet<String>,
58 pub disabled_tools: HashSet<String>,
60 pub storage_key_blocklist: HashSet<String>,
63 pub redactor: Redactor,
65 pub redaction_enabled: bool,
67}
68
69impl PrivacyConfig {
70 #[must_use]
72 pub fn is_command_allowed(&self, command: &str) -> bool {
73 if self.command_blocklist.contains(command) {
74 return false;
75 }
76 match &self.command_allowlist {
77 Some(allow) => allow.contains(command),
78 None => true,
79 }
80 }
81
82 #[must_use]
86 pub fn is_storage_key_allowed(&self, key: &str) -> bool {
87 !self.storage_key_blocklist.contains(key)
88 }
89
90 #[must_use]
93 pub fn is_tool_enabled(&self, tool_or_action: &str) -> bool {
94 if self.is_disabled(tool_or_action) {
95 return false;
96 }
97 is_allowed_by_profile(self.profile, tool_or_action)
98 }
99
100 fn is_disabled(&self, name: &str) -> bool {
104 self.disabled_tools.contains(name)
105 || crate::mcp::authz::capability_alias(name)
106 .is_some_and(|alias| self.disabled_tools.contains(alias))
107 }
108
109 #[must_use]
125 pub fn is_call_allowed(&self, bare_tool: &str, capability: &str) -> bool {
126 if self.disabled_tools.contains(bare_tool) {
127 return false;
128 }
129 self.is_tool_enabled(capability)
130 }
131
132 #[must_use]
137 pub fn is_invoke_allowed(&self, command: &str) -> bool {
138 if self.disabled_tools.contains("invoke_command") {
139 return false;
140 }
141 match self.profile {
142 PrivacyProfile::FullControl => true,
143 PrivacyProfile::Test => self
144 .command_allowlist
145 .as_ref()
146 .is_some_and(|al| al.contains(command)),
147 PrivacyProfile::Observe => false,
148 }
149 }
150
151 #[must_use]
153 pub fn redact_output(&self, output: &str) -> String {
154 if self.redaction_enabled {
155 self.redactor.redact(output)
156 } else {
157 output.to_string()
158 }
159 }
160}
161
162#[must_use]
171fn is_allowed_by_profile(profile: PrivacyProfile, tool_or_action: &str) -> bool {
172 match profile {
173 PrivacyProfile::FullControl => true,
174 PrivacyProfile::Test => matches!(
175 tool_or_action,
176 "dom_snapshot"
178 | "find_elements"
179 | "get_registry"
180 | "get_memory_stats"
181 | "get_plugin_info"
182 | "get_diagnostics"
183 | "detect_ghost_commands"
184 | "check_ipc_integrity"
185 | "resolve_command"
186 | "wait_for"
187 | "app_state"
188 | "interact"
196 | "interact.click"
197 | "interact.double_click"
198 | "interact.hover"
199 | "interact.focus"
200 | "interact.scroll_into_view"
201 | "interact.select_option"
202 | "fill"
204 | "input"
205 | "input.fill"
206 | "type_text"
207 | "input.type_text"
208 | "input.press_key"
209 | "storage"
211 | "set_storage"
212 | "storage.set"
213 | "delete_storage"
214 | "storage.delete"
215 | "storage.get"
216 | "storage.get_cookies"
217 | "get_storage"
218 | "get_cookies"
219 | "recording"
221 | "recording.start"
222 | "recording.stop"
223 | "recording.checkpoint"
224 | "recording.list_checkpoints"
225 | "recording.get_events"
226 | "recording.events_between"
227 | "recording.get_replay"
228 | "recording.export"
229 | "recording.import"
230 | "logs"
232 | "logs.console"
233 | "logs.network"
234 | "logs.ipc"
235 | "logs.navigation"
236 | "logs.dialogs"
237 | "logs.events"
238 | "logs.slow_ipc"
239 | "logs.clear"
242 | "inspect"
244 | "inspect.styles"
245 | "inspect.bounds"
246 | "inspect.highlight"
247 | "inspect.clear_highlights"
248 | "inspect.audit_a11y"
249 | "inspect.performance"
250 | "list_windows"
252 | "window"
253 | "window.get_state"
254 | "window.list"
255 | "window.introspectability"
256 | "get_window_state"
257 | "navigate"
262 | "navigate.go_back"
263 | "navigate.get_history"
264 | "navigate.get_dialog_log"
265 ),
266 PrivacyProfile::Observe => matches!(
267 tool_or_action,
268 "dom_snapshot"
269 | "find_elements"
270 | "get_registry"
271 | "get_memory_stats"
272 | "get_plugin_info"
273 | "get_diagnostics"
274 | "detect_ghost_commands"
275 | "check_ipc_integrity"
276 | "resolve_command"
277 | "app_state"
278 | "logs"
279 | "logs.console"
280 | "logs.network"
281 | "logs.ipc"
282 | "logs.navigation"
283 | "logs.dialogs"
284 | "logs.events"
285 | "logs.slow_ipc"
286 | "inspect"
289 | "inspect.styles"
290 | "inspect.bounds"
291 | "inspect.audit_a11y"
294 | "inspect.performance"
295 | "list_windows"
296 | "window"
297 | "window.get_state"
298 | "window.list"
299 | "window.introspectability"
300 | "get_window_state"
301 | "wait_for"
302 ),
303 }
304}
305
306#[must_use]
308#[doc(hidden)]
309pub fn observe_privacy_config() -> PrivacyConfig {
310 PrivacyConfig {
311 profile: PrivacyProfile::Observe,
312 command_allowlist: None,
313 command_blocklist: HashSet::new(),
314 disabled_tools: HashSet::new(),
315 storage_key_blocklist: HashSet::new(),
316 redactor: Redactor::default(),
317 redaction_enabled: true,
318 }
319}
320
321#[must_use]
323#[doc(hidden)]
324pub fn test_privacy_config() -> PrivacyConfig {
325 PrivacyConfig {
326 profile: PrivacyProfile::Test,
327 command_allowlist: None,
328 command_blocklist: HashSet::new(),
329 disabled_tools: HashSet::new(),
330 storage_key_blocklist: HashSet::new(),
331 redactor: Redactor::default(),
332 redaction_enabled: true,
333 }
334}
335
336#[must_use]
341#[doc(hidden)]
342pub fn strict_privacy_config() -> PrivacyConfig {
343 observe_privacy_config()
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
353 fn storage_key_blocklist_protects_keys() {
354 let mut config = PrivacyConfig::default();
355 assert!(config.is_storage_key_allowed("auth"));
357 config.storage_key_blocklist = ["auth", "license_tier"]
359 .iter()
360 .map(ToString::to_string)
361 .collect();
362 assert!(!config.is_storage_key_allowed("auth"));
363 assert!(!config.is_storage_key_allowed("license_tier"));
364 assert!(config.is_storage_key_allowed("theme"));
365 }
366
367 #[test]
368 fn default_allows_all_commands() {
369 let config = PrivacyConfig::default();
370 assert!(config.is_command_allowed("get_settings"));
371 assert!(config.is_command_allowed("anything"));
372 }
373
374 #[test]
375 fn blocklist_blocks() {
376 let mut config = PrivacyConfig::default();
377 config.command_blocklist.insert("save_api_key".to_string());
378 assert!(!config.is_command_allowed("save_api_key"));
379 assert!(config.is_command_allowed("get_settings"));
380 }
381
382 #[test]
383 fn allowlist_restricts() {
384 let mut allow = HashSet::new();
385 allow.insert("get_settings".to_string());
386 allow.insert("get_monitoring_status".to_string());
387 let config = PrivacyConfig {
388 command_allowlist: Some(allow),
389 ..Default::default()
390 };
391 assert!(config.is_command_allowed("get_settings"));
392 assert!(!config.is_command_allowed("save_api_key"));
393 }
394
395 #[test]
396 fn blocklist_wins_over_allowlist() {
397 let mut allow = HashSet::new();
398 allow.insert("save_api_key".to_string());
399 let mut block = HashSet::new();
400 block.insert("save_api_key".to_string());
401 let config = PrivacyConfig {
402 command_allowlist: Some(allow),
403 command_blocklist: block,
404 ..Default::default()
405 };
406 assert!(!config.is_command_allowed("save_api_key"));
407 }
408
409 #[test]
412 fn full_control_allows_everything() {
413 let config = PrivacyConfig::default();
414 assert_eq!(config.profile, PrivacyProfile::FullControl);
415 assert!(config.is_tool_enabled("eval_js"));
416 assert!(config.is_tool_enabled("screenshot"));
417 assert!(config.is_tool_enabled("invoke_command"));
418 assert!(config.is_tool_enabled("interact"));
419 assert!(config.is_tool_enabled("interact.click"));
420 assert!(config.is_tool_enabled("input.fill"));
421 assert!(config.is_tool_enabled("window.manage"));
422 assert!(config.is_tool_enabled("navigate"));
423 assert!(config.is_tool_enabled("navigate.go_to"));
424 assert!(config.is_tool_enabled("css.inject"));
425 assert!(config.is_tool_enabled("recording"));
426 assert!(config.is_tool_enabled("storage.set"));
427 assert!(config.is_tool_enabled("set_dialog_response"));
428 }
429
430 #[test]
433 fn test_profile_allows_interactions() {
434 let config = test_privacy_config();
435 assert!(config.is_tool_enabled("interact"));
436 assert!(config.is_tool_enabled("interact.click"));
437 assert!(config.is_tool_enabled("interact.double_click"));
438 assert!(config.is_tool_enabled("interact.hover"));
439 assert!(config.is_tool_enabled("interact.focus"));
440 assert!(config.is_tool_enabled("interact.scroll_into_view"));
441 assert!(config.is_tool_enabled("interact.select_option"));
442 }
443
444 #[test]
445 fn test_profile_allows_input() {
446 let config = test_privacy_config();
447 assert!(config.is_tool_enabled("fill"));
448 assert!(config.is_tool_enabled("input.fill"));
449 assert!(config.is_tool_enabled("type_text"));
450 assert!(config.is_tool_enabled("input.type_text"));
451 assert!(config.is_tool_enabled("input.press_key"));
452 }
453
454 #[test]
455 fn test_profile_allows_storage_writes() {
456 let config = test_privacy_config();
457 assert!(config.is_tool_enabled("set_storage"));
458 assert!(config.is_tool_enabled("storage.set"));
459 assert!(config.is_tool_enabled("delete_storage"));
460 assert!(config.is_tool_enabled("storage.delete"));
461 }
462
463 #[test]
464 fn test_profile_allows_recording() {
465 let config = test_privacy_config();
466 assert!(config.is_tool_enabled("recording"));
467 assert!(config.is_tool_enabled("recording.start"));
468 assert!(config.is_tool_enabled("recording.stop"));
469 }
470
471 #[test]
472 fn test_profile_blocks_eval_and_screenshot() {
473 let config = test_privacy_config();
474 assert!(!config.is_tool_enabled("eval_js"));
475 assert!(!config.is_tool_enabled("screenshot"));
476 }
477
478 #[test]
479 fn test_profile_blocks_navigation_mutations() {
480 let config = test_privacy_config();
481 assert!(config.is_tool_enabled("navigate"));
485 assert!(!config.is_tool_enabled("navigate.go_to"));
486 assert!(!config.is_tool_enabled("set_dialog_response"));
487 assert!(!config.is_tool_enabled("navigate.set_dialog_response"));
488 }
489
490 #[test]
491 fn test_profile_blocks_window_mutations() {
492 let config = test_privacy_config();
493 assert!(!config.is_tool_enabled("window.manage"));
494 assert!(!config.is_tool_enabled("window.resize"));
495 assert!(!config.is_tool_enabled("window.move_to"));
496 assert!(!config.is_tool_enabled("window.set_title"));
497 }
498
499 #[test]
500 fn test_profile_blocks_css_injection() {
501 let config = test_privacy_config();
502 assert!(!config.is_tool_enabled("inject_css"));
503 assert!(!config.is_tool_enabled("css.inject"));
504 assert!(!config.is_tool_enabled("css.remove"));
505 }
506
507 #[test]
508 fn test_profile_blocks_invoke_command() {
509 let config = test_privacy_config();
510 assert!(!config.is_tool_enabled("invoke_command"));
511 }
512
513 #[test]
514 fn test_profile_allows_read_only_tools() {
515 let config = test_privacy_config();
516 assert!(config.is_tool_enabled("dom_snapshot"));
517 assert!(config.is_tool_enabled("find_elements"));
518 assert!(config.is_tool_enabled("detect_ghost_commands"));
519 assert!(config.is_tool_enabled("check_ipc_integrity"));
520 assert!(config.is_tool_enabled("get_registry"));
521 assert!(config.is_tool_enabled("get_memory_stats"));
522 assert!(config.is_tool_enabled("get_plugin_info"));
523 assert!(config.is_tool_enabled("resolve_command"));
524 assert!(config.is_tool_enabled("wait_for"));
525 }
526
527 #[test]
528 fn test_profile_blocks_arbitrary_eval_assertions() {
529 let config = test_privacy_config();
532 assert!(!config.is_tool_enabled("verify_state"));
533 assert!(!config.is_tool_enabled("assert_semantic"));
534 }
535
536 #[test]
537 fn test_profile_allows_navigation_reads_and_introspectability() {
538 let config = test_privacy_config();
541 assert!(config.is_tool_enabled("navigate.go_back"));
542 assert!(config.is_tool_enabled("navigate.get_history"));
543 assert!(config.is_tool_enabled("navigate.get_dialog_log"));
544 assert!(config.is_tool_enabled("window.introspectability"));
545 assert!(!config.is_tool_enabled("navigate.go_to"));
547 assert!(!config.is_tool_enabled("navigate.set_dialog_response"));
548 }
549
550 #[test]
553 fn observe_blocks_all_interactions() {
554 let config = observe_privacy_config();
555 assert!(!config.is_tool_enabled("interact"));
556 assert!(!config.is_tool_enabled("interact.click"));
557 assert!(!config.is_tool_enabled("interact.double_click"));
558 assert!(!config.is_tool_enabled("interact.hover"));
559 assert!(!config.is_tool_enabled("interact.focus"));
560 assert!(!config.is_tool_enabled("interact.scroll_into_view"));
561 assert!(!config.is_tool_enabled("interact.select_option"));
562 }
563
564 #[test]
565 fn observe_blocks_all_input() {
566 let config = observe_privacy_config();
567 assert!(!config.is_tool_enabled("fill"));
568 assert!(!config.is_tool_enabled("input.fill"));
569 assert!(!config.is_tool_enabled("type_text"));
570 assert!(!config.is_tool_enabled("input.type_text"));
571 assert!(!config.is_tool_enabled("input.press_key"));
572 }
573
574 #[test]
575 fn observe_blocks_storage_writes() {
576 let config = observe_privacy_config();
577 assert!(!config.is_tool_enabled("set_storage"));
578 assert!(!config.is_tool_enabled("storage.set"));
579 assert!(!config.is_tool_enabled("delete_storage"));
580 assert!(!config.is_tool_enabled("storage.delete"));
581 }
582
583 #[test]
584 fn observe_blocks_recording() {
585 let config = observe_privacy_config();
586 assert!(!config.is_tool_enabled("recording"));
587 assert!(!config.is_tool_enabled("recording.start"));
588 assert!(!config.is_tool_enabled("recording.stop"));
589 }
590
591 #[test]
592 fn observe_blocks_dangerous_tools() {
593 let config = observe_privacy_config();
594 assert!(!config.is_tool_enabled("eval_js"));
595 assert!(!config.is_tool_enabled("screenshot"));
596 assert!(!config.is_tool_enabled("invoke_command"));
597 assert!(!config.is_tool_enabled("navigate"));
598 assert!(!config.is_tool_enabled("navigate.go_to"));
599 assert!(!config.is_tool_enabled("inject_css"));
600 assert!(!config.is_tool_enabled("css.inject"));
601 assert!(!config.is_tool_enabled("css.remove"));
602 assert!(!config.is_tool_enabled("window.manage"));
603 assert!(!config.is_tool_enabled("window.resize"));
604 assert!(!config.is_tool_enabled("window.move_to"));
605 assert!(!config.is_tool_enabled("window.set_title"));
606 }
607
608 #[test]
609 fn observe_allows_read_only_tools() {
610 let config = observe_privacy_config();
611 assert!(config.is_tool_enabled("dom_snapshot"));
612 assert!(config.is_tool_enabled("find_elements"));
613 assert!(config.is_tool_enabled("detect_ghost_commands"));
614 assert!(config.is_tool_enabled("check_ipc_integrity"));
615 assert!(config.is_tool_enabled("get_registry"));
616 assert!(config.is_tool_enabled("get_memory_stats"));
617 assert!(config.is_tool_enabled("get_plugin_info"));
618 assert!(config.is_tool_enabled("get_diagnostics"));
619 assert!(config.is_tool_enabled("resolve_command"));
620 assert!(config.is_tool_enabled("wait_for"));
621 assert!(config.is_tool_enabled("window")); }
623
624 #[test]
625 fn observe_blocks_eval_dependent_tools() {
626 let config = observe_privacy_config();
627 assert!(!config.is_tool_enabled("verify_state"));
630 assert!(!config.is_tool_enabled("assert_semantic"));
631 }
632
633 #[test]
634 fn observe_allows_read_actions_on_compound_tools() {
635 let config = observe_privacy_config();
636 assert!(config.is_tool_enabled("window.get_state"));
638 assert!(config.is_tool_enabled("window.list"));
639 assert!(config.is_tool_enabled("logs"));
641 assert!(config.is_tool_enabled("logs.console"));
642 assert!(config.is_tool_enabled("logs.network"));
643 assert!(config.is_tool_enabled("logs.ipc"));
644 assert!(config.is_tool_enabled("logs.navigation"));
645 assert!(config.is_tool_enabled("logs.dialogs"));
646 assert!(config.is_tool_enabled("logs.events"));
647 assert!(config.is_tool_enabled("logs.slow_ipc"));
648 assert!(config.is_tool_enabled("inspect"));
650 assert!(config.is_tool_enabled("inspect.styles"));
651 assert!(config.is_tool_enabled("inspect.bounds"));
652 assert!(config.is_tool_enabled("inspect.audit_a11y"));
653 assert!(config.is_tool_enabled("inspect.performance"));
654 assert!(!config.is_tool_enabled("inspect.highlight"));
656 assert!(!config.is_tool_enabled("inspect.clear_highlights"));
657 assert!(!config.is_tool_enabled("logs.clear"));
658 }
659
660 #[test]
661 fn observe_allows_app_state_probe_reads() {
662 assert!(observe_privacy_config().is_tool_enabled("app_state"));
664 }
665
666 #[test]
667 fn test_profile_allows_log_clearing_and_highlight() {
668 let config = test_privacy_config();
671 assert!(config.is_tool_enabled("logs.clear"));
672 assert!(config.is_tool_enabled("inspect.highlight"));
673 assert!(config.is_tool_enabled("inspect.clear_highlights"));
674 assert!(config.is_tool_enabled("app_state"));
675 }
676
677 #[test]
678 fn observe_blocks_unlisted_tools() {
679 let config = observe_privacy_config();
680 assert!(!config.is_tool_enabled("navigate.get_history"));
682 assert!(!config.is_tool_enabled("navigate.get_dialog_log"));
683 assert!(!config.is_tool_enabled("css.get_styles"));
684 assert!(!config.is_tool_enabled("css.get_computed"));
685 assert!(!config.is_tool_enabled("storage.get"));
686 assert!(!config.is_tool_enabled("storage.get_cookies"));
687 assert!(!config.is_tool_enabled("navigate.go_back"));
688 }
689
690 #[test]
691 fn observe_enables_redaction() {
692 let config = observe_privacy_config();
693 assert!(config.redaction_enabled);
694 }
695
696 #[test]
699 fn disabled_tools_override_full_control() {
700 let mut disabled = HashSet::new();
701 disabled.insert("eval_js".to_string());
702 let config = PrivacyConfig {
703 profile: PrivacyProfile::FullControl,
704 disabled_tools: disabled,
705 ..Default::default()
706 };
707 assert!(!config.is_tool_enabled("eval_js"));
708 assert!(config.is_tool_enabled("screenshot"));
709 }
710
711 #[test]
712 fn disabled_tools_stack_with_profile() {
713 let mut disabled = HashSet::new();
714 disabled.insert("dom_snapshot".to_string());
715 let mut config = test_privacy_config();
716 config.disabled_tools = disabled;
717 assert!(!config.is_tool_enabled("dom_snapshot"));
719 assert!(!config.is_tool_enabled("eval_js"));
721 }
722
723 #[test]
728 fn inspect_action_spelling_and_capability_id_both_disable() {
729 for (action_spelling, capability) in [
730 ("inspect.get_styles", "inspect.styles"),
731 ("inspect.get_bounding_boxes", "inspect.bounds"),
732 ("inspect.audit_accessibility", "inspect.audit_a11y"),
733 ("inspect.get_performance", "inspect.performance"),
734 ] {
735 for disabled in [action_spelling, capability] {
736 let config = PrivacyConfig {
737 disabled_tools: HashSet::from([disabled.to_string()]),
738 ..Default::default()
739 };
740 assert!(
741 !config.is_call_allowed("inspect", capability),
742 "disabling {disabled:?} must block {capability}"
743 );
744 assert!(
745 !config.is_tool_enabled(action_spelling),
746 "disabling {disabled:?} must block {action_spelling}"
747 );
748 }
749 }
750 let config = PrivacyConfig {
752 disabled_tools: HashSet::from(["inspect.get_styles".to_string()]),
753 ..Default::default()
754 };
755 assert!(config.is_call_allowed("inspect", "inspect.bounds"));
756 assert!(config.is_call_allowed("inspect", "inspect.highlight"));
757 }
758
759 #[test]
762 fn invoke_allowed_in_full_control() {
763 let config = PrivacyConfig::default();
764 assert!(config.is_invoke_allowed("any_command"));
765 }
766
767 #[test]
768 fn invoke_blocked_in_observe() {
769 let config = observe_privacy_config();
770 assert!(!config.is_invoke_allowed("any_command"));
771 }
772
773 #[test]
774 fn invoke_allowed_in_test_with_allowlist() {
775 let mut allow = HashSet::new();
776 allow.insert("greet".to_string());
777 let mut config = test_privacy_config();
778 config.command_allowlist = Some(allow);
779 assert!(config.is_invoke_allowed("greet"));
780 assert!(!config.is_invoke_allowed("delete_user"));
781 }
782
783 #[test]
784 fn invoke_blocked_in_test_without_allowlist() {
785 let config = test_privacy_config();
786 assert!(!config.is_invoke_allowed("greet"));
787 }
788
789 #[test]
792 fn strict_privacy_is_observe_profile() {
793 let config = strict_privacy_config();
794 assert_eq!(config.profile, PrivacyProfile::Observe);
795 assert!(config.redaction_enabled);
796 }
797
798 #[test]
801 fn strict_mode_disables_dangerous_tools() {
802 let config = strict_privacy_config();
803 assert!(!config.is_tool_enabled("eval_js"));
804 assert!(!config.is_tool_enabled("screenshot"));
805 assert!(!config.is_tool_enabled("inject_css"));
806 assert!(!config.is_tool_enabled("navigate"));
807 assert!(!config.is_tool_enabled("invoke_command"));
808 assert!(config.is_tool_enabled("dom_snapshot"));
809 assert!(config.is_tool_enabled("get_memory_stats"));
810 assert!(config.redaction_enabled);
811 }
812
813 #[test]
814 fn strict_mode_blocks_window_mutations() {
815 let config = strict_privacy_config();
816 assert!(!config.is_tool_enabled("window.manage"));
817 assert!(!config.is_tool_enabled("window.resize"));
818 assert!(!config.is_tool_enabled("window.move_to"));
819 assert!(!config.is_tool_enabled("window.set_title"));
820 assert!(config.is_tool_enabled("window"));
821 }
822
823 #[test]
824 fn default_allows_all_actions() {
825 let config = PrivacyConfig::default();
826 assert!(config.is_tool_enabled("invoke_command"));
827 assert!(config.is_tool_enabled("window.manage"));
828 assert!(config.is_tool_enabled("window.resize"));
829 assert!(config.is_tool_enabled("window.move_to"));
830 assert!(config.is_tool_enabled("window.set_title"));
831 }
832
833 #[test]
836 fn redaction_when_enabled() {
837 let config = PrivacyConfig {
838 redaction_enabled: true,
839 ..Default::default()
840 };
841 let output = config.redact_output("key is sk-abc123def456ghi789jkl012mno");
842 assert!(output.contains("[REDACTED]"));
843 }
844
845 #[test]
846 fn no_redaction_when_disabled() {
847 let config = PrivacyConfig::default();
848 let input = "key is sk-abc123def456ghi789jkl012mno";
849 assert_eq!(config.redact_output(input), input);
850 }
851
852 #[test]
855 fn profile_display() {
856 assert_eq!(PrivacyProfile::Observe.to_string(), "observe");
857 assert_eq!(PrivacyProfile::Test.to_string(), "test");
858 assert_eq!(PrivacyProfile::FullControl.to_string(), "full_control");
859 }
860}