mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 13:16:47 +00:00
fix: remove insecure empty folder cleanup logic to prevent critical data loss (#320)
- Removes clean_empty_library_items functionality that incorrectly deleted critical paths (e.g., Postgres data, Steam locks) - Cleans up associated tests and unnecessary protection rules - Ensures empty folders are preserved by default for safety
This commit is contained in:
@@ -5,9 +5,7 @@ clean_user_essentials() {
|
|||||||
start_section_spinner "Scanning caches..."
|
start_section_spinner "Scanning caches..."
|
||||||
safe_clean ~/Library/Caches/* "User app cache"
|
safe_clean ~/Library/Caches/* "User app cache"
|
||||||
stop_section_spinner
|
stop_section_spinner
|
||||||
start_section_spinner "Scanning empty items..."
|
|
||||||
clean_empty_library_items
|
|
||||||
stop_section_spinner
|
|
||||||
safe_clean ~/Library/Logs/* "User app logs"
|
safe_clean ~/Library/Logs/* "User app logs"
|
||||||
if is_path_whitelisted "$HOME/.Trash"; then
|
if is_path_whitelisted "$HOME/.Trash"; then
|
||||||
note_activity
|
note_activity
|
||||||
@@ -17,65 +15,7 @@ clean_user_essentials() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
clean_empty_library_items() {
|
|
||||||
if [[ ! -d "$HOME/Library" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 1. Clean top-level empty directories and files in Library
|
|
||||||
local -a empty_dirs=()
|
|
||||||
while IFS= read -r -d '' dir; do
|
|
||||||
[[ -d "$dir" ]] && empty_dirs+=("$dir")
|
|
||||||
done < <(find "$HOME/Library" -mindepth 1 -maxdepth 1 -type d -empty -print0 2> /dev/null)
|
|
||||||
|
|
||||||
if [[ ${#empty_dirs[@]} -gt 0 ]]; then
|
|
||||||
safe_clean "${empty_dirs[@]}" "Empty Library folders"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 2. Clean empty subdirectories in Application Support and other key locations
|
|
||||||
# Iteratively remove empty directories until no more are found
|
|
||||||
local -a key_locations=(
|
|
||||||
"$HOME/Library/Application Support"
|
|
||||||
"$HOME/Library/Caches"
|
|
||||||
)
|
|
||||||
|
|
||||||
for location in "${key_locations[@]}"; do
|
|
||||||
[[ -d "$location" ]] || continue
|
|
||||||
|
|
||||||
# Limit passes to keep cleanup fast; 3 iterations handle most nested scenarios.
|
|
||||||
local max_iterations=3
|
|
||||||
local iteration=0
|
|
||||||
|
|
||||||
while [[ $iteration -lt $max_iterations ]]; do
|
|
||||||
local -a nested_empty_dirs=()
|
|
||||||
# Find empty directories
|
|
||||||
while IFS= read -r -d '' dir; do
|
|
||||||
# Skip if whitelisted
|
|
||||||
if is_path_whitelisted "$dir"; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
# Skip protected system components
|
|
||||||
local dir_name=$(basename "$dir")
|
|
||||||
if is_critical_system_component "$dir_name"; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
[[ -d "$dir" ]] && nested_empty_dirs+=("$dir")
|
|
||||||
done < <(find "$location" -mindepth 1 -type d -empty -print0 2> /dev/null)
|
|
||||||
|
|
||||||
# If no empty dirs found, we're done with this location
|
|
||||||
if [[ ${#nested_empty_dirs[@]} -eq 0 ]]; then
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
local location_name=$(basename "$location")
|
|
||||||
safe_clean "${nested_empty_dirs[@]}" "Empty $location_name subdirs"
|
|
||||||
|
|
||||||
((iteration++))
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
# Empty file cleanup is skipped to avoid removing app sentinel files.
|
|
||||||
}
|
|
||||||
|
|
||||||
# Remove old Google Chrome versions while keeping Current.
|
# Remove old Google Chrome versions while keeping Current.
|
||||||
clean_chrome_old_versions() {
|
clean_chrome_old_versions() {
|
||||||
|
|||||||
@@ -114,3 +114,4 @@ EOF
|
|||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
[[ "$output" == "ok" ]]
|
[[ "$output" == "ok" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -264,91 +264,4 @@ EOF
|
|||||||
[[ "$output" == *"Time Machine backup in progress, skipping cleanup"* ]]
|
[[ "$output" == *"Time Machine backup in progress, skipping cleanup"* ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "clean_empty_library_items removes nested empty directories in Application Support" {
|
|
||||||
# Create nested empty directory structure
|
|
||||||
mkdir -p "$HOME/Library/Application Support/UninstalledApp1/SubDir/DeepDir"
|
|
||||||
mkdir -p "$HOME/Library/Application Support/UninstalledApp2/Cache"
|
|
||||||
mkdir -p "$HOME/Library/Application Support/ActiveApp/Data"
|
|
||||||
mkdir -p "$HOME/Library/Caches/EmptyCache/SubCache"
|
|
||||||
|
|
||||||
# Create a file in ActiveApp to make it non-empty
|
|
||||||
touch "$HOME/Library/Application Support/ActiveApp/Data/config.json"
|
|
||||||
|
|
||||||
# Create top-level empty directory in Library
|
|
||||||
mkdir -p "$HOME/Library/EmptyTopLevel"
|
|
||||||
|
|
||||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc << 'EOF'
|
|
||||||
set -euo pipefail
|
|
||||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
|
||||||
source "$PROJECT_ROOT/lib/clean/user.sh"
|
|
||||||
|
|
||||||
# Mock dependencies
|
|
||||||
is_path_whitelisted() { return 1; }
|
|
||||||
is_critical_system_component() { return 1; }
|
|
||||||
bytes_to_human() { echo "$1"; }
|
|
||||||
note_activity() { :; }
|
|
||||||
safe_clean() {
|
|
||||||
# Actually remove the directories for testing
|
|
||||||
for path in "$@"; do
|
|
||||||
if [ "$path" != "${@: -1}" ]; then # Skip the description (last arg)
|
|
||||||
rm -rf "$path" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
clean_empty_library_items
|
|
||||||
EOF
|
|
||||||
|
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
|
|
||||||
# Empty nested dirs should be removed
|
|
||||||
[ ! -d "$HOME/Library/Application Support/UninstalledApp1" ]
|
|
||||||
[ ! -d "$HOME/Library/Application Support/UninstalledApp2" ]
|
|
||||||
[ ! -d "$HOME/Library/Caches/EmptyCache" ]
|
|
||||||
[ ! -d "$HOME/Library/EmptyTopLevel" ]
|
|
||||||
|
|
||||||
# Non-empty directory should remain
|
|
||||||
[ -d "$HOME/Library/Application Support/ActiveApp" ]
|
|
||||||
[ -f "$HOME/Library/Application Support/ActiveApp/Data/config.json" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
@test "clean_empty_library_items respects whitelist for empty directories" {
|
|
||||||
mkdir -p "$HOME/Library/Application Support/ProtectedEmptyApp"
|
|
||||||
mkdir -p "$HOME/Library/Application Support/UnprotectedEmptyApp"
|
|
||||||
mkdir -p "$HOME/.config/mole"
|
|
||||||
|
|
||||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc << 'EOF'
|
|
||||||
set -euo pipefail
|
|
||||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
|
||||||
source "$PROJECT_ROOT/lib/clean/user.sh"
|
|
||||||
|
|
||||||
# Mock dependencies
|
|
||||||
is_critical_system_component() { return 1; }
|
|
||||||
bytes_to_human() { echo "$1"; }
|
|
||||||
note_activity() { :; }
|
|
||||||
|
|
||||||
# Mock whitelist to protect ProtectedEmptyApp
|
|
||||||
is_path_whitelisted() {
|
|
||||||
[[ "$1" == *"ProtectedEmptyApp"* ]]
|
|
||||||
}
|
|
||||||
|
|
||||||
safe_clean() {
|
|
||||||
# Actually remove the directories for testing
|
|
||||||
for path in "$@"; do
|
|
||||||
if [ "$path" != "${@: -1}" ]; then # Skip the description (last arg)
|
|
||||||
rm -rf "$path" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
clean_empty_library_items
|
|
||||||
EOF
|
|
||||||
|
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
|
|
||||||
# Whitelisted directory should remain even if empty
|
|
||||||
[ -d "$HOME/Library/Application Support/ProtectedEmptyApp" ]
|
|
||||||
|
|
||||||
# Non-whitelisted directory should be removed
|
|
||||||
[ ! -d "$HOME/Library/Application Support/UnprotectedEmptyApp" ]
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -103,21 +103,7 @@ EOF
|
|||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "clean_empty_library_items cleans empty dirs and files" {
|
|
||||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" /bin/bash --noprofile --norc <<'EOF'
|
|
||||||
set -euo pipefail
|
|
||||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
|
||||||
source "$PROJECT_ROOT/lib/clean/user.sh"
|
|
||||||
safe_clean() { echo "$2"; }
|
|
||||||
WHITELIST_PATTERNS=()
|
|
||||||
mkdir -p "$HOME/Library/EmptyDir"
|
|
||||||
touch "$HOME/Library/empty.txt"
|
|
||||||
clean_empty_library_items
|
|
||||||
EOF
|
|
||||||
|
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
[[ "$output" == *"Empty Library folders"* ]]
|
|
||||||
}
|
|
||||||
|
|
||||||
@test "clean_browsers calls expected cache paths" {
|
@test "clean_browsers calls expected cache paths" {
|
||||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
|
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
|
||||||
|
|||||||
Reference in New Issue
Block a user