From 1c3f12744b43207fdf651bb48c69caa0830a5482 Mon Sep 17 00:00:00 2001 From: Developer Date: Thu, 23 Apr 2026 22:19:14 -0400 Subject: [PATCH] Fix: Replace process substitution with mapfile to prevent hanging in threat score calculation ISSUE: The calculate_threat_scores() function was hanging when loading threat IPs from various threat files using < <(pipe...) process substitution. SOLUTION: Replaced all while-read + process substitution patterns with mapfile, which loads data into arrays without spawning subshells or creating deadlock conditions. Changed from: done < <(awk ... | cut ...) Changed to: mapfile -t array < <(awk ... | cut ...) for item in "${array[@]}"; do ...done This maintains the original functionality while avoiding the hanging behavior. --- modules/security/bot-analyzer.sh | 51 ++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/modules/security/bot-analyzer.sh b/modules/security/bot-analyzer.sh index 75ce99e..6838a12 100755 --- a/modules/security/bot-analyzer.sh +++ b/modules/security/bot-analyzer.sh @@ -1667,34 +1667,41 @@ calculate_threat_scores() { declare -A threat_ips_sqli threat_ips_xss threat_ips_path threat_ips_rce threat_ips_login declare -A threat_ips_suspicious threat_ips_ddos threat_admin_count threat_404_count - # Parse each threat file and build hash tables (optimized with awk) - [ -f "$TEMP_DIR/sqli_attempts.txt" ] && while read -r ip; do - threat_ips_sqli["$ip"]=1 - done < <(awk '{print $2}' "$TEMP_DIR/sqli_attempts.txt" | cut -d'|' -f1) + # Parse each threat file and build hash tables (using mapfile to avoid subshells) + if [ -f "$TEMP_DIR/sqli_attempts.txt" ]; then + mapfile -t sqli_ips < <(awk '{print $2}' "$TEMP_DIR/sqli_attempts.txt" | cut -d'|' -f1) + for ip in "${sqli_ips[@]}"; do threat_ips_sqli["$ip"]=1; done + fi - [ -f "$TEMP_DIR/xss_attempts.txt" ] && while read -r ip; do - threat_ips_xss["$ip"]=1 - done < <(awk '{print $2}' "$TEMP_DIR/xss_attempts.txt" | cut -d'|' -f1) + if [ -f "$TEMP_DIR/xss_attempts.txt" ]; then + mapfile -t xss_ips < <(awk '{print $2}' "$TEMP_DIR/xss_attempts.txt" | cut -d'|' -f1) + for ip in "${xss_ips[@]}"; do threat_ips_xss["$ip"]=1; done + fi - [ -f "$TEMP_DIR/path_traversal_attempts.txt" ] && while read -r ip; do - threat_ips_path["$ip"]=1 - done < <(awk '{print $2}' "$TEMP_DIR/path_traversal_attempts.txt" | cut -d'|' -f1) + if [ -f "$TEMP_DIR/path_traversal_attempts.txt" ]; then + mapfile -t path_ips < <(awk '{print $2}' "$TEMP_DIR/path_traversal_attempts.txt" | cut -d'|' -f1) + for ip in "${path_ips[@]}"; do threat_ips_path["$ip"]=1; done + fi - [ -f "$TEMP_DIR/rce_upload_attempts.txt" ] && while read -r ip; do - threat_ips_rce["$ip"]=1 - done < <(awk '{print $2}' "$TEMP_DIR/rce_upload_attempts.txt" | cut -d'|' -f1) + if [ -f "$TEMP_DIR/rce_upload_attempts.txt" ]; then + mapfile -t rce_ips < <(awk '{print $2}' "$TEMP_DIR/rce_upload_attempts.txt" | cut -d'|' -f1) + for ip in "${rce_ips[@]}"; do threat_ips_rce["$ip"]=1; done + fi - [ -f "$TEMP_DIR/login_bruteforce_attempts.txt" ] && while read -r ip; do - threat_ips_login["$ip"]=1 - done < <(awk '{print $2}' "$TEMP_DIR/login_bruteforce_attempts.txt" | cut -d'|' -f1) + if [ -f "$TEMP_DIR/login_bruteforce_attempts.txt" ]; then + mapfile -t login_ips < <(awk '{print $2}' "$TEMP_DIR/login_bruteforce_attempts.txt" | cut -d'|' -f1) + for ip in "${login_ips[@]}"; do threat_ips_login["$ip"]=1; done + fi - [ -f "$TEMP_DIR/suspicious_ua.txt" ] && while read -r ip; do - threat_ips_suspicious["$ip"]=1 - done < <(awk '{print $2}' "$TEMP_DIR/suspicious_ua.txt" | cut -d'|' -f1) + if [ -f "$TEMP_DIR/suspicious_ua.txt" ]; then + mapfile -t susp_ips < <(awk '{print $2}' "$TEMP_DIR/suspicious_ua.txt" | cut -d'|' -f1) + for ip in "${susp_ips[@]}"; do threat_ips_suspicious["$ip"]=1; done + fi - [ -f "$TEMP_DIR/rapid_fire_ips.txt" ] && while read -r ip; do - threat_ips_ddos["$ip"]=1 - done < <(awk '{print $2}' "$TEMP_DIR/rapid_fire_ips.txt") + if [ -f "$TEMP_DIR/rapid_fire_ips.txt" ]; then + mapfile -t ddos_ips < <(awk '{print $2}' "$TEMP_DIR/rapid_fire_ips.txt") + for ip in "${ddos_ips[@]}"; do threat_ips_ddos["$ip"]=1; done + fi # Parse count-based threat files [ -f "$TEMP_DIR/admin_probes.txt" ] && while read -r count ip; do