1
0
mirror of https://github.com/tw93/Mole.git synced 2026-03-24 12:55:07 +00:00

fix(purge): save and restore caller traps to prevent state leak

- Save caller's INT/TERM traps before installing local cleanup trap
- Restore original traps after clean_project_artifacts completes
- Add test to verify trap restoration behavior

Fixes P3 issue: project.sh (line 825, 870)
This commit is contained in:
tw93
2026-02-21 20:21:54 +08:00
parent 7648bd9d12
commit 0597021fd4
2 changed files with 44 additions and 8 deletions

View File

@@ -800,6 +800,9 @@ clean_project_artifacts() {
local -a all_found_items=() local -a all_found_items=()
local -a safe_to_clean=() local -a safe_to_clean=()
local -a recently_modified=() local -a recently_modified=()
local previous_int_trap=""
local previous_term_trap=""
local trap_installed_by_this_call=false
# Set up cleanup on interrupt # Set up cleanup on interrupt
# Note: Declared without 'local' so cleanup_scan trap can access them # Note: Declared without 'local' so cleanup_scan trap can access them
scan_pids=() scan_pids=()
@@ -820,12 +823,11 @@ clean_project_artifacts() {
echo "" echo ""
exit 130 exit 130
} }
# Set up signal handling only if not already set by parent script # Save caller traps and install local cleanup trap for this function call.
# This prevents trap conflicts between purge.sh and project.sh previous_int_trap=$(trap -p INT || true)
if [[ -z "${_MO_PURGE_TRAP_SET:-}" ]]; then previous_term_trap=$(trap -p TERM || true)
trap cleanup_scan INT TERM trap cleanup_scan INT TERM
export _MO_PURGE_TRAP_SET=1 trap_installed_by_this_call=true
fi
# Scanning is started from purge.sh with start_inline_spinner # Scanning is started from purge.sh with start_inline_spinner
# Launch all scans in parallel # Launch all scans in parallel
for path in "${PURGE_SEARCH_PATHS[@]}"; do for path in "${PURGE_SEARCH_PATHS[@]}"; do
@@ -866,8 +868,12 @@ clean_project_artifacts() {
rm -f "$scan_output" rm -f "$scan_output"
fi fi
done done
# Clean up trap # Restore caller traps after this function completes.
if [[ "$trap_installed_by_this_call" == "true" ]]; then
trap - INT TERM trap - INT TERM
[[ -n "$previous_int_trap" ]] && eval "$previous_int_trap"
[[ -n "$previous_term_trap" ]] && eval "$previous_term_trap"
fi
if [[ ${#all_found_items[@]} -eq 0 ]]; then if [[ ${#all_found_items[@]} -eq 0 ]]; then
echo "" echo ""
echo -e "${GREEN}${ICON_SUCCESS}${NC} Great! No old project artifacts to clean" echo -e "${GREEN}${ICON_SUCCESS}${NC} Great! No old project artifacts to clean"

View File

@@ -505,6 +505,36 @@ EOF
[[ "$result" == "TIMEOUT" ]] [[ "$result" == "TIMEOUT" ]]
} }
@test "clean_project_artifacts: restores caller INT/TERM traps" {
result=$(bash -c "
set -euo pipefail
export HOME='$HOME'
source '$PROJECT_ROOT/lib/core/common.sh'
source '$PROJECT_ROOT/lib/clean/project.sh'
mkdir -p '$HOME/www'
PURGE_SEARCH_PATHS=('$HOME/www')
trap 'echo parent-int' INT
trap 'echo parent-term' TERM
before_int=\$(trap -p INT)
before_term=\$(trap -p TERM)
clean_project_artifacts > /dev/null 2>&1 || true
after_int=\$(trap -p INT)
after_term=\$(trap -p TERM)
if [[ \"\$before_int\" == \"\$after_int\" && \"\$before_term\" == \"\$after_term\" ]]; then
echo 'PASS'
else
echo 'FAIL'
echo \"before_int=\$before_int\"
echo \"after_int=\$after_int\"
echo \"before_term=\$before_term\"
echo \"after_term=\$after_term\"
exit 1
fi
")
[[ "$result" == *"PASS"* ]]
}
@test "clean_project_artifacts: handles empty directory gracefully" { @test "clean_project_artifacts: handles empty directory gracefully" {
run bash -c " run bash -c "
export HOME='$HOME' export HOME='$HOME'