mirror of
https://github.com/tw93/Mole.git
synced 2026-03-23 21:35:06 +00:00
fix: fall back to safe temp directories
This commit is contained in:
@@ -221,7 +221,8 @@ is_bundle_orphaned() {
|
||||
if [[ -n "$bundle_id" ]] && [[ "$bundle_id" =~ ^[a-zA-Z0-9._-]+$ ]] && [[ ${#bundle_id} -ge 5 ]]; then
|
||||
# Initialize cache file if needed
|
||||
if [[ -z "$ORPHAN_MDFIND_CACHE_FILE" ]]; then
|
||||
ORPHAN_MDFIND_CACHE_FILE=$(mktemp "${TMPDIR:-/tmp}/mole_mdfind_cache.XXXXXX")
|
||||
ensure_mole_temp_root
|
||||
ORPHAN_MDFIND_CACHE_FILE=$(mktemp "$MOLE_RESOLVED_TMPDIR/mole_mdfind_cache.XXXXXX")
|
||||
register_temp_file "$ORPHAN_MDFIND_CACHE_FILE"
|
||||
fi
|
||||
|
||||
@@ -277,7 +278,8 @@ is_claude_vm_bundle_orphaned() {
|
||||
fi
|
||||
|
||||
if [[ -z "$ORPHAN_MDFIND_CACHE_FILE" ]]; then
|
||||
ORPHAN_MDFIND_CACHE_FILE=$(mktemp "${TMPDIR:-/tmp}/mole_mdfind_cache.XXXXXX")
|
||||
ensure_mole_temp_root
|
||||
ORPHAN_MDFIND_CACHE_FILE=$(mktemp "$MOLE_RESOLVED_TMPDIR/mole_mdfind_cache.XXXXXX")
|
||||
register_temp_file "$ORPHAN_MDFIND_CACHE_FILE"
|
||||
fi
|
||||
|
||||
@@ -449,7 +451,8 @@ clean_orphaned_system_services() {
|
||||
|
||||
if [[ -n "$bundle_id" ]] && [[ "$bundle_id" =~ ^[a-zA-Z0-9._-]+$ ]] && [[ ${#bundle_id} -ge 5 ]]; then
|
||||
if [[ -z "$mdfind_cache_file" ]]; then
|
||||
mdfind_cache_file=$(mktemp "${TMPDIR:-/tmp}/mole_mdfind_cache.XXXXXX")
|
||||
ensure_mole_temp_root
|
||||
mdfind_cache_file=$(mktemp "$MOLE_RESOLVED_TMPDIR/mole_mdfind_cache.XXXXXX")
|
||||
register_temp_file "$mdfind_cache_file"
|
||||
fi
|
||||
|
||||
|
||||
@@ -556,10 +556,93 @@ bytes_to_human_kb() {
|
||||
declare -a MOLE_TEMP_FILES=()
|
||||
declare -a MOLE_TEMP_DIRS=()
|
||||
|
||||
normalize_temp_root() {
|
||||
local path="${1:-}"
|
||||
[[ -z "$path" ]] && return 1
|
||||
|
||||
if [[ "$path" == "~"* ]]; then
|
||||
path="${path/#\~/$HOME}"
|
||||
fi
|
||||
|
||||
while [[ "$path" != "/" && "$path" == */ ]]; do
|
||||
path="${path%/}"
|
||||
done
|
||||
|
||||
[[ -n "$path" ]] || return 1
|
||||
printf '%s\n' "$path"
|
||||
}
|
||||
|
||||
probe_temp_root() {
|
||||
local raw_path="$1"
|
||||
local allow_create="${2:-false}"
|
||||
local path
|
||||
local probe=""
|
||||
|
||||
path=$(normalize_temp_root "$raw_path") || return 1
|
||||
|
||||
if [[ "$allow_create" == "true" ]]; then
|
||||
ensure_user_dir "$path"
|
||||
fi
|
||||
|
||||
[[ -d "$path" ]] || return 1
|
||||
|
||||
probe=$(mktemp "$path/mole.probe.XXXXXX" 2> /dev/null) || return 1
|
||||
rm -f "$probe" 2> /dev/null || true
|
||||
|
||||
printf '%s\n' "$path"
|
||||
}
|
||||
|
||||
ensure_mole_temp_root() {
|
||||
if [[ -n "${MOLE_RESOLVED_TMPDIR:-}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local resolved=""
|
||||
local candidate="${TMPDIR:-}"
|
||||
local invoking_home=""
|
||||
|
||||
if [[ -n "$candidate" ]]; then
|
||||
resolved=$(probe_temp_root "$candidate" false || true)
|
||||
fi
|
||||
|
||||
if [[ -z "$resolved" ]]; then
|
||||
invoking_home=$(get_invoking_home)
|
||||
if [[ -n "$invoking_home" ]]; then
|
||||
resolved=$(probe_temp_root "$invoking_home/.cache/mole/tmp" true || true)
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$resolved" ]]; then
|
||||
resolved=$(probe_temp_root "/tmp" false || true)
|
||||
fi
|
||||
|
||||
[[ -n "$resolved" ]] || resolved="/tmp"
|
||||
MOLE_RESOLVED_TMPDIR="$resolved"
|
||||
export MOLE_RESOLVED_TMPDIR
|
||||
}
|
||||
|
||||
get_mole_temp_root() {
|
||||
ensure_mole_temp_root
|
||||
printf '%s\n' "$MOLE_RESOLVED_TMPDIR"
|
||||
}
|
||||
|
||||
prepare_mole_tmpdir() {
|
||||
ensure_mole_temp_root
|
||||
export TMPDIR="$MOLE_RESOLVED_TMPDIR"
|
||||
printf '%s\n' "$MOLE_RESOLVED_TMPDIR"
|
||||
}
|
||||
|
||||
mole_temp_path_template() {
|
||||
local prefix="${1:-mole}"
|
||||
ensure_mole_temp_root
|
||||
printf '%s/%s.XXXXXX\n' "$MOLE_RESOLVED_TMPDIR" "$prefix"
|
||||
}
|
||||
|
||||
# Create tracked temporary file
|
||||
create_temp_file() {
|
||||
local temp
|
||||
temp=$(mktemp "${TMPDIR:-/tmp}/mole.XXXXXX") || return 1
|
||||
ensure_mole_temp_root
|
||||
temp=$(mktemp "$MOLE_RESOLVED_TMPDIR/mole.XXXXXX") || return 1
|
||||
register_temp_file "$temp"
|
||||
echo "$temp"
|
||||
}
|
||||
@@ -567,7 +650,8 @@ create_temp_file() {
|
||||
# Create tracked temporary directory
|
||||
create_temp_dir() {
|
||||
local temp
|
||||
temp=$(mktemp -d "${TMPDIR:-/tmp}/mole.XXXXXX") || return 1
|
||||
ensure_mole_temp_root
|
||||
temp=$(mktemp -d "$MOLE_RESOLVED_TMPDIR/mole.XXXXXX") || return 1
|
||||
register_temp_dir "$temp"
|
||||
echo "$temp"
|
||||
}
|
||||
@@ -588,9 +672,8 @@ mktemp_file() {
|
||||
local prefix="${1:-mole}"
|
||||
local temp
|
||||
local error_msg
|
||||
# Use TMPDIR if set, otherwise /tmp
|
||||
# Add .XXXXXX suffix to work with both BSD and GNU mktemp
|
||||
if ! error_msg=$(mktemp "${TMPDIR:-/tmp}/${prefix}.XXXXXX" 2>&1); then
|
||||
if ! error_msg=$(mktemp "$(mole_temp_path_template "$prefix")" 2>&1); then
|
||||
echo "Error: Failed to create temporary file: $error_msg" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -14,6 +14,7 @@ _MOLE_CORE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Load core modules
|
||||
source "$_MOLE_CORE_DIR/base.sh"
|
||||
prepare_mole_tmpdir > /dev/null
|
||||
source "$_MOLE_CORE_DIR/log.sh"
|
||||
|
||||
source "$_MOLE_CORE_DIR/timeout.sh"
|
||||
|
||||
@@ -324,7 +324,8 @@ start_inline_spinner() {
|
||||
|
||||
if [[ -t 1 ]]; then
|
||||
# Create unique stop flag file for this spinner instance
|
||||
INLINE_SPINNER_STOP_FILE="${TMPDIR:-/tmp}/mole_spinner_$$_$RANDOM.stop"
|
||||
ensure_mole_temp_root
|
||||
INLINE_SPINNER_STOP_FILE="$MOLE_RESOLVED_TMPDIR/mole_spinner_$$_$RANDOM.stop"
|
||||
|
||||
(
|
||||
local stop_file="$INLINE_SPINNER_STOP_FILE"
|
||||
|
||||
@@ -91,6 +91,26 @@ run_clean_dry_run() {
|
||||
[[ "$output" == *"full preview"* ]]
|
||||
}
|
||||
|
||||
@test "mo clean --dry-run survives an unwritable TMPDIR" {
|
||||
local blocked_tmp="$HOME/blocked-tmp"
|
||||
mkdir -p "$blocked_tmp"
|
||||
chmod 500 "$blocked_tmp"
|
||||
|
||||
set_mock_sudo_uncached
|
||||
local test_path="$PATH"
|
||||
if [[ -n "${TEST_MOCK_BIN:-}" ]]; then
|
||||
test_path="$TEST_MOCK_BIN:$PATH"
|
||||
fi
|
||||
|
||||
run env HOME="$HOME" TMPDIR="$blocked_tmp" MOLE_TEST_MODE=1 PATH="$test_path" \
|
||||
"$PROJECT_ROOT/mole" clean --dry-run
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" != *"mktemp:"* ]]
|
||||
[[ "$output" != *"Failed to create temporary file"* ]]
|
||||
[ -d "$HOME/.cache/mole/tmp" ]
|
||||
}
|
||||
|
||||
@test "mo clean --dry-run reports user cache without deleting it" {
|
||||
mkdir -p "$HOME/Library/Caches/TestApp"
|
||||
echo "cache data" > "$HOME/Library/Caches/TestApp/cache.tmp"
|
||||
|
||||
@@ -76,6 +76,61 @@ setup() {
|
||||
[ -d "$result" ]
|
||||
}
|
||||
|
||||
@test "get_mole_temp_root uses writable TMPDIR when available" {
|
||||
local writable_tmp="$HOME/custom-tmp"
|
||||
mkdir -p "$writable_tmp"
|
||||
|
||||
result=$(env HOME="$HOME" TMPDIR="$writable_tmp" bash -c "source '$PROJECT_ROOT/lib/core/base.sh'; get_mole_temp_root")
|
||||
[ "$result" = "$writable_tmp" ]
|
||||
}
|
||||
|
||||
@test "get_mole_temp_root falls back to user cache when TMPDIR is not writable" {
|
||||
local blocked_tmp="$HOME/blocked-tmp"
|
||||
mkdir -p "$blocked_tmp"
|
||||
chmod 500 "$blocked_tmp"
|
||||
|
||||
result=$(env HOME="$HOME" TMPDIR="$blocked_tmp" bash -c "source '$PROJECT_ROOT/lib/core/base.sh'; get_mole_temp_root")
|
||||
[ "$result" = "$HOME/.cache/mole/tmp" ]
|
||||
[ -d "$HOME/.cache/mole/tmp" ]
|
||||
}
|
||||
|
||||
@test "get_mole_temp_root caches the first resolved directory" {
|
||||
local first_tmp="$HOME/first-tmp"
|
||||
local second_tmp="$HOME/second-tmp"
|
||||
mkdir -p "$first_tmp" "$second_tmp"
|
||||
|
||||
result=$(env HOME="$HOME" TMPDIR="$first_tmp" bash -c "
|
||||
source '$PROJECT_ROOT/lib/core/base.sh'
|
||||
ensure_mole_temp_root
|
||||
first=\$MOLE_RESOLVED_TMPDIR
|
||||
export TMPDIR='$second_tmp'
|
||||
ensure_mole_temp_root
|
||||
second=\$MOLE_RESOLVED_TMPDIR
|
||||
printf '%s|%s\n' \"\$first\" \"\$second\"
|
||||
")
|
||||
|
||||
[ "$result" = "$first_tmp|$first_tmp" ]
|
||||
}
|
||||
|
||||
@test "get_mole_temp_root falls back to /tmp when TMPDIR and invoking home are unavailable" {
|
||||
result=$(env HOME="$HOME" TMPDIR="/var/empty" bash -c "
|
||||
source '$PROJECT_ROOT/lib/core/base.sh'
|
||||
get_invoking_home() { echo '/var/empty'; }
|
||||
get_mole_temp_root
|
||||
")
|
||||
|
||||
[ "$result" = "/tmp" ]
|
||||
}
|
||||
|
||||
@test "common.sh exports resolved TMPDIR for runtime callers" {
|
||||
local blocked_tmp="$HOME/common-blocked-tmp"
|
||||
mkdir -p "$blocked_tmp"
|
||||
chmod 500 "$blocked_tmp"
|
||||
|
||||
result=$(env HOME="$HOME" TMPDIR="$blocked_tmp" bash -c "source '$PROJECT_ROOT/lib/core/common.sh'; printf '%s\n' \"\$TMPDIR\"")
|
||||
[ "$result" = "$HOME/.cache/mole/tmp" ]
|
||||
}
|
||||
|
||||
@test "get_user_home returns home for valid user" {
|
||||
current_user="${USER:-$(whoami)}"
|
||||
result=$(bash -c "source '$PROJECT_ROOT/lib/core/base.sh'; get_user_home '$current_user'")
|
||||
|
||||
Reference in New Issue
Block a user