diff --git a/SECURITY_AUDIT.md b/SECURITY_AUDIT.md index 4aa089c..7bec259 100644 --- a/SECURITY_AUDIT.md +++ b/SECURITY_AUDIT.md @@ -93,9 +93,14 @@ Even with `sudo`, these paths are **unconditionally blocked**: /bin, /sbin, /usr # Core binaries /etc, /var # System configuration /Library/Extensions # Kernel extensions +/private # System-private directories ``` -**Exception:** `/System/Library/Caches/com.apple.coresymbolicationd/data` (safe, rebuildable cache). +**Exceptions:** + +- `/System/Library/Caches/com.apple.coresymbolicationd/data` (safe, rebuildable cache) +- `/private/tmp`, `/private/var/tmp`, `/private/var/log`, `/private/var/folders` +- `/private/var/db/diagnostics`, `/private/var/db/DiagnosticPipeline`, `/private/var/db/powerlog`, `/private/var/db/reportmemoryexception` **Code:** `lib/core/file_ops.sh:60-78` @@ -161,6 +166,7 @@ For user-selected app removal: - **Safety Limit:** 3-char minimum (prevents "Go" matching "Google") - **Disabled:** Fuzzy matching and wildcard expansion for short names. - **User Confirmation:** Required before deletion. +- **Receipt Scans:** BOM-derived files are limited to safe system prefixes and filtered by `should_protect_path()`. **Code:** `lib/clean/apps.sh:uninstall_app()` diff --git a/lib/core/app_protection.sh b/lib/core/app_protection.sh index 4770bad..98b753a 100755 --- a/lib/core/app_protection.sh +++ b/lib/core/app_protection.sh @@ -13,7 +13,7 @@ _MOLE_CORE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" [[ -z "${MOLE_BASE_LOADED:-}" ]] && source "$_MOLE_CORE_DIR/base.sh" # Declare WHITELIST_PATTERNS if not already set (used by is_path_whitelisted) -if ! declare -p WHITELIST_PATTERNS &>/dev/null; then +if ! declare -p WHITELIST_PATTERNS &> /dev/null; then declare -a WHITELIST_PATTERNS=() fi diff --git a/lib/uninstall/batch.sh b/lib/uninstall/batch.sh index 8ee9b69..843af7f 100755 --- a/lib/uninstall/batch.sh +++ b/lib/uninstall/batch.sh @@ -363,7 +363,7 @@ batch_uninstall_applications() { # Perform uninstallations with per-app progress feedback local success_count=0 failed_count=0 - local brew_apps_removed=0 # Track successful brew uninstalls for autoremove tip + local brew_apps_removed=0 # Track successful brew uninstalls for autoremove tip local -a failed_items=() local -a success_items=() local current_index=0 @@ -573,7 +573,7 @@ batch_uninstall_applications() { # Auto-run brew autoremove if Homebrew casks were uninstalled if [[ $brew_apps_removed -gt 0 ]]; then local autoremove_output removed_count - autoremove_output=$(HOMEBREW_NO_ENV_HINTS=1 brew autoremove 2>/dev/null) || true + autoremove_output=$(HOMEBREW_NO_ENV_HINTS=1 brew autoremove 2> /dev/null) || true removed_count=$(printf '%s\n' "$autoremove_output" | grep -c "^Uninstalling" || true) removed_count=${removed_count:-0} if [[ $removed_count -gt 0 ]]; then diff --git a/lib/uninstall/brew.sh b/lib/uninstall/brew.sh index a749e6a..3811079 100644 --- a/lib/uninstall/brew.sh +++ b/lib/uninstall/brew.sh @@ -18,13 +18,13 @@ resolve_path() { [[ -e "$p" ]] || return 1 # macOS 12.3+ and Linux have realpath - if realpath "$p" 2>/dev/null; then + if realpath "$p" 2> /dev/null; then return 0 fi # Fallback: use cd -P to resolve directory, then append basename local dir base - dir=$(cd -P "$(dirname "$p")" 2>/dev/null && pwd) || return 1 + dir=$(cd -P "$(dirname "$p")" 2> /dev/null && pwd) || return 1 base=$(basename "$p") echo "$dir/$base" } @@ -87,9 +87,9 @@ _detect_cask_via_caskroom_search() { [[ -d "$room" ]] || continue while IFS= read -r match; do [[ -n "$match" ]] || continue - token=$(_extract_cask_token_from_path "$match" 2>/dev/null) || continue + token=$(_extract_cask_token_from_path "$match" 2> /dev/null) || continue [[ -n "$token" ]] && tokens+=("$token") - done < <(find "$room" -maxdepth 3 -name "$app_bundle_name" 2>/dev/null) + done < <(find "$room" -maxdepth 3 -name "$app_bundle_name" 2> /dev/null) done # Need at least one token @@ -101,7 +101,7 @@ _detect_cask_via_caskroom_search() { # Only succeed if exactly one unique token found and it's installed if ((${#uniq[@]} == 1)) && [[ -n "${uniq[0]}" ]]; then - HOMEBREW_NO_ENV_HINTS=1 brew list --cask 2>/dev/null | grep -qxF "${uniq[0]}" || return 1 + HOMEBREW_NO_ENV_HINTS=1 brew list --cask 2> /dev/null | grep -qxF "${uniq[0]}" || return 1 echo "${uniq[0]}" return 0 fi @@ -115,7 +115,7 @@ _detect_cask_via_symlink_check() { [[ -L "$app_path" ]] || return 1 local target - target=$(readlink "$app_path" 2>/dev/null) || return 1 + target=$(readlink "$app_path" 2> /dev/null) || return 1 _extract_cask_token_from_path "$target" } @@ -127,10 +127,10 @@ _detect_cask_via_brew_list() { app_name_lower=$(echo "${app_bundle_name%.app}" | LC_ALL=C tr '[:upper:]' '[:lower:]') local cask_name - cask_name=$(HOMEBREW_NO_ENV_HINTS=1 brew list --cask 2>/dev/null | grep -Fix "$app_name_lower") || return 1 + cask_name=$(HOMEBREW_NO_ENV_HINTS=1 brew list --cask 2> /dev/null | grep -Fix "$app_name_lower") || return 1 # Verify this cask actually owns this app path - HOMEBREW_NO_ENV_HINTS=1 brew info --cask "$cask_name" 2>/dev/null | grep -qF "$app_path" || return 1 + HOMEBREW_NO_ENV_HINTS=1 brew info --cask "$cask_name" 2> /dev/null | grep -qF "$app_path" || return 1 echo "$cask_name" } @@ -185,7 +185,7 @@ brew_uninstall_cask() { # Verify removal local cask_gone=true app_gone=true - HOMEBREW_NO_ENV_HINTS=1 brew list --cask 2>/dev/null | grep -qxF "$cask_name" && cask_gone=false + HOMEBREW_NO_ENV_HINTS=1 brew list --cask 2> /dev/null | grep -qxF "$cask_name" && cask_gone=false [[ -n "$app_path" && -e "$app_path" ]] && app_gone=false # Success: uninstall worked and both are gone, or already uninstalled