1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-07 11:03:29 +00:00

feat: Enhance app protection with centralized critical component checks, improve UI string width calculation, refine analysis and cleaning logic, and add new tests.

This commit is contained in:
Tw93
2025-12-22 11:24:04 +08:00
parent 5c6e643b0c
commit d2dc68da90
16 changed files with 270 additions and 102 deletions

View File

@@ -484,10 +484,17 @@ clean_project_artifacts() {
local path="$1"
# Find the project root by looking for direct child of search paths
local search_roots=("$HOME/www" "$HOME/dev" "$HOME/Projects")
local search_roots=()
if [[ ${#PURGE_SEARCH_PATHS[@]} -gt 0 ]]; then
search_roots=("${PURGE_SEARCH_PATHS[@]}")
else
search_roots=("$HOME/www" "$HOME/dev" "$HOME/Projects")
fi
for root in "${search_roots[@]}"; do
if [[ "$path" == "$root/"* ]]; then
# Normalize trailing slash for consistent matching
root="${root%/}"
if [[ -n "$root" && "$path" == "$root/"* ]]; then
# Remove root prefix and get first directory component
local relative_path="${path#"$root"/}"
# Extract first directory name

View File

@@ -119,6 +119,11 @@ clean_sandboxed_app_caches() {
local containers_dir="$HOME/Library/Containers"
[[ ! -d "$containers_dir" ]] && return 0
# Enable nullglob for safe globbing; restore afterwards
local _ng_state
_ng_state=$(shopt -p nullglob || true)
shopt -s nullglob
if [[ -t 1 ]]; then
MOLE_SPINNER_PREFIX=" " start_inline_spinner "Scanning sandboxed apps..."
fi
@@ -146,6 +151,9 @@ clean_sandboxed_app_caches() {
((total_items++))
note_activity
fi
# Restore nullglob to previous state
eval "$_ng_state"
}
# Process a single container cache directory (reduces nesting)
@@ -155,14 +163,10 @@ process_container_cache() {
# Extract bundle ID and check protection status early
local bundle_id=$(basename "$container_dir")
local bundle_id_lower=$(echo "$bundle_id" | tr '[:upper:]' '[:lower:]')
# Check explicit critical system components (case-insensitive regex)
if [[ "$bundle_id_lower" =~ backgroundtaskmanagement || "$bundle_id_lower" =~ loginitems || "$bundle_id_lower" =~ systempreferences || "$bundle_id_lower" =~ systemsettings || "$bundle_id_lower" =~ settings || "$bundle_id_lower" =~ preferences || "$bundle_id_lower" =~ controlcenter || "$bundle_id_lower" =~ biometrickit || "$bundle_id_lower" =~ sfl || "$bundle_id_lower" =~ tcc ]]; then
if is_critical_system_component "$bundle_id"; then
return 0
fi
if should_protect_data "$bundle_id" || should_protect_data "$bundle_id_lower"; then
if should_protect_data "$bundle_id" || should_protect_data "$(echo "$bundle_id" | tr '[:upper:]' '[:lower:]')"; then
return 0
fi
@@ -180,10 +184,14 @@ process_container_cache() {
if [[ "$DRY_RUN" != "true" ]]; then
# Clean contents safely (rm -rf is restricted by safe_remove)
local _ng_item_state
_ng_item_state=$(shopt -p nullglob || true)
shopt -s nullglob
for item in "$cache_dir"/*; do
[[ -e "$item" ]] || continue
safe_remove "$item" true || true
done
eval "$_ng_item_state"
fi
fi
}
@@ -259,6 +267,9 @@ clean_application_support_logs() {
local found_any=false
# Clean log directories and cache patterns
local _ng_app_state
_ng_app_state=$(shopt -p nullglob || true)
shopt -s nullglob
for app_dir in ~/Library/Application\ Support/*; do
[[ -d "$app_dir" ]] || continue
@@ -276,7 +287,7 @@ clean_application_support_logs() {
continue
fi
if [[ "$app_name_lower" =~ backgroundtaskmanagement || "$app_name_lower" =~ loginitems || "$app_name_lower" =~ systempreferences || "$app_name_lower" =~ systemsettings || "$app_name_lower" =~ settings || "$app_name_lower" =~ preferences || "$app_name_lower" =~ controlcenter || "$app_name_lower" =~ biometrickit || "$app_name_lower" =~ sfl || "$app_name_lower" =~ tcc ]]; then
if is_critical_system_component "$app_name"; then
continue
fi
@@ -291,15 +302,20 @@ clean_application_support_logs() {
found_any=true
if [[ "$DRY_RUN" != "true" ]]; then
local _ng_candidate_state
_ng_candidate_state=$(shopt -p nullglob || true)
shopt -s nullglob
for item in "$candidate"/*; do
[[ -e "$item" ]] || continue
safe_remove "$item" true > /dev/null 2>&1 || true
done
eval "$_ng_candidate_state"
fi
fi
fi
done
done
eval "$_ng_app_state"
# Clean Group Containers logs
local known_group_containers=(

View File

@@ -435,6 +435,24 @@ readonly DATA_PROTECTED_BUNDLES=(
"org.sparkle-project.Sparkle" # Sparkle (update framework)
)
# Centralized check for critical system components (case-insensitive)
is_critical_system_component() {
local token="$1"
[[ -z "$token" ]] && return 1
local lower
lower=$(echo "$token" | tr '[:upper:]' '[:lower:]')
case "$lower" in
*backgroundtaskmanagement* | *loginitems* | *systempreferences* | *systemsettings* | *settings* | *preferences* | *controlcenter* | *biometrickit* | *sfl* | *tcc* )
return 0
;;
*)
return 1
;;
esac
}
# Legacy function - preserved for backward compatibility
# Use should_protect_from_uninstall() or should_protect_data() instead
readonly PRESERVED_BUNDLE_PATTERNS=("${SYSTEM_CRITICAL_BUNDLES[@]}" "${DATA_PROTECTED_BUNDLES[@]}")

View File

@@ -60,6 +60,22 @@ get_display_width() {
local padding=$((extra_bytes / 2))
width=$((char_count + padding))
# Adjust for zero-width joiners and emoji variation selectors (common in filenames/emojis)
# These characters add bytes but no visible width; subtract their count if present.
local zwj=$'\u200d' # zero-width joiner
local vs16=$'\ufe0f' # emoji variation selector
local zero_width=0
local without_zwj=${str//$zwj/}
zero_width=$((zero_width + (char_count - ${#without_zwj})))
local without_vs=${str//$vs16/}
zero_width=$((zero_width + (char_count - ${#without_vs})))
if ((zero_width > 0 && width > zero_width)); then
width=$((width - zero_width))
fi
echo "$width"
}