1use std::path::Path;
2
3use crate::types::Risk;
4
5const CRITICAL_APP_PATTERNS: &[&str] = &[
10 "telegram",
12 "whatsapp",
13 "signal",
14 "discord",
15 "viber",
16 "line",
17 "wechat",
18 "imessage",
19 "messages",
20 "1password",
22 "bitwarden",
23 "lastpass",
24 "dashlane",
25 "enpass",
26 "keepass",
27 "thunderbird",
29 "airmail",
30 "spark",
31 "canary mail",
32 "obsidian",
34 "bear",
35 "notion",
36 "evernote",
37 "joplin",
38 "logseq",
39 "craft",
40 "apple notes",
41 "metamask",
43 "ledger",
44 "exodus",
45 "phantom",
46 "coinbase wallet",
47 "mongodb",
49 "postgres",
50 "mysql",
51 "redis",
52 "nordvpn",
54 "expressvpn",
55 "wireguard",
56 "tunnelblick",
57 "docker",
59 "github desktop",
60 "sourcetree",
61];
62
63const CRITICAL_FILE_PATTERNS: &[&str] = &[
65 ".sqlite",
66 ".db",
67 ".realm",
68 "keychain",
69 "credential",
70 "session",
71 "auth",
72 "wallet",
73 "vault",
74 "accounts",
75 "login",
76];
77
78pub fn is_critical_app_path(path: &Path) -> bool {
80 let path_lower = path.to_string_lossy().to_lowercase();
81 CRITICAL_APP_PATTERNS
82 .iter()
83 .any(|pattern| path_lower.contains(pattern))
84}
85
86pub fn is_critical_file(path: &Path) -> bool {
88 let name = path
89 .file_name()
90 .map(|n| n.to_string_lossy().to_lowercase())
91 .unwrap_or_default();
92 CRITICAL_FILE_PATTERNS.iter().any(|p| name.contains(p))
93}
94
95pub fn adjust_risk(path: &Path, current_risk: Risk) -> Risk {
98 if current_risk == Risk::High {
99 return Risk::High;
100 }
101 if is_critical_app_path(path) || is_critical_file(path) {
102 return Risk::High;
103 }
104 current_risk
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110 use std::path::PathBuf;
111
112 #[test]
113 fn telegram_is_critical() {
114 let path = PathBuf::from(
115 "/Users/x/Library/Group Containers/group.6N38VWS5BX.ru.keepcoder.Telegram",
116 );
117 assert!(is_critical_app_path(&path));
118 }
119
120 #[test]
121 fn whatsapp_is_critical() {
122 let path = PathBuf::from("/Users/x/Library/Application Support/WhatsApp");
123 assert!(is_critical_app_path(&path));
124 }
125
126 #[test]
127 fn onepassword_is_critical() {
128 let path = PathBuf::from("/Users/x/Library/Containers/com.1password.1password");
129 assert!(is_critical_app_path(&path));
130 }
131
132 #[test]
133 fn sqlite_file_is_critical() {
134 let path = PathBuf::from("/some/path/messages.sqlite");
135 assert!(is_critical_file(&path));
136 }
137
138 #[test]
139 fn cache_file_is_not_critical() {
140 let path = PathBuf::from("/Users/x/Library/Caches/BraveSoftware");
141 assert!(!is_critical_app_path(&path));
142 assert!(!is_critical_file(&path));
143 }
144
145 #[test]
146 fn adjust_risk_elevates() {
147 let path = PathBuf::from("/Users/x/Library/Application Support/Telegram");
148 assert_eq!(adjust_risk(&path, Risk::Low), Risk::High);
149 assert_eq!(adjust_risk(&path, Risk::None), Risk::High);
150 }
151
152 #[test]
153 fn adjust_risk_keeps_high() {
154 let path = PathBuf::from("/Users/x/Downloads");
155 assert_eq!(adjust_risk(&path, Risk::High), Risk::High);
156 }
157
158 #[test]
159 fn adjust_risk_normal_path_unchanged() {
160 let path = PathBuf::from("/Users/x/Library/Caches/com.brave.Browser");
161 assert_eq!(adjust_risk(&path, Risk::None), Risk::None);
162 }
163}