mirror of
https://github.com/tw93/Mole.git
synced 2026-02-17 00:34:10 +00:00
security: harden BOM processing and LaunchAgents detection
- Add path traversal protection in BOM receipt parsing - Remove invalid ~/Library/LaunchDaemons path references - Strengthen LaunchAgents matching (min 5 chars, exclude com.apple.*) - Add 300s timeout to brew cask uninstall to prevent hangs Addresses security review findings from V1.21.0 audit.
This commit is contained in:
@@ -643,6 +643,14 @@ is_path_whitelisted() {
|
||||
find_app_files() {
|
||||
local bundle_id="$1"
|
||||
local app_name="$2"
|
||||
|
||||
# Early validation: require at least one valid identifier
|
||||
# Skip scanning if both bundle_id and app_name are invalid
|
||||
if [[ -z "$bundle_id" || "$bundle_id" == "unknown" ]] &&
|
||||
[[ -z "$app_name" || ${#app_name} -lt 2 ]]; then
|
||||
return 0 # Silent return to avoid invalid scanning
|
||||
fi
|
||||
|
||||
local -a files_to_clean=()
|
||||
|
||||
# Normalize app name for matching
|
||||
@@ -665,7 +673,6 @@ find_app_files() {
|
||||
"$HOME/Library/HTTPStorages/$bundle_id"
|
||||
"$HOME/Library/Cookies/$bundle_id.binarycookies"
|
||||
"$HOME/Library/LaunchAgents/$bundle_id.plist"
|
||||
"$HOME/Library/LaunchDaemons/$bundle_id.plist"
|
||||
"$HOME/Library/Application Scripts/$bundle_id"
|
||||
"$HOME/Library/Services/$app_name.workflow"
|
||||
"$HOME/Library/QuickLook/$app_name.qlgenerator"
|
||||
@@ -740,18 +747,16 @@ find_app_files() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# Launch Agents and Daemons by name (special handling)
|
||||
if [[ ${#app_name} -gt 3 ]]; then
|
||||
if [[ -d ~/Library/LaunchAgents ]]; then
|
||||
# Launch Agents by name (special handling)
|
||||
# Note: LaunchDaemons are system-level and handled in find_app_system_files()
|
||||
if [[ ${#app_name} -ge 5 ]] && [[ -d ~/Library/LaunchAgents ]]; then
|
||||
while IFS= read -r -d '' plist; do
|
||||
files_to_clean+=("$plist")
|
||||
done < <(command find ~/Library/LaunchAgents -maxdepth 1 \( -name "*$app_name*.plist" \) -print0 2> /dev/null)
|
||||
local plist_name=$(basename "$plist")
|
||||
if [[ "$plist_name" =~ ^com\.apple\. ]]; then
|
||||
continue
|
||||
fi
|
||||
if [[ -d ~/Library/LaunchDaemons ]]; then
|
||||
while IFS= read -r -d '' plist; do
|
||||
files_to_clean+=("$plist")
|
||||
done < <(command find ~/Library/LaunchDaemons -maxdepth 1 \( -name "*$app_name*.plist" \) -print0 2> /dev/null)
|
||||
fi
|
||||
done < <(command find ~/Library/LaunchAgents -maxdepth 1 -name "*$app_name*.plist" -print0 2>/dev/null)
|
||||
fi
|
||||
|
||||
# Handle specialized toolchains and development environments
|
||||
@@ -764,7 +769,7 @@ find_app_files() {
|
||||
|
||||
# 2. Android Studio (Google)
|
||||
if [[ "$app_name" =~ Android.*Studio|android.*studio ]] || [[ "$bundle_id" =~ google.*android.*studio|jetbrains.*android ]]; then
|
||||
for d in ~/AndroidStudioProjects ~/Library/Android ~/.android ~/.gradle; do
|
||||
for d in ~/AndroidStudioProjects ~/Library/Android ~/.android; do
|
||||
[[ -d "$d" ]] && files_to_clean+=("$d")
|
||||
done
|
||||
[[ -d ~/Library/Application\ Support/Google ]] && while IFS= read -r -d '' d; do files_to_clean+=("$d"); done < <(command find ~/Library/Application\ Support/Google -maxdepth 1 -name "AndroidStudio*" -print0 2>/dev/null)
|
||||
@@ -904,6 +909,13 @@ find_app_receipt_files() {
|
||||
# Skip if no bundle ID
|
||||
[[ -z "$bundle_id" || "$bundle_id" == "unknown" ]] && return 0
|
||||
|
||||
# Validate bundle_id format to prevent wildcard injection
|
||||
# Only allow alphanumeric characters, dots, hyphens, and underscores
|
||||
if [[ ! "$bundle_id" =~ ^[a-zA-Z0-9._-]+$ ]]; then
|
||||
debug_log "Invalid bundle_id format: $bundle_id"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local -a receipt_files=()
|
||||
local -a bom_files=()
|
||||
|
||||
@@ -935,6 +947,15 @@ find_app_receipt_files() {
|
||||
clean_path="/$clean_path"
|
||||
fi
|
||||
|
||||
# Path traversal protection: reject paths containing ..
|
||||
if [[ "$clean_path" =~ \.\. ]]; then
|
||||
debug_log "Rejected path traversal in BOM: $clean_path"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Normalize path (remove duplicate slashes)
|
||||
clean_path=$(echo "$clean_path" | sed 's#//*#/#g')
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Safety check: restrict removal to trusted paths
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
@@ -173,25 +173,22 @@ brew_uninstall_cask() {
|
||||
|
||||
debug_log "Attempting brew uninstall --cask $cask_name"
|
||||
|
||||
# Run uninstall with timeout (suppress hints/auto-update)
|
||||
debug_log "Attempting brew uninstall --cask $cask_name"
|
||||
|
||||
# Ensure we have sudo access if needed, to prevent brew from hanging on password prompt
|
||||
# Many brew casks need sudo to uninstall
|
||||
if ! sudo -n true 2>/dev/null; then
|
||||
# If we don't have sudo, try to get it (visibly)
|
||||
sudo -v
|
||||
fi
|
||||
|
||||
local uninstall_ok=false
|
||||
local brew_exit=0
|
||||
|
||||
# Run directly without output capture to allow user interaction/visibility
|
||||
# This avoids silence/hangs when brew asks for passwords or confirmation
|
||||
if HOMEBREW_NO_ENV_HINTS=1 HOMEBREW_NO_AUTO_UPDATE=1 NONINTERACTIVE=1 \
|
||||
brew uninstall --cask "$cask_name"; then
|
||||
# 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
|
||||
uninstall_ok=true
|
||||
else
|
||||
debug_log "brew uninstall failed with exit code $?"
|
||||
brew_exit=$?
|
||||
debug_log "brew uninstall timeout or failed with exit code: $brew_exit"
|
||||
fi
|
||||
|
||||
# Verify removal
|
||||
|
||||
Reference in New Issue
Block a user