diff --git a/bin/optimize.sh b/bin/optimize.sh index 58c2853..583561c 100755 --- a/bin/optimize.sh +++ b/bin/optimize.sh @@ -14,13 +14,13 @@ source "$SCRIPT_DIR/lib/check/health_json.sh" # Load check modules source "$SCRIPT_DIR/lib/check/all.sh" +source "$SCRIPT_DIR/lib/manage/whitelist.sh" # Colors and icons from common.sh print_header() { printf '\n' echo -e "${PURPLE_BOLD}Optimize and Check${NC}" - echo "" } # System check functions (real-time display) @@ -123,7 +123,6 @@ show_system_health() { # Compact one-line format with icon printf "${ICON_ADMIN} System %.0f/%.0f GB RAM | %.0f/%.0f GB Disk | Uptime %.0fd\n" \ "$mem_used" "$mem_total" "$disk_used" "$disk_total" "$uptime" - echo "" } parse_optimizations() { @@ -233,10 +232,14 @@ declare -a SECURITY_FIXES=() collect_security_fix_actions() { SECURITY_FIXES=() if [[ "${FIREWALL_DISABLED:-}" == "true" ]]; then - SECURITY_FIXES+=("firewall|Enable macOS firewall") + if ! is_whitelisted "firewall"; then + SECURITY_FIXES+=("firewall|Enable macOS firewall") + fi fi if [[ "${GATEKEEPER_DISABLED:-}" == "true" ]]; then - SECURITY_FIXES+=("gatekeeper|Enable Gatekeeper (App download protection)") + if ! is_whitelisted "gatekeeper"; then + SECURITY_FIXES+=("gatekeeper|Enable Gatekeeper (App download protection)") + fi fi ((${#SECURITY_FIXES[@]} > 0)) @@ -325,12 +328,17 @@ cleanup_all() { } main() { + local health_json # Declare health_json at the top of main scope # Parse args for arg in "$@"; do case "$arg" in "--debug") export MO_DEBUG=1 ;; + "--whitelist") + manage_whitelist "optimize" + exit 0 + ;; esac done @@ -340,8 +348,8 @@ main() { if [[ -t 1 ]]; then clear fi - print_header - + print_header # Outputs "Optimize and Check" + # Check dependencies if ! command -v jq > /dev/null 2>&1; then echo -e "${RED}${ICON_ERROR}${NC} Missing dependency: jq" @@ -355,28 +363,11 @@ main() { exit 1 fi - # Simple confirmation - echo -ne "${PURPLE}${ICON_ARROW}${NC} Optimization needs sudo — ${GREEN}Enter${NC} continue, ${GRAY}ESC${NC} cancel: " - - local key - if ! key=$(read_key); then - echo -e " ${GRAY}Cancelled${NC}" - exit 0 - fi - - if [[ "$key" == "ENTER" ]]; then - printf "\r\033[K" - else - echo -e " ${GRAY}Cancelled${NC}" - exit 0 - fi - - # Collect system health data after confirmation + # Collect system health data (doesn't require sudo) if [[ -t 1 ]]; then start_inline_spinner "Collecting system info..." fi - local health_json if ! health_json=$(generate_health_json 2> /dev/null); then if [[ -t 1 ]]; then stop_inline_spinner @@ -402,7 +393,40 @@ main() { fi # Show system health - show_system_health "$health_json" + show_system_health "$health_json" # Outputs "⚙ System ..." + + # Load whitelist patterns for checks + load_whitelist "optimize" + + # Display active whitelist patterns + if [[ ${#CURRENT_WHITELIST_PATTERNS[@]} -gt 0 ]]; then + local patterns_list=$( + local IFS=', ' + echo "${CURRENT_WHITELIST_PATTERNS[*]}" + ) + echo -e "${ICON_ADMIN} Active Whitelist: ${patterns_list}" + fi + echo "" # Empty line before sudo prompt + + # Simple confirmation + echo -ne "${PURPLE}${ICON_ARROW}${NC} Optimization needs sudo — ${GREEN}Enter${NC} continue, ${GRAY}ESC${NC} cancel: " + + local key + if ! key=$(read_key); then + echo -e " ${GRAY}Cancelled${NC}" + exit 0 + fi + + if [[ "$key" == "ENTER" ]]; then + printf "\r\033[K" + else + echo -e " ${GRAY}Cancelled${NC}" + exit 0 + fi + + if [[ -t 1 ]]; then + stop_inline_spinner + fi # Parse and display optimizations local -a safe_items=() diff --git a/lib/check/all.sh b/lib/check/all.sh index b52e25e..9cd4cbd 100644 --- a/lib/check/all.sh +++ b/lib/check/all.sh @@ -30,6 +30,8 @@ list_login_items() { # ============================================================================ check_touchid_sudo() { + # Check whitelist + if command -v is_whitelisted >/dev/null && is_whitelisted "check_touchid"; then return; fi # Check if Touch ID is configured for sudo local pam_file="/etc/pam.d/sudo" if [[ -f "$pam_file" ]] && grep -q "pam_tid.so" "$pam_file" 2> /dev/null; then @@ -53,6 +55,8 @@ check_touchid_sudo() { } check_rosetta() { + # Check whitelist + if command -v is_whitelisted >/dev/null && is_whitelisted "check_rosetta"; then return; fi # Check Rosetta 2 (for Apple Silicon Macs) if [[ "$(uname -m)" == "arm64" ]]; then if [[ -f "/Library/Apple/usr/share/rosetta/rosetta" ]]; then @@ -65,6 +69,8 @@ check_rosetta() { } check_git_config() { + # Check whitelist + if command -v is_whitelisted >/dev/null && is_whitelisted "check_git_config"; then return; fi # Check basic Git configuration if command -v git > /dev/null 2>&1; then local git_name=$(git config --global user.name 2> /dev/null || echo "") @@ -89,6 +95,8 @@ check_all_config() { # ============================================================================ check_filevault() { + # Check whitelist + if command -v is_whitelisted >/dev/null && is_whitelisted "check_filevault"; then return; fi # Check FileVault encryption status if command -v fdesetup > /dev/null 2>&1; then local fv_status=$(fdesetup status 2> /dev/null || echo "") @@ -102,6 +110,8 @@ check_filevault() { } check_firewall() { + # Check whitelist + if command -v is_whitelisted >/dev/null && is_whitelisted "firewall"; then return; fi # Check firewall status unset FIREWALL_DISABLED local firewall_status=$(defaults read /Library/Preferences/com.apple.alf globalstate 2> /dev/null || echo "0") @@ -116,6 +126,8 @@ check_firewall() { } check_gatekeeper() { + # Check whitelist + if command -v is_whitelisted >/dev/null && is_whitelisted "gatekeeper"; then return; fi # Check Gatekeeper status if command -v spctl > /dev/null 2>&1; then local gk_status=$(spctl --status 2> /dev/null || echo "") @@ -132,6 +144,8 @@ check_gatekeeper() { } check_sip() { + # Check whitelist + if command -v is_whitelisted >/dev/null && is_whitelisted "check_sip"; then return; fi # Check System Integrity Protection if command -v csrutil > /dev/null 2>&1; then local sip_status=$(csrutil status 2> /dev/null || echo "") @@ -194,6 +208,8 @@ is_cache_valid() { } check_homebrew_updates() { + # Check whitelist + if command -v is_whitelisted >/dev/null && is_whitelisted "check_brew_updates"; then return; fi if ! command -v brew > /dev/null 2>&1; then return fi @@ -320,6 +336,8 @@ check_appstore_updates() { } check_macos_update() { + # Check whitelist + if command -v is_whitelisted >/dev/null && is_whitelisted "check_macos_updates"; then return; fi local spinner_started=false if [[ -t 1 ]]; then printf " Checking macOS updates...\r" @@ -523,6 +541,8 @@ check_memory_usage() { } check_login_items() { + # Check whitelist + if command -v is_whitelisted >/dev/null && is_whitelisted "check_login_items"; then return; fi local login_items_count=0 local -a login_items_list=() @@ -640,6 +660,8 @@ check_swap_usage() { } check_brew_health() { + # Check whitelist + if command -v is_whitelisted >/dev/null && is_whitelisted "check_brew_health"; then return; fi # Check Homebrew doctor if command -v brew > /dev/null 2>&1; then # Show spinner while running brew doctor diff --git a/lib/core/base.sh b/lib/core/base.sh index 7216b48..699760a 100644 --- a/lib/core/base.sh +++ b/lib/core/base.sh @@ -68,6 +68,13 @@ declare -a DEFAULT_WHITELIST_PATTERNS=( "$FINDER_METADATA_SENTINEL" ) +declare -a DEFAULT_OPTIMIZE_WHITELIST_PATTERNS=( + "check_brew_updates" + "check_brew_health" + "check_touchid" + "check_git_config" +) + # ============================================================================ # BSD Stat Compatibility # ============================================================================ diff --git a/lib/manage/whitelist.sh b/lib/manage/whitelist.sh index ff8de16..1d49d59 100755 --- a/lib/manage/whitelist.sh +++ b/lib/manage/whitelist.sh @@ -9,8 +9,9 @@ _MOLE_MANAGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$_MOLE_MANAGE_DIR/../core/common.sh" source "$_MOLE_MANAGE_DIR/../ui/menu_simple.sh" -# Config file path -WHITELIST_CONFIG="$HOME/.config/mole/whitelist" +# Config file paths +readonly WHITELIST_CONFIG_CLEAN="$HOME/.config/mole/whitelist" +readonly WHITELIST_CONFIG_OPTIMIZE="$HOME/.config/mole/whitelist_checks" # Default whitelist patterns defined in lib/core/common.sh: # - DEFAULT_WHITELIST_PATTERNS @@ -18,15 +19,25 @@ WHITELIST_CONFIG="$HOME/.config/mole/whitelist" # Save whitelist patterns to config save_whitelist_patterns() { + local mode="$1" + shift local -a patterns patterns=("$@") - mkdir -p "$(dirname "$WHITELIST_CONFIG")" + + local config_file + local header_text - cat > "$WHITELIST_CONFIG" << 'EOF' -# Mole Whitelist - Protected paths won't be deleted -# Default protections: Playwright browsers, HuggingFace models, Maven repo, Ollama models, Surge Mac, R renv, Finder metadata -# Add one pattern per line to keep items safe. -EOF + if [[ "$mode" == "optimize" ]]; then + config_file="$WHITELIST_CONFIG_OPTIMIZE" + header_text="# Mole Optimization Whitelist - These checks will be skipped during optimization" + else + config_file="$WHITELIST_CONFIG_CLEAN" + header_text="# Mole Whitelist - Protected paths won't be deleted\n# Default protections: Playwright browsers, HuggingFace models, Maven repo, Ollama models, Surge Mac, R renv, Finder metadata\n# Add one pattern per line to keep items safe." + fi + + mkdir -p "$(dirname "$config_file")" + + echo -e "$header_text" > "$config_file" if [[ ${#patterns[@]} -gt 0 ]]; then local -a unique_patterns=() @@ -45,9 +56,9 @@ EOF done if [[ ${#unique_patterns[@]} -gt 0 ]]; then - printf '\n' >> "$WHITELIST_CONFIG" + printf '\n' >> "$config_file" for pattern in "${unique_patterns[@]}"; do - echo "$pattern" >> "$WHITELIST_CONFIG" + echo "$pattern" >> "$config_file" done fi fi @@ -128,6 +139,24 @@ EOF echo "Finder metadata (.DS_Store)|$FINDER_METADATA_SENTINEL|system_cache" } +# Get all optimize items with their patterns +get_optimize_whitelist_items() { + # Format: "display_name|pattern|category" + cat << 'EOF' +macOS Firewall check|firewall|security_check +Gatekeeper check|gatekeeper|security_check +Homebrew updates check|check_brew_updates|update_check +macOS system updates check|check_macos_updates|update_check +Homebrew health check (doctor)|check_brew_health|health_check +SIP status check|check_sip|security_check +FileVault status check|check_filevault|security_check +TouchID sudo check|check_touchid|config_check +Rosetta 2 check|check_rosetta|config_check +Git configuration check|check_git_config|config_check +Login items check|check_login_items|config_check +EOF +} + patterns_equivalent() { local first="${1/#~/$HOME}" local second="${2/#~/$HOME}" @@ -138,9 +167,17 @@ patterns_equivalent() { } load_whitelist() { + local mode="${1:-clean}" local -a patterns=() + local config_file - if [[ -f "$WHITELIST_CONFIG" ]]; then + if [[ "$mode" == "optimize" ]]; then + config_file="$WHITELIST_CONFIG_OPTIMIZE" + else + config_file="$WHITELIST_CONFIG_CLEAN" + fi + + if [[ -f "$config_file" ]]; then while IFS= read -r line; do # shellcheck disable=SC2295 line="${line#"${line%%[![:space:]]*}"}" @@ -148,9 +185,13 @@ load_whitelist() { line="${line%"${line##*[![:space:]]}"}" [[ -z "$line" || "$line" =~ ^# ]] && continue patterns+=("$line") - done < "$WHITELIST_CONFIG" + done < "$config_file" else - patterns=("${DEFAULT_WHITELIST_PATTERNS[@]}") + if [[ "$mode" == "clean" ]]; then + patterns=("${DEFAULT_WHITELIST_PATTERNS[@]}") + elif [[ "$mode" == "optimize" ]]; then + patterns=("${DEFAULT_OPTIMIZE_WHITELIST_PATTERNS[@]}") + fi fi if [[ ${#patterns[@]} -gt 0 ]]; then @@ -193,12 +234,15 @@ is_whitelisted() { } manage_whitelist() { - manage_whitelist_categories + local mode="${1:-clean}" + manage_whitelist_categories "$mode" } manage_whitelist_categories() { + local mode="$1" + # Load currently enabled patterns from both sources - load_whitelist + load_whitelist "$mode" # Build cache items list local -a cache_items=() @@ -206,6 +250,21 @@ manage_whitelist_categories() { local -a menu_options=() local index=0 + # Choose source based on mode + local items_source + local menu_title + local active_config_file + + if [[ "$mode" == "optimize" ]]; then + items_source=$(get_optimize_whitelist_items) + menu_title="Whitelist Manager – Select system checks to ignore" + active_config_file="$WHITELIST_CONFIG_OPTIMIZE" + else + items_source=$(get_all_cache_items) + menu_title="Whitelist Manager – Select caches to protect" + active_config_file="$WHITELIST_CONFIG_CLEAN" + fi + while IFS='|' read -r display_name pattern _; do # Expand $HOME in pattern pattern="${pattern/\$HOME/$HOME}" @@ -215,7 +274,7 @@ manage_whitelist_categories() { menu_options+=("$display_name") ((index++)) || true - done < <(get_all_cache_items) + done <<< "$items_source" # Identify custom patterns (not in predefined list) local -a custom_patterns=() @@ -280,7 +339,7 @@ manage_whitelist_categories() { fi MOLE_SELECTION_RESULT="" - paginated_multi_select "Whitelist Manager – Select caches to protect" "${menu_options[@]}" + paginated_multi_select "$menu_title" "${menu_options[@]}" unset MOLE_PRESELECTED_INDICES local exit_code=$? @@ -318,9 +377,9 @@ manage_whitelist_categories() { # Save to whitelist config (bash 3.2 + set -u safe) if [[ ${#all_patterns[@]} -gt 0 ]]; then - save_whitelist_patterns "${all_patterns[@]}" + save_whitelist_patterns "$mode" "${all_patterns[@]}" else - save_whitelist_patterns + save_whitelist_patterns "$mode" fi local total_protected=$((${#selected_patterns[@]} + ${#custom_patterns[@]})) @@ -331,7 +390,7 @@ manage_whitelist_categories() { else summary_lines+=("Protected ${total_protected} cache(s)") fi - summary_lines+=("Saved to ${WHITELIST_CONFIG}") + summary_lines+=("Saved to ${active_config_file}") print_summary_block "${summary_lines[@]}" printf '\n'