mirror of
https://github.com/tw93/Mole.git
synced 2026-02-16 04:51:11 +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() {
|
find_app_files() {
|
||||||
local bundle_id="$1"
|
local bundle_id="$1"
|
||||||
local app_name="$2"
|
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=()
|
local -a files_to_clean=()
|
||||||
|
|
||||||
# Normalize app name for matching
|
# Normalize app name for matching
|
||||||
@@ -665,7 +673,6 @@ find_app_files() {
|
|||||||
"$HOME/Library/HTTPStorages/$bundle_id"
|
"$HOME/Library/HTTPStorages/$bundle_id"
|
||||||
"$HOME/Library/Cookies/$bundle_id.binarycookies"
|
"$HOME/Library/Cookies/$bundle_id.binarycookies"
|
||||||
"$HOME/Library/LaunchAgents/$bundle_id.plist"
|
"$HOME/Library/LaunchAgents/$bundle_id.plist"
|
||||||
"$HOME/Library/LaunchDaemons/$bundle_id.plist"
|
|
||||||
"$HOME/Library/Application Scripts/$bundle_id"
|
"$HOME/Library/Application Scripts/$bundle_id"
|
||||||
"$HOME/Library/Services/$app_name.workflow"
|
"$HOME/Library/Services/$app_name.workflow"
|
||||||
"$HOME/Library/QuickLook/$app_name.qlgenerator"
|
"$HOME/Library/QuickLook/$app_name.qlgenerator"
|
||||||
@@ -740,18 +747,16 @@ find_app_files() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Launch Agents and Daemons by name (special handling)
|
# Launch Agents by name (special handling)
|
||||||
if [[ ${#app_name} -gt 3 ]]; then
|
# Note: LaunchDaemons are system-level and handled in find_app_system_files()
|
||||||
if [[ -d ~/Library/LaunchAgents ]]; then
|
if [[ ${#app_name} -ge 5 ]] && [[ -d ~/Library/LaunchAgents ]]; then
|
||||||
while IFS= read -r -d '' plist; do
|
while IFS= read -r -d '' plist; do
|
||||||
files_to_clean+=("$plist")
|
local plist_name=$(basename "$plist")
|
||||||
done < <(command find ~/Library/LaunchAgents -maxdepth 1 \( -name "*$app_name*.plist" \) -print0 2> /dev/null)
|
if [[ "$plist_name" =~ ^com\.apple\. ]]; then
|
||||||
|
continue
|
||||||
fi
|
fi
|
||||||
if [[ -d ~/Library/LaunchDaemons ]]; then
|
|
||||||
while IFS= read -r -d '' plist; do
|
|
||||||
files_to_clean+=("$plist")
|
files_to_clean+=("$plist")
|
||||||
done < <(command find ~/Library/LaunchDaemons -maxdepth 1 \( -name "*$app_name*.plist" \) -print0 2> /dev/null)
|
done < <(command find ~/Library/LaunchAgents -maxdepth 1 -name "*$app_name*.plist" -print0 2>/dev/null)
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Handle specialized toolchains and development environments
|
# Handle specialized toolchains and development environments
|
||||||
@@ -764,7 +769,7 @@ find_app_files() {
|
|||||||
|
|
||||||
# 2. Android Studio (Google)
|
# 2. Android Studio (Google)
|
||||||
if [[ "$app_name" =~ Android.*Studio|android.*studio ]] || [[ "$bundle_id" =~ google.*android.*studio|jetbrains.*android ]]; then
|
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")
|
[[ -d "$d" ]] && files_to_clean+=("$d")
|
||||||
done
|
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)
|
[[ -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
|
# Skip if no bundle ID
|
||||||
[[ -z "$bundle_id" || "$bundle_id" == "unknown" ]] && return 0
|
[[ -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 receipt_files=()
|
||||||
local -a bom_files=()
|
local -a bom_files=()
|
||||||
|
|
||||||
@@ -935,6 +947,15 @@ find_app_receipt_files() {
|
|||||||
clean_path="/$clean_path"
|
clean_path="/$clean_path"
|
||||||
fi
|
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
|
# Safety check: restrict removal to trusted paths
|
||||||
# ------------------------------------------------------------------------
|
# ------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -173,25 +173,22 @@ brew_uninstall_cask() {
|
|||||||
|
|
||||||
debug_log "Attempting brew uninstall --cask $cask_name"
|
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
|
# 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 ! sudo -n true 2>/dev/null; then
|
||||||
# If we don't have sudo, try to get it (visibly)
|
|
||||||
sudo -v
|
sudo -v
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local uninstall_ok=false
|
local uninstall_ok=false
|
||||||
|
local brew_exit=0
|
||||||
|
|
||||||
# Run directly without output capture to allow user interaction/visibility
|
# Run with timeout to prevent hangs from problematic cask scripts
|
||||||
# This avoids silence/hangs when brew asks for passwords or confirmation
|
if run_with_timeout 300 \
|
||||||
if HOMEBREW_NO_ENV_HINTS=1 HOMEBREW_NO_AUTO_UPDATE=1 NONINTERACTIVE=1 \
|
env HOMEBREW_NO_ENV_HINTS=1 HOMEBREW_NO_AUTO_UPDATE=1 NONINTERACTIVE=1 \
|
||||||
brew uninstall --cask "$cask_name"; then
|
brew uninstall --cask "$cask_name" 2>&1; then
|
||||||
uninstall_ok=true
|
uninstall_ok=true
|
||||||
else
|
else
|
||||||
debug_log "brew uninstall failed with exit code $?"
|
brew_exit=$?
|
||||||
|
debug_log "brew uninstall timeout or failed with exit code: $brew_exit"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify removal
|
# Verify removal
|
||||||
|
|||||||
Reference in New Issue
Block a user