1
0
mirror of https://github.com/tw93/Mole.git synced 2026-03-22 20:15:07 +00:00

Harden test mode against auth and uninstall side effects

This commit is contained in:
Tw93
2026-03-15 12:19:50 +08:00
parent 7a0b4cf07e
commit 30c1a95731
4 changed files with 101 additions and 34 deletions

View File

@@ -108,6 +108,11 @@ request_sudo_access() {
return 0 return 0
fi fi
# Tests must never trigger real password or Touch ID prompts.
if [[ "${MOLE_TEST_MODE:-0}" == "1" || "${MOLE_TEST_NO_AUTH:-0}" == "1" ]]; then
return 1
fi
# Detect if running in TTY environment # Detect if running in TTY environment
local tty_path="/dev/tty" local tty_path="/dev/tty"
local is_gui_mode=false local is_gui_mode=false
@@ -299,6 +304,11 @@ ensure_sudo_session() {
return 0 return 0
fi fi
if [[ "${MOLE_TEST_MODE:-0}" == "1" || "${MOLE_TEST_NO_AUTH:-0}" == "1" ]]; then
MOLE_SUDO_ESTABLISHED="false"
return 1
fi
# Stop old keepalive if exists # Stop old keepalive if exists
if [[ -n "$MOLE_SUDO_KEEPALIVE_PID" ]]; then if [[ -n "$MOLE_SUDO_KEEPALIVE_PID" ]]; then
_stop_sudo_keepalive "$MOLE_SUDO_KEEPALIVE_PID" _stop_sudo_keepalive "$MOLE_SUDO_KEEPALIVE_PID"

86
mole
View File

@@ -494,6 +494,10 @@ update_mole() {
# Remove flow (Homebrew + manual + config/cache). # Remove flow (Homebrew + manual + config/cache).
remove_mole() { remove_mole() {
local dry_run_mode="${1:-false}" local dry_run_mode="${1:-false}"
local test_mode=false
if [[ "${MOLE_TEST_MODE:-0}" == "1" ]]; then
test_mode=true
fi
if [[ -t 1 ]]; then if [[ -t 1 ]]; then
start_inline_spinner "Detecting Mole installations..." start_inline_spinner "Detecting Mole installations..."
@@ -507,37 +511,47 @@ remove_mole() {
local -a manual_installs=() local -a manual_installs=()
local -a alias_installs=() local -a alias_installs=()
if command -v brew > /dev/null 2>&1; then if [[ "$test_mode" != "true" ]]; then
brew_cmd="brew" if command -v brew > /dev/null 2>&1; then
elif [[ -x "/opt/homebrew/bin/brew" ]]; then brew_cmd="brew"
brew_cmd="/opt/homebrew/bin/brew" elif [[ -x "/opt/homebrew/bin/brew" ]]; then
elif [[ -x "/usr/local/bin/brew" ]]; then brew_cmd="/opt/homebrew/bin/brew"
brew_cmd="/usr/local/bin/brew" elif [[ -x "/usr/local/bin/brew" ]]; then
fi brew_cmd="/usr/local/bin/brew"
if [[ -n "$brew_cmd" ]]; then
if "$brew_cmd" list mole > /dev/null 2>&1; then
brew_has_mole="true"
fi fi
fi
if [[ "$brew_has_mole" == "true" ]] || is_homebrew_install; then if [[ -n "$brew_cmd" ]]; then
is_homebrew=true if "$brew_cmd" list mole > /dev/null 2>&1; then
brew_has_mole="true"
fi
fi
if [[ "$brew_has_mole" == "true" ]] || is_homebrew_install; then
is_homebrew=true
fi
fi fi
local found_mole local found_mole
found_mole=$(command -v mole 2> /dev/null || true) found_mole=""
if [[ -n "$found_mole" && -f "$found_mole" ]]; then if [[ "$test_mode" != "true" ]]; then
if [[ ! -L "$found_mole" ]] || ! readlink "$found_mole" | grep -q "Cellar/mole"; then found_mole=$(command -v mole 2> /dev/null || true)
manual_installs+=("$found_mole") if [[ -n "$found_mole" && -f "$found_mole" ]]; then
if [[ ! -L "$found_mole" ]] || ! readlink "$found_mole" | grep -q "Cellar/mole"; then
manual_installs+=("$found_mole")
fi
fi fi
fi fi
local -a fallback_paths=( local -a fallback_paths=()
"/usr/local/bin/mole" if [[ "$test_mode" == "true" ]]; then
"$HOME/.local/bin/mole" fallback_paths=("$HOME/.local/bin/mole")
"/opt/local/bin/mole" else
) fallback_paths=(
"/usr/local/bin/mole"
"$HOME/.local/bin/mole"
"/opt/local/bin/mole"
)
fi
for path in "${fallback_paths[@]}"; do for path in "${fallback_paths[@]}"; do
if [[ -f "$path" && "$path" != "$found_mole" ]]; then if [[ -f "$path" && "$path" != "$found_mole" ]]; then
@@ -548,18 +562,26 @@ remove_mole() {
done done
local found_mo local found_mo
found_mo=$(command -v mo 2> /dev/null || true) found_mo=""
if [[ -n "$found_mo" && -f "$found_mo" ]]; then if [[ "$test_mode" != "true" ]]; then
if [[ ! -L "$found_mo" ]] || ! readlink "$found_mo" | grep -q "Cellar/mole"; then found_mo=$(command -v mo 2> /dev/null || true)
alias_installs+=("$found_mo") if [[ -n "$found_mo" && -f "$found_mo" ]]; then
if [[ ! -L "$found_mo" ]] || ! readlink "$found_mo" | grep -q "Cellar/mole"; then
alias_installs+=("$found_mo")
fi
fi fi
fi fi
local -a alias_fallback=( local -a alias_fallback=()
"/usr/local/bin/mo" if [[ "$test_mode" == "true" ]]; then
"$HOME/.local/bin/mo" alias_fallback=("$HOME/.local/bin/mo")
"/opt/local/bin/mo" else
) alias_fallback=(
"/usr/local/bin/mo"
"$HOME/.local/bin/mo"
"/opt/local/bin/mo"
)
fi
for alias in "${alias_fallback[@]}"; do for alias in "${alias_fallback[@]}"; do
if [[ -f "$alias" && "$alias" != "$found_mo" ]]; then if [[ -f "$alias" && "$alias" != "$found_mo" ]]; then

View File

@@ -10,6 +10,9 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$PROJECT_ROOT" cd "$PROJECT_ROOT"
# Never allow the scripted test run to trigger real sudo or Touch ID prompts.
export MOLE_TEST_NO_AUTH=1
# shellcheck source=lib/core/file_ops.sh # shellcheck source=lib/core/file_ops.sh
source "$PROJECT_ROOT/lib/core/file_ops.sh" source "$PROJECT_ROOT/lib/core/file_ops.sh"

View File

@@ -362,7 +362,7 @@ EOF
touch "$HOME/.local/bin/mo" touch "$HOME/.local/bin/mo"
mkdir -p "$HOME/.config/mole" "$HOME/.cache/mole" mkdir -p "$HOME/.config/mole" "$HOME/.cache/mole"
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="/usr/bin:/bin" bash --noprofile --norc <<'EOF' run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="/usr/bin:/bin" MOLE_TEST_MODE=1 bash --noprofile --norc <<'EOF'
set -euo pipefail set -euo pipefail
start_inline_spinner() { :; } start_inline_spinner() { :; }
stop_inline_spinner() { :; } stop_inline_spinner() { :; }
@@ -410,7 +410,7 @@ EOF
touch "$HOME/.local/bin/mo" touch "$HOME/.local/bin/mo"
mkdir -p "$HOME/.config/mole" "$HOME/.cache/mole" mkdir -p "$HOME/.config/mole" "$HOME/.cache/mole"
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="/usr/bin:/bin" bash --noprofile --norc <<'EOF' run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="/usr/bin:/bin" MOLE_TEST_MODE=1 bash --noprofile --norc <<'EOF'
set -euo pipefail set -euo pipefail
start_inline_spinner() { :; } start_inline_spinner() { :; }
stop_inline_spinner() { :; } stop_inline_spinner() { :; }
@@ -425,3 +425,35 @@ EOF
[ -d "$HOME/.config/mole" ] [ -d "$HOME/.config/mole" ]
[ -d "$HOME/.cache/mole" ] [ -d "$HOME/.cache/mole" ]
} }
@test "remove_mole test mode ignores PATH installs outside test HOME" {
mkdir -p "$HOME/.local/bin" "$HOME/.config/mole" "$HOME/.cache/mole"
touch "$HOME/.local/bin/mole"
touch "$HOME/.local/bin/mo"
fake_global_bin="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-remove-path.XXXXXX")"
touch "$fake_global_bin/mole"
touch "$fake_global_bin/mo"
cat > "$fake_global_bin/brew" <<'EOF'
#!/bin/bash
exit 0
EOF
chmod +x "$fake_global_bin/brew"
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="$fake_global_bin:/usr/bin:/bin" MOLE_TEST_MODE=1 bash --noprofile --norc <<'EOF'
set -euo pipefail
start_inline_spinner() { :; }
stop_inline_spinner() { :; }
export -f start_inline_spinner stop_inline_spinner
printf '\n' | "$PROJECT_ROOT/mole" remove --dry-run
EOF
rm -rf "$fake_global_bin"
[ "$status" -eq 0 ]
[[ "$output" == *"$HOME/.local/bin/mole"* ]]
[[ "$output" == *"$HOME/.local/bin/mo"* ]]
[[ "$output" != *"$fake_global_bin/mole"* ]]
[[ "$output" != *"$fake_global_bin/mo"* ]]
[[ "$output" != *"brew uninstall --force mole"* ]]
}