Skip to content

Commit 30c1a95

Browse files
committed
Harden test mode against auth and uninstall side effects
1 parent 7a0b4cf commit 30c1a95

File tree

4 files changed

+100
-33
lines changed

4 files changed

+100
-33
lines changed

lib/core/sudo.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ request_sudo_access() {
108108
return 0
109109
fi
110110

111+
# Tests must never trigger real password or Touch ID prompts.
112+
if [[ "${MOLE_TEST_MODE:-0}" == "1" || "${MOLE_TEST_NO_AUTH:-0}" == "1" ]]; then
113+
return 1
114+
fi
115+
111116
# Detect if running in TTY environment
112117
local tty_path="/dev/tty"
113118
local is_gui_mode=false
@@ -299,6 +304,11 @@ ensure_sudo_session() {
299304
return 0
300305
fi
301306

307+
if [[ "${MOLE_TEST_MODE:-0}" == "1" || "${MOLE_TEST_NO_AUTH:-0}" == "1" ]]; then
308+
MOLE_SUDO_ESTABLISHED="false"
309+
return 1
310+
fi
311+
302312
# Stop old keepalive if exists
303313
if [[ -n "$MOLE_SUDO_KEEPALIVE_PID" ]]; then
304314
_stop_sudo_keepalive "$MOLE_SUDO_KEEPALIVE_PID"

mole

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,10 @@ update_mole() {
494494
# Remove flow (Homebrew + manual + config/cache).
495495
remove_mole() {
496496
local dry_run_mode="${1:-false}"
497+
local test_mode=false
498+
if [[ "${MOLE_TEST_MODE:-0}" == "1" ]]; then
499+
test_mode=true
500+
fi
497501

498502
if [[ -t 1 ]]; then
499503
start_inline_spinner "Detecting Mole installations..."
@@ -507,37 +511,47 @@ remove_mole() {
507511
local -a manual_installs=()
508512
local -a alias_installs=()
509513

510-
if command -v brew > /dev/null 2>&1; then
511-
brew_cmd="brew"
512-
elif [[ -x "/opt/homebrew/bin/brew" ]]; then
513-
brew_cmd="/opt/homebrew/bin/brew"
514-
elif [[ -x "/usr/local/bin/brew" ]]; then
515-
brew_cmd="/usr/local/bin/brew"
516-
fi
514+
if [[ "$test_mode" != "true" ]]; then
515+
if command -v brew > /dev/null 2>&1; then
516+
brew_cmd="brew"
517+
elif [[ -x "/opt/homebrew/bin/brew" ]]; then
518+
brew_cmd="/opt/homebrew/bin/brew"
519+
elif [[ -x "/usr/local/bin/brew" ]]; then
520+
brew_cmd="/usr/local/bin/brew"
521+
fi
517522

518-
if [[ -n "$brew_cmd" ]]; then
519-
if "$brew_cmd" list mole > /dev/null 2>&1; then
520-
brew_has_mole="true"
523+
if [[ -n "$brew_cmd" ]]; then
524+
if "$brew_cmd" list mole > /dev/null 2>&1; then
525+
brew_has_mole="true"
526+
fi
521527
fi
522-
fi
523528

524-
if [[ "$brew_has_mole" == "true" ]] || is_homebrew_install; then
525-
is_homebrew=true
529+
if [[ "$brew_has_mole" == "true" ]] || is_homebrew_install; then
530+
is_homebrew=true
531+
fi
526532
fi
527533

528534
local found_mole
529-
found_mole=$(command -v mole 2> /dev/null || true)
530-
if [[ -n "$found_mole" && -f "$found_mole" ]]; then
531-
if [[ ! -L "$found_mole" ]] || ! readlink "$found_mole" | grep -q "Cellar/mole"; then
532-
manual_installs+=("$found_mole")
535+
found_mole=""
536+
if [[ "$test_mode" != "true" ]]; then
537+
found_mole=$(command -v mole 2> /dev/null || true)
538+
if [[ -n "$found_mole" && -f "$found_mole" ]]; then
539+
if [[ ! -L "$found_mole" ]] || ! readlink "$found_mole" | grep -q "Cellar/mole"; then
540+
manual_installs+=("$found_mole")
541+
fi
533542
fi
534543
fi
535544

536-
local -a fallback_paths=(
537-
"/usr/local/bin/mole"
538-
"$HOME/.local/bin/mole"
539-
"/opt/local/bin/mole"
540-
)
545+
local -a fallback_paths=()
546+
if [[ "$test_mode" == "true" ]]; then
547+
fallback_paths=("$HOME/.local/bin/mole")
548+
else
549+
fallback_paths=(
550+
"/usr/local/bin/mole"
551+
"$HOME/.local/bin/mole"
552+
"/opt/local/bin/mole"
553+
)
554+
fi
541555

542556
for path in "${fallback_paths[@]}"; do
543557
if [[ -f "$path" && "$path" != "$found_mole" ]]; then
@@ -548,18 +562,26 @@ remove_mole() {
548562
done
549563

550564
local found_mo
551-
found_mo=$(command -v mo 2> /dev/null || true)
552-
if [[ -n "$found_mo" && -f "$found_mo" ]]; then
553-
if [[ ! -L "$found_mo" ]] || ! readlink "$found_mo" | grep -q "Cellar/mole"; then
554-
alias_installs+=("$found_mo")
565+
found_mo=""
566+
if [[ "$test_mode" != "true" ]]; then
567+
found_mo=$(command -v mo 2> /dev/null || true)
568+
if [[ -n "$found_mo" && -f "$found_mo" ]]; then
569+
if [[ ! -L "$found_mo" ]] || ! readlink "$found_mo" | grep -q "Cellar/mole"; then
570+
alias_installs+=("$found_mo")
571+
fi
555572
fi
556573
fi
557574

558-
local -a alias_fallback=(
559-
"/usr/local/bin/mo"
560-
"$HOME/.local/bin/mo"
561-
"/opt/local/bin/mo"
562-
)
575+
local -a alias_fallback=()
576+
if [[ "$test_mode" == "true" ]]; then
577+
alias_fallback=("$HOME/.local/bin/mo")
578+
else
579+
alias_fallback=(
580+
"/usr/local/bin/mo"
581+
"$HOME/.local/bin/mo"
582+
"/opt/local/bin/mo"
583+
)
584+
fi
563585

564586
for alias in "${alias_fallback[@]}"; do
565587
if [[ -f "$alias" && "$alias" != "$found_mo" ]]; then

scripts/test.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
1010

1111
cd "$PROJECT_ROOT"
1212

13+
# Never allow the scripted test run to trigger real sudo or Touch ID prompts.
14+
export MOLE_TEST_NO_AUTH=1
15+
1316
# shellcheck source=lib/core/file_ops.sh
1417
source "$PROJECT_ROOT/lib/core/file_ops.sh"
1518

tests/uninstall.bats

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ EOF
362362
touch "$HOME/.local/bin/mo"
363363
mkdir -p "$HOME/.config/mole" "$HOME/.cache/mole"
364364

365-
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="/usr/bin:/bin" bash --noprofile --norc <<'EOF'
365+
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="/usr/bin:/bin" MOLE_TEST_MODE=1 bash --noprofile --norc <<'EOF'
366366
set -euo pipefail
367367
start_inline_spinner() { :; }
368368
stop_inline_spinner() { :; }
@@ -410,7 +410,7 @@ EOF
410410
touch "$HOME/.local/bin/mo"
411411
mkdir -p "$HOME/.config/mole" "$HOME/.cache/mole"
412412

413-
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="/usr/bin:/bin" bash --noprofile --norc <<'EOF'
413+
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="/usr/bin:/bin" MOLE_TEST_MODE=1 bash --noprofile --norc <<'EOF'
414414
set -euo pipefail
415415
start_inline_spinner() { :; }
416416
stop_inline_spinner() { :; }
@@ -425,3 +425,35 @@ EOF
425425
[ -d "$HOME/.config/mole" ]
426426
[ -d "$HOME/.cache/mole" ]
427427
}
428+
429+
@test "remove_mole test mode ignores PATH installs outside test HOME" {
430+
mkdir -p "$HOME/.local/bin" "$HOME/.config/mole" "$HOME/.cache/mole"
431+
touch "$HOME/.local/bin/mole"
432+
touch "$HOME/.local/bin/mo"
433+
434+
fake_global_bin="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-remove-path.XXXXXX")"
435+
touch "$fake_global_bin/mole"
436+
touch "$fake_global_bin/mo"
437+
cat > "$fake_global_bin/brew" <<'EOF'
438+
#!/bin/bash
439+
exit 0
440+
EOF
441+
chmod +x "$fake_global_bin/brew"
442+
443+
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="$fake_global_bin:/usr/bin:/bin" MOLE_TEST_MODE=1 bash --noprofile --norc <<'EOF'
444+
set -euo pipefail
445+
start_inline_spinner() { :; }
446+
stop_inline_spinner() { :; }
447+
export -f start_inline_spinner stop_inline_spinner
448+
printf '\n' | "$PROJECT_ROOT/mole" remove --dry-run
449+
EOF
450+
451+
rm -rf "$fake_global_bin"
452+
453+
[ "$status" -eq 0 ]
454+
[[ "$output" == *"$HOME/.local/bin/mole"* ]]
455+
[[ "$output" == *"$HOME/.local/bin/mo"* ]]
456+
[[ "$output" != *"$fake_global_bin/mole"* ]]
457+
[[ "$output" != *"$fake_global_bin/mo"* ]]
458+
[[ "$output" != *"brew uninstall --force mole"* ]]
459+
}

0 commit comments

Comments
 (0)