diff --git a/lib/core/app_protection.sh b/lib/core/app_protection.sh index f68c57a..4770bad 100755 --- a/lib/core/app_protection.sh +++ b/lib/core/app_protection.sh @@ -877,12 +877,24 @@ find_app_system_files() { done < <(command find /private/var/db/receipts -maxdepth 1 \( -name "*$bundle_id*" \) -print0 2> /dev/null) fi + local receipt_files="" + receipt_files=$(find_app_receipt_files "$bundle_id") + + local combined_files="" if [[ ${#system_files[@]} -gt 0 ]]; then - printf '%s\n' "${system_files[@]}" + combined_files=$(printf '%s\n' "${system_files[@]}") fi - # Find files from receipts (Deep Scan) - find_app_receipt_files "$bundle_id" + if [[ -n "$receipt_files" ]]; then + if [[ -n "$combined_files" ]]; then + combined_files+=$'\n' + fi + combined_files+="$receipt_files" + fi + + if [[ -n "$combined_files" ]]; then + printf '%s\n' "$combined_files" | sort -u + fi } # Locate files using installation receipts (BOM) @@ -928,44 +940,44 @@ find_app_receipt_files() { # ------------------------------------------------------------------------ local is_safe=false - # Whitelisted prefixes + # Whitelisted prefixes (exclude /Users, /usr, /opt) case "$clean_path" in /Applications/*) is_safe=true ;; - /Users/*) is_safe=true ;; - /usr/local/*) is_safe=true ;; - /opt/*) is_safe=true ;; - /Library/*) - # Filter sub-paths in /Library to avoid system damage - # Allow safely: Application Support, Caches, Logs, Preferences - case "$clean_path" in - /Library/Application\ Support/*) is_safe=true ;; - /Library/Caches/*) is_safe=true ;; - /Library/Logs/*) is_safe=true ;; - /Library/Preferences/*) is_safe=true ;; - /Library/PrivilegedHelperTools/*) is_safe=true ;; - /Library/LaunchAgents/*) is_safe=true ;; - /Library/LaunchDaemons/*) is_safe=true ;; - /Library/Internet\ Plug-Ins/*) is_safe=true ;; - /Library/Audio/Plug-Ins/*) is_safe=true ;; - /Library/Extensions/*) is_safe=false ;; # Default unsafe - *) is_safe=false ;; - esac - ;; + /Library/Application\ Support/*) is_safe=true ;; + /Library/Caches/*) is_safe=true ;; + /Library/Logs/*) is_safe=true ;; + /Library/Preferences/*) is_safe=true ;; + /Library/LaunchAgents/*) is_safe=true ;; + /Library/LaunchDaemons/*) is_safe=true ;; + /Library/PrivilegedHelperTools/*) is_safe=true ;; + /Library/Internet\ Plug-Ins/*) is_safe=true ;; + /Library/Audio/Plug-Ins/*) is_safe=true ;; + /Library/Frameworks/*) is_safe=true ;; + /Library/Input\ Methods/*) is_safe=true ;; + /Library/QuickLook/*) is_safe=true ;; + /Library/PreferencePanes/*) is_safe=true ;; + /Library/Screen\ Savers/*) is_safe=true ;; + /Library/Extensions/*) is_safe=false ;; + *) is_safe=false ;; esac # Hard blocks case "$clean_path" in - /System/* | /usr/bin/* | /usr/lib/* | /bin/* | /sbin/*) is_safe=false ;; + /System/* | /usr/bin/* | /usr/lib/* | /bin/* | /sbin/* | /private/*) is_safe=false ;; esac if [[ "$is_safe" == "true" && -e "$clean_path" ]]; then - # If lsbom lists /Applications, skip to avoid system damage. - # Extra check: path must be deep enough? - # If path is just "/Applications", skip. - if [[ "$clean_path" == "/Applications" || "$clean_path" == "/Library" || "$clean_path" == "/usr/local" ]]; then + # Skip top-level directories + if [[ "$clean_path" == "/Applications" || "$clean_path" == "/Library" ]]; then continue fi + if declare -f should_protect_path > /dev/null 2>&1; then + if should_protect_path "$clean_path"; then + continue + fi + fi + receipt_files+=("$clean_path") fi diff --git a/lib/core/file_ops.sh b/lib/core/file_ops.sh index 4fb03a7..9fdd65f 100644 --- a/lib/core/file_ops.sh +++ b/lib/core/file_ops.sh @@ -66,14 +66,50 @@ validate_path_for_deletion() { ;; esac + # Allow known safe paths under /private + case "$path" in + /private/tmp | /private/tmp/* | \ + /private/var/tmp | /private/var/tmp/* | \ + /private/var/log | /private/var/log/* | \ + /private/var/folders | /private/var/folders/* | \ + /private/var/db/diagnostics | /private/var/db/diagnostics/* | \ + /private/var/db/DiagnosticPipeline | /private/var/db/DiagnosticPipeline/* | \ + /private/var/db/powerlog | /private/var/db/powerlog/* | \ + /private/var/db/reportmemoryexception | /private/var/db/reportmemoryexception/*) + return 0 + ;; + esac + # Check path isn't critical system directory case "$path" in - / | /bin | /sbin | /usr | /usr/bin | /usr/sbin | /etc | /var | /System | /System/* | /Library/Extensions) + / | /bin | /bin/* | /sbin | /sbin/* | /usr | /usr/bin | /usr/bin/* | /usr/sbin | /usr/sbin/* | /usr/lib | /usr/lib/* | /System | /System/* | /Library/Extensions) log_error "Path validation failed: critical system directory: $path" return 1 ;; + /private) + log_error "Path validation failed: critical system directory: $path" + return 1 + ;; + /etc | /etc/* | /private/etc | /private/etc/*) + log_error "Path validation failed: /etc contains critical system files: $path" + return 1 + ;; + /var | /var/db | /var/db/* | /private/var | /private/var/db | /private/var/db/*) + log_error "Path validation failed: /var/db contains system databases: $path" + return 1 + ;; esac + # Check if path is protected (keychains, system settings, etc) + if declare -f should_protect_path > /dev/null 2>&1; then + if should_protect_path "$path"; then + if [[ "${MO_DEBUG:-0}" == "1" ]]; then + log_warning "Path validation: protected path skipped: $path" + fi + return 1 + fi + fi + return 0 } diff --git a/lib/uninstall/batch.sh b/lib/uninstall/batch.sh index cf98629..8ee9b69 100755 --- a/lib/uninstall/batch.sh +++ b/lib/uninstall/batch.sh @@ -136,6 +136,10 @@ remove_file_list() { while IFS= read -r file; do [[ -n "$file" && -e "$file" ]] || continue + if ! validate_path_for_deletion "$file"; then + continue + fi + if [[ -L "$file" ]]; then if [[ "$use_sudo" == "true" ]]; then sudo rm "$file" 2> /dev/null && ((++count)) || true