1
0
mirror of https://github.com/tw93/Mole.git synced 2026-03-24 11:10:08 +00:00

fix(uninstall): pre-auth brew cask removals

This commit is contained in:
Tw93
2026-03-24 10:29:52 +08:00
parent 22379b78c8
commit a38b24f740
2 changed files with 64 additions and 35 deletions

View File

@@ -285,6 +285,7 @@ batch_uninstall_applications() {
# Pre-scan: running apps, sudo needs, size. # Pre-scan: running apps, sudo needs, size.
local -a running_apps=() local -a running_apps=()
local -a sudo_apps=() local -a sudo_apps=()
local -a brew_cask_apps=()
local total_estimated_size=0 local total_estimated_size=0
local -a app_details=() local -a app_details=()
@@ -322,6 +323,10 @@ batch_uninstall_applications() {
fi fi
fi fi
if [[ "$is_brew_cask" == "true" ]]; then
brew_cask_apps+=("$app_name")
fi
# Check if sudo is needed # Check if sudo is needed
local needs_sudo=false local needs_sudo=false
local app_owner=$(get_file_owner "$app_path") local app_owner=$(get_file_owner "$app_path")
@@ -383,10 +388,7 @@ batch_uninstall_applications() {
# Warn if brew cask apps are present. # Warn if brew cask apps are present.
local has_brew_cask=false local has_brew_cask=false
for detail in "${app_details[@]}"; do [[ ${#brew_cask_apps[@]} -gt 0 ]] && has_brew_cask=true
IFS='|' read -r _ _ _ _ _ _ _ _ is_brew_cask_flag _ <<< "$detail"
[[ "$is_brew_cask_flag" == "true" ]] && has_brew_cask=true
done
if [[ "$has_brew_cask" == "true" ]]; then if [[ "$has_brew_cask" == "true" ]]; then
echo -e "${GRAY}${ICON_WARNING} Homebrew apps will be fully cleaned, --zap removes configs and data${NC}" echo -e "${GRAY}${ICON_WARNING} Homebrew apps will be fully cleaned, --zap removes configs and data${NC}"
@@ -467,11 +469,19 @@ batch_uninstall_applications() {
# that user explicitly chose to uninstall. System-critical components remain protected. # that user explicitly chose to uninstall. System-critical components remain protected.
export MOLE_UNINSTALL_MODE=1 export MOLE_UNINSTALL_MODE=1
# Request sudo if needed for non-Homebrew removal operations. # Establish sudo once before uninstalling apps that need admin access.
# Note: Homebrew resets sudo timestamp at process startup, so pre-auth would # Homebrew cask removal can prompt via sudo during uninstall hooks, which
# cause duplicate password prompts in cask-only flows. # does not work reliably under Mole's timed non-interactive execution path.
if [[ ${#sudo_apps[@]} -gt 0 && "${MOLE_DRY_RUN:-0}" != "1" ]]; then if [[ "${MOLE_DRY_RUN:-0}" != "1" ]] &&
if ! ensure_sudo_session "Admin required for system apps: ${sudo_apps[*]}"; then { [[ ${#sudo_apps[@]} -gt 0 ]] || [[ ${#brew_cask_apps[@]} -gt 0 ]]; }; then
local admin_prompt="Admin required to uninstall selected apps"
if [[ ${#sudo_apps[@]} -gt 0 && ${#brew_cask_apps[@]} -eq 0 ]]; then
admin_prompt="Admin required for system apps: ${sudo_apps[*]}"
elif [[ ${#brew_cask_apps[@]} -gt 0 && ${#sudo_apps[@]} -eq 0 ]]; then
admin_prompt="Admin required for Homebrew casks: ${brew_cask_apps[*]}"
fi
if ! ensure_sudo_session "$admin_prompt"; then
echo "" echo ""
log_error "Admin access denied" log_error "Admin access denied"
_restore_uninstall_traps _restore_uninstall_traps

View File

@@ -101,6 +101,10 @@ remove_apps_from_dock() { :; }
force_kill_app() { return 0; } force_kill_app() { return 0; }
run_with_timeout() { shift; "$@"; } run_with_timeout() { shift; "$@"; }
export -f run_with_timeout export -f run_with_timeout
ensure_sudo_session() {
echo "ENSURE_SUDO:$*" >> "$HOME/brew_calls.log"
return 0
}
# Mock brew to track calls # Mock brew to track calls
brew() { brew() {
@@ -121,13 +125,14 @@ total_size_cleaned=0
# Simulate 'Enter' for confirmation # Simulate 'Enter' for confirmation
printf '\n' | batch_uninstall_applications > /dev/null 2>&1 printf '\n' | batch_uninstall_applications > /dev/null 2>&1
grep -q "ENSURE_SUDO:Admin required for Homebrew casks: BrewApp" "$HOME/brew_calls.log"
grep -q "uninstall --cask --zap brew-app-cask" "$HOME/brew_calls.log" grep -q "uninstall --cask --zap brew-app-cask" "$HOME/brew_calls.log"
EOF EOF
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
} }
@test "batch_uninstall_applications does not pre-auth sudo for brew-only casks" { @test "batch_uninstall_applications pre-auths sudo for brew-only casks" {
local app_bundle="$HOME/Applications/BrewPreAuth.app" local app_bundle="$HOME/Applications/BrewPreAuth.app"
mkdir -p "$app_bundle" mkdir -p "$app_bundle"
@@ -149,8 +154,8 @@ run_with_timeout() { shift; "$@"; }
export -f run_with_timeout export -f run_with_timeout
ensure_sudo_session() { ensure_sudo_session() {
echo "UNEXPECTED_ENSURE_SUDO:$*" >> "$HOME/order.log" echo "ENSURE_SUDO:$*" >> "$HOME/order.log"
return 1 return 0
} }
brew() { brew() {
@@ -169,8 +174,9 @@ total_size_cleaned=0
printf '\n' | batch_uninstall_applications > /dev/null 2>&1 printf '\n' | batch_uninstall_applications > /dev/null 2>&1
grep -q "ENSURE_SUDO:Admin required for Homebrew casks: BrewPreAuth" "$HOME/order.log"
grep -q "BREW_CALL:uninstall --cask --zap brew-preauth-cask" "$HOME/order.log" grep -q "BREW_CALL:uninstall --cask --zap brew-preauth-cask" "$HOME/order.log"
! grep -q "UNEXPECTED_ENSURE_SUDO:" "$HOME/order.log" [[ "$(sed -n '1p' "$HOME/order.log")" == "ENSURE_SUDO:Admin required for Homebrew casks: BrewPreAuth" ]]
EOF EOF
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
@@ -196,6 +202,7 @@ print_summary_block() { :; }
force_kill_app() { return 0; } force_kill_app() { return 0; }
remove_apps_from_dock() { :; } remove_apps_from_dock() { :; }
refresh_launch_services_after_uninstall() { echo "LS_REFRESH"; } refresh_launch_services_after_uninstall() { echo "LS_REFRESH"; }
ensure_sudo_session() { return 0; }
get_brew_cask_name() { echo "brew-timeout-cask"; return 0; } get_brew_cask_name() { echo "brew-timeout-cask"; return 0; }
brew_uninstall_cask() { return 0; } brew_uninstall_cask() { return 0; }
@@ -257,6 +264,7 @@ has_sensitive_data() { return 1; }
decode_file_list() { return 0; } decode_file_list() { return 0; }
remove_file_list() { :; } remove_file_list() { :; }
run_with_timeout() { shift; "$@"; } run_with_timeout() { shift; "$@"; }
ensure_sudo_session() { return 0; }
safe_remove() { safe_remove() {
echo "SAFE_REMOVE:$1" >> "$HOME/remove.log" echo "SAFE_REMOVE:$1" >> "$HOME/remove.log"
@@ -315,6 +323,7 @@ has_sensitive_data() { return 1; }
decode_file_list() { return 0; } decode_file_list() { return 0; }
remove_file_list() { :; } remove_file_list() { :; }
run_with_timeout() { shift; "$@"; } run_with_timeout() { shift; "$@"; }
ensure_sudo_session() { return 0; }
safe_remove() { safe_remove() {
echo "SAFE_REMOVE:$1" >> "$HOME/remove.log" echo "SAFE_REMOVE:$1" >> "$HOME/remove.log"
@@ -344,37 +353,47 @@ EOF
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
} }
@test "brew_uninstall_cask does not trigger extra sudo pre-auth" { @test "batch_uninstall_applications skips brew sudo pre-auth in dry-run mode" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc << 'EOF' run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc << 'EOF'
set -euo pipefail set -euo pipefail
source "$PROJECT_ROOT/lib/core/common.sh" source "$PROJECT_ROOT/lib/core/common.sh"
source "$PROJECT_ROOT/lib/uninstall/brew.sh" source "$PROJECT_ROOT/lib/uninstall/batch.sh"
debug_log() { :; }
get_path_size_kb() { echo "0"; }
run_with_timeout() { local _timeout="$1"; shift; "$@"; }
sudo() {
echo "UNEXPECTED_SUDO_CALL:$*"
return 1
}
brew() { brew() {
if [[ "${1:-}" == "uninstall" ]]; then echo "BREW_CALL:$*" >> "$HOME/dry_run.log"
return 0 return 0
fi
if [[ "${1:-}" == "list" && "${2:-}" == "--cask" ]]; then
return 0
fi
return 0
} }
export -f sudo brew export -f brew
brew_uninstall_cask "mock-cask" start_inline_spinner() { :; }
echo "DONE" stop_inline_spinner() { :; }
get_file_owner() { whoami; }
get_path_size_kb() { echo "100"; }
bytes_to_human() { echo "$1"; }
drain_pending_input() { :; }
print_summary_block() { :; }
remove_apps_from_dock() { :; }
force_kill_app() { return 0; }
ensure_sudo_session() {
echo "UNEXPECTED_ENSURE_SUDO:$*" >> "$HOME/dry_run.log"
return 1
}
run_with_timeout() { shift; "$@"; }
export -f run_with_timeout
get_brew_cask_name() { echo "brew-dry-run-cask"; return 0; }
export MOLE_DRY_RUN=1
selected_apps=("0|$HOME/Applications/BrewDryRun.app|BrewDryRun|com.example.brewdryrun|0|Never")
mkdir -p "$HOME/Applications/BrewDryRun.app"
files_cleaned=0
total_items=0
total_size_cleaned=0
printf '\n' | batch_uninstall_applications > /dev/null 2>&1
! grep -q "UNEXPECTED_ENSURE_SUDO:" "$HOME/dry_run.log" 2> /dev/null
EOF EOF
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[[ "$output" == *"DONE"* ]]
[[ "$output" != *"UNEXPECTED_SUDO_CALL:"* ]]
} }