1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-04 12:41:46 +00:00

fix(uninstall): enhance receipt file processing safety and prevent system file deletion

CRITICAL SECURITY FIX

Enhanced the receipt file parsing in uninstall operations to prevent
accidental deletion of critical system files while maintaining deep
cleanup capabilities.

Changes:
- Tightened whitelist in find_app_receipt_files() to exclude /Users/*,
  /usr/*, and /opt/* broad patterns
- Added explicit blacklist for /private/* with safe exceptions for
  logs, temp files, and diagnostic data
- Integrated should_protect_path() check for additional protection
- Added file deduplication with sort -u to prevent duplicate deletions
- Removed dry-run feature from batch uninstall (unused entry point)

Path Protection:
 Blocked: /etc/passwd, /var/db/*, /private/etc/*, all system binaries
 Allowed: /Applications/*, specific /Library/* subdirs, safe /private/* paths
 Additional: Keychain files, system preferences via should_protect_path()

This fixes a critical security issue where parsing .bom receipt files
could result in deletion of system files like /etc/passwd and /var/db/*,
leading to system corruption and data loss.

Affects: V1.12.14 and later versions
Testing: Validated against critical system paths, all blocked correctly
This commit is contained in:
Tw93
2026-01-15 21:01:11 +08:00
parent 30547c9c4c
commit 7dc854cf30
3 changed files with 82 additions and 30 deletions

View File

@@ -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

View File

@@ -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
}

View File

@@ -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