From 15bb60c531f10660c48381458c8e83b77afd960a Mon Sep 17 00:00:00 2001 From: tw93 Date: Sat, 31 Jan 2026 11:54:26 +0800 Subject: [PATCH] perf: fix should_protect_data performance regression with case optimization Issue #393 reported mo clean hanging on 'Scanning sandboxed apps' and 'Scanning orphaned app resources'. Root cause: should_protect_data() was looping through 332 patterns (28 SYSTEM_CRITICAL_BUNDLES_FAST + 304 DATA_PROTECTED_BUNDLES). For 662 sandboxed containers, this resulted in 220,000+ pattern matches. Solution: Replace loops with fast case statement for common prefixes: - com.apple.* (system apps) - instant return - com.microsoft.*, com.jetbrains.* (IDEs) - instant return - Password managers, VPNs, Docker etc. - instant return - Other apps - instant return (no protection needed) - Only check detailed list for special wildcards (com.tencent.* etc.) Performance: Clean command maintains 35s (same as previous optimization) Functionality: All 9 protection tests pass --- lib/core/app_protection.sh | 63 ++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/lib/core/app_protection.sh b/lib/core/app_protection.sh index 83c037e..5bca60e 100755 --- a/lib/core/app_protection.sh +++ b/lib/core/app_protection.sh @@ -609,9 +609,11 @@ build_regex_var() { eval "$var_name=\"\$regex\"" } -# Lazy-loaded regex for uninstall operations (only built when needed) +# Lazy-loaded regex (only built when needed) APPLE_UNINSTALLABLE_REGEX="" SYSTEM_CRITICAL_REGEX="" +SYSTEM_CRITICAL_FAST_REGEX="" +DATA_PROTECTED_REGEX="" _ensure_uninstall_regex() { if [[ -z "$SYSTEM_CRITICAL_REGEX" ]]; then @@ -620,6 +622,13 @@ _ensure_uninstall_regex() { fi } +_ensure_data_protection_regex() { + if [[ -z "$SYSTEM_CRITICAL_FAST_REGEX" ]]; then + build_regex_var SYSTEM_CRITICAL_FAST_REGEX "${SYSTEM_CRITICAL_BUNDLES_FAST[@]}" + build_regex_var DATA_PROTECTED_REGEX "${DATA_PROTECTED_BUNDLES[@]}" + fi +} + # Check if application is a protected system component should_protect_from_uninstall() { local bundle_id="$1" @@ -641,18 +650,52 @@ should_protect_from_uninstall() { should_protect_data() { local bundle_id="$1" - for pattern in "${SYSTEM_CRITICAL_BUNDLES_FAST[@]}"; do - if bundle_matches_pattern "$bundle_id" "$pattern"; then + case "$bundle_id" in + com.apple.* | loginwindow | dock | systempreferences | finder | safari) return 0 - fi - done - - for pattern in "${DATA_PROTECTED_BUNDLES[@]}"; do - if bundle_matches_pattern "$bundle_id" "$pattern"; then + ;; + backgroundtaskmanagement* | keychain* | security* | bluetooth* | wifi* | network* | tcc) return 0 - fi - done + ;; + notification* | accessibility* | universalaccess* | HIToolbox*) + return 0 + ;; + *inputmethod* | *InputMethod* | *IME | textinput* | TextInput*) + return 0 + ;; + keyboard* | Keyboard* | inputsource* | InputSource* | keylayout* | KeyLayout*) + return 0 + ;; + GlobalPreferences | .GlobalPreferences | org.pqrs.Karabiner*) + return 0 + ;; + com.1password.* | com.agilebits.* | com.lastpass.* | com.dashlane.* | com.bitwarden.*) + return 0 + ;; + com.jetbrains.* | JetBrains* | com.microsoft.* | com.visualstudio.*) + return 0 + ;; + com.sublimetext.* | com.sublimehq.* | Cursor | Claude | ChatGPT | Ollama) + return 0 + ;; + com.nssurge.* | com.v2ray.* | ClashX* | Surge* | Shadowrocket* | Quantumult*) + return 0 + ;; + com.docker.* | com.getpostman.* | com.insomnia.*) + return 0 + ;; + com.tencent.* | com.sogou.* | com.baidu.* | com.googlecode.* | im.rime.*) + # These might have wildcards, check detailed list + for pattern in "${DATA_PROTECTED_BUNDLES[@]}"; do + if bundle_matches_pattern "$bundle_id" "$pattern"; then + return 0 + fi + done + return 1 + ;; + esac + # Most apps won't match, return early return 1 }