1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-04 09:46:44 +00:00

refactor: enhance uninstall safety and fix dock removal

- Add symlink/bundle_id/BOM validation to prevent injection attacks
- Fix Dock removal for /Applications symlink (use pwd not pwd -P)
- Fix brew uninstall test hanging (skip sudo in non-interactive mode)
- Remove unused SENSITIVE_DATA_REGEX
This commit is contained in:
Tw93
2026-01-17 09:49:42 +08:00
parent 12cacaa6cc
commit 060c48c48d
7 changed files with 22 additions and 33 deletions

1
.gitignore vendored
View File

@@ -48,6 +48,7 @@ tests/tmp-*
CLAUDE.md
GEMINI.md
ANTIGRAVITY.md
WARP.md
.cursorrules
# Go build artifacts (development)

View File

@@ -749,6 +749,8 @@ find_app_files() {
# Launch Agents by name (special handling)
# Note: LaunchDaemons are system-level and handled in find_app_system_files()
# Minimum 5-char threshold prevents false positives (e.g., "Time" matching system agents)
# Short-name apps (e.g., Zoom, Arc) are still cleaned via bundle_id matching above
if [[ ${#app_name} -ge 5 ]] && [[ -d ~/Library/LaunchAgents ]]; then
while IFS= read -r -d '' plist; do
local plist_name=$(basename "$plist")

View File

@@ -133,7 +133,7 @@ remove_apps_from_dock() {
fi
if [[ -e "$app_path" ]]; then
if full_path=$(cd "$(dirname "$app_path")" 2>/dev/null && pwd -P); then
if full_path=$(cd "$(dirname "$app_path")" 2>/dev/null && pwd); then
full_path="$full_path/$(basename "$app_path")"
else
continue

View File

@@ -47,11 +47,19 @@ validate_path_for_deletion() {
return 1
}
# If symlink points to absolute path, validate target
if [[ "$link_target" == /* ]]; then
case "$link_target" in
# Resolve relative symlinks to absolute paths for validation
local resolved_target="$link_target"
if [[ "$link_target" != /* ]]; then
local link_dir
link_dir=$(dirname "$path")
resolved_target=$(cd "$link_dir" 2>/dev/null && cd "$(dirname "$link_target")" 2>/dev/null && pwd)/$(basename "$link_target") || resolved_target=""
fi
# Validate resolved target against protected paths
if [[ -n "$resolved_target" ]]; then
case "$resolved_target" in
/System/* | /usr/bin/* | /usr/lib/* | /bin/* | /sbin/* | /private/etc/*)
log_error "Symlink points to protected system path: $path -> $link_target"
log_error "Symlink points to protected system path: $path -> $resolved_target"
return 1
;;
esac

View File

@@ -11,25 +11,6 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# Batch uninstall with a single confirmation.
# User data detection patterns (prompt user to backup if found).
readonly SENSITIVE_DATA_PATTERNS=(
"\.warp" # Warp terminal configs/themes
"/\.config/" # Standard Unix config directory
"/themes/" # Theme customizations
"/settings/" # Settings directories
"/Application Support/[^/]+/User Data" # Chrome/Electron user data
"/Preferences/[^/]+\.plist" # User preference files
"/Documents/" # User documents
"/\.ssh/" # SSH keys and configs (critical)
"/\.gnupg/" # GPG keys (critical)
)
# Join patterns into a single regex for grep.
SENSITIVE_DATA_REGEX=$(
IFS='|'
echo "${SENSITIVE_DATA_PATTERNS[*]}"
)
# High-performance sensitive data detection (pure Bash, no subprocess)
# Faster than grep for batch operations, especially when processing many apps
has_sensitive_data() {

View File

@@ -174,17 +174,18 @@ brew_uninstall_cask() {
debug_log "Attempting brew uninstall --cask $cask_name"
# Ensure we have sudo access if needed, to prevent brew from hanging on password prompt
if ! sudo -n true 2>/dev/null; then
sudo -v
if [[ "${NONINTERACTIVE:-}" != "1" && -t 0 && -t 1 ]]; then
if ! sudo -n true 2>/dev/null; then
sudo -v
fi
fi
local uninstall_ok=false
local brew_exit=0
# Run with timeout to prevent hangs from problematic cask scripts
if run_with_timeout 300 \
env HOMEBREW_NO_ENV_HINTS=1 HOMEBREW_NO_AUTO_UPDATE=1 NONINTERACTIVE=1 \
brew uninstall --cask "$cask_name" 2>&1; then
if HOMEBREW_NO_ENV_HINTS=1 HOMEBREW_NO_AUTO_UPDATE=1 NONINTERACTIVE=1 \
run_with_timeout 300 brew uninstall --cask "$cask_name" 2>&1; then
uninstall_ok=true
else
brew_exit=$?

View File

@@ -39,8 +39,6 @@ create_app_artifacts() {
mkdir -p "$HOME/Library/Saved Application State/com.example.TestApp.savedState"
mkdir -p "$HOME/Library/LaunchAgents"
touch "$HOME/Library/LaunchAgents/com.example.TestApp.plist"
mkdir -p "$HOME/Library/LaunchDaemons"
touch "$HOME/Library/LaunchDaemons/com.example.TestApp.plist"
}
@test "find_app_files discovers user-level leftovers" {
@@ -60,7 +58,6 @@ EOF
[[ "$result" == *"Saved Application State/com.example.TestApp.savedState"* ]]
[[ "$result" == *"Containers/com.example.TestApp"* ]]
[[ "$result" == *"LaunchAgents/com.example.TestApp.plist"* ]]
[[ "$result" == *"LaunchDaemons/com.example.TestApp.plist"* ]]
}
@test "calculate_total_size returns aggregate kilobytes" {
@@ -121,7 +118,6 @@ batch_uninstall_applications
[[ ! -d "$HOME/Library/Caches/TestApp" ]] || exit 1
[[ ! -f "$HOME/Library/Preferences/com.example.TestApp.plist" ]] || exit 1
[[ ! -f "$HOME/Library/LaunchAgents/com.example.TestApp.plist" ]] || exit 1
[[ ! -f "$HOME/Library/LaunchDaemons/com.example.TestApp.plist" ]] || exit 1
EOF
[ "$status" -eq 0 ]