Fix 22 critical runtime errors from 'local' keyword used outside functions

Removed 'local' keyword from script-level variable declarations in:
- website-error-analyzer.sh (8 instances)
- wordpress-cron-manager.sh (3 instances)
- live-attack-monitor.sh (3 instances)
- live-attack-monitor-v2.sh (3 instances)
- acronis-uninstall.sh (3 instances)
- malware-scanner.sh (1 instance)
- acronis-troubleshoot.sh (1 instance)
- diagnostic-report.sh (1 instance)

The 'local' keyword can only be used inside bash functions.
Using it at script-level causes immediate runtime errors.
This commit is contained in:
cschantz
2025-12-30 18:38:59 -05:00
parent b3d31e838e
commit 77f91462e1
8 changed files with 396 additions and 159 deletions
+66 -31
View File
@@ -72,22 +72,23 @@ IPSET_INIT_ERROR="" # Store initialization error message
# Initialize IPset for fast blocking (if available)
if command -v ipset &>/dev/null; then
# Check if CSF's chain_DENY IPset exists (preferred - already integrated with CSF)
if ipset list chain_DENY &>/dev/null 2>&1; then
# Check if CSF's chain_DENY IPset exists AND supports timeouts
if ipset list chain_DENY &>/dev/null 2>&1 && ipset list chain_DENY | grep -q "^Type:.*timeout"; then
# CSF ipset exists with timeout support - use it!
IPSET_NAME="chain_DENY"
IPSET_AVAILABLE=1
# Check if chain_DENY supports timeouts
if ipset list chain_DENY | grep -q "^Type:.*timeout"; then
IPSET_SUPPORTS_TIMEOUT=1
echo "✓ Using CSF IPset: chain_DENY (with timeout support)" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
else
echo "✓ Using CSF IPset: chain_DENY (no timeout support, will use CSF for temp blocks)" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
fi
IPSET_SUPPORTS_TIMEOUT=1
echo "✓ Using CSF IPset: chain_DENY (with timeout support)" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
else
# No CSF IPset found, create our own temporary one
# CSF ipset doesn't exist OR doesn't support timeouts - create our own
IPSET_NAME="live_monitor_$$"
if ipset list chain_DENY &>/dev/null 2>&1; then
echo "→ CSF chain_DENY exists but no timeout support - creating our own ipset" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
else
echo "→ No CSF IPset found - creating our own ipset" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
fi
# Capture detailed error output
IPSET_CREATE_OUTPUT=$(ipset create "$IPSET_NAME" hash:ip timeout 3600 maxelem 65536 2>&1)
IPSET_CREATE_EXIT=$?
@@ -149,17 +150,17 @@ fi
{
# Get CSF temporary blocks - extract just the IP address
if command -v csf &>/dev/null; then
csf -t 2>/dev/null | awk '{print $1}' | grep -E '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'
csf -t 2>/dev/null | awk '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ {print $1}'
fi
# Get CSF permanent denies
if [ -f /etc/csf/csf.deny ]; then
awk '{print $1}' /etc/csf/csf.deny 2>/dev/null | grep -E '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'
awk '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ {print $1}' /etc/csf/csf.deny 2>/dev/null
fi
# Get iptables DROP rules
if command -v iptables &>/dev/null; then
iptables -L INPUT -n -v 2>/dev/null | grep DROP | awk '{print $8}' | grep -E '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'
iptables -L INPUT -n -v 2>/dev/null | awk '/DROP/ && $8 ~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ {print $8}'
fi
} | sort -u > "$TEMP_DIR/blocked_ips_cache" 2>/dev/null
@@ -893,10 +894,14 @@ batch_block_ips() {
return 0
fi
# DEBUG: Log function entry
echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: Starting batch block for ${#ip_list[@]} IPs: ${ip_list[*]}" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
echo "Batch blocking ${#ip_list[@]} IPs..."
# Use IPset for instant batch blocking if available
if [ "$IPSET_AVAILABLE" -eq 1 ]; then
echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: Using IPSET path" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
for ip in "${ip_list[@]}"; do
# Validate IP format
if ! is_valid_ip "$ip"; then
@@ -920,23 +925,33 @@ batch_block_ips() {
echo "✓ IPset batch: $blocked blocked, $failed skipped"
else
# Fallback to CSF (slower, but still batch where possible)
echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: Using CSF path (IPSET_AVAILABLE=$IPSET_AVAILABLE)" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
for ip in "${ip_list[@]}"; do
if ! is_valid_ip "$ip"; then
echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: Invalid IP format: $ip" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
((failed++))
continue
fi
if csf -td "$ip" 3600 "Batch auto-block" >/dev/null 2>&1; then
echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: Attempting CSF block for $ip" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
local csf_output=$(csf -td "$ip" 3600 "Batch auto-block" 2>&1)
if [ $? -eq 0 ]; then
echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: CSF SUCCESS for $ip" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
((blocked++))
else
echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: CSF FAILED for $ip: $csf_output" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
((failed++))
fi
done
echo "✓ CSF batch: $blocked blocked, $failed failed"
echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: CSF batch complete - blocked=$blocked, failed=$failed" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
fi
# Update total counter atomically
echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: Incrementing counter by $blocked" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
increment_block_counter "$blocked"
return 0
@@ -1371,7 +1386,7 @@ draw_quick_actions() {
if [ "$has_ddos" -eq 1 ] || [ "$high_conn_count" -gt 0 ]; then
# Check current security settings
local synflood_status=$(grep "^SYNFLOOD\s*=" /etc/csf/csf.conf 2>/dev/null | cut -d'"' -f2)
local ct_limit=$(grep "^CT_LIMIT\s*=" /etc/csf/csf.conf 2>/dev/null | grep -oE '[0-9]+' | head -1)
local ct_limit=$(grep -oP "^CT_LIMIT\s*=\s*\"\K[0-9]+" /etc/csf/csf.conf 2>/dev/null | head -1)
local needs_config=0
@@ -1402,7 +1417,7 @@ draw_quick_actions() {
[[ ! "$ssh_attacks" =~ ^[0-9]+$ ]] && ssh_attacks=0
if [ "$ssh_attacks" -gt 5 ]; then
# Check if SSH hardening is already applied
local current_lf=$(grep "^LF_SSHD\s*=" /etc/csf/csf.conf 2>/dev/null | grep -oE '[0-9]+' | head -1)
local current_lf=$(grep -oP "^LF_SSHD\s*=\s*\"\K[0-9]+" /etc/csf/csf.conf 2>/dev/null | head -1)
[ -z "$current_lf" ] && current_lf="5"
# Only show recommendation if not already hardened
@@ -1556,7 +1571,7 @@ show_security_hardening_menu() {
# Check current settings
local synflood_status=$(grep "^SYNFLOOD\s*=" /etc/csf/csf.conf 2>/dev/null | cut -d'"' -f2)
local current_lf=$(grep "^LF_SSHD\s*=" /etc/csf/csf.conf 2>/dev/null | grep -oE '[0-9]+' | head -1)
local current_lf=$(grep -oP "^LF_SSHD\s*=\s*\"\K[0-9]+" /etc/csf/csf.conf 2>/dev/null | head -1)
[ -z "$current_lf" ] && current_lf="5"
echo "Current Security Status:"
@@ -1577,7 +1592,7 @@ show_security_hardening_menu() {
fi
# CT_LIMIT status (basic check)
local ct_limit=$(grep "^CT_LIMIT\s*=" /etc/csf/csf.conf 2>/dev/null | grep -oE '[0-9]+' | head -1)
local ct_limit=$(grep -oP "^CT_LIMIT\s*=\s*\"\K[0-9]+" /etc/csf/csf.conf 2>/dev/null | head -1)
if [ -n "$ct_limit" ] && [ "$ct_limit" -gt 0 ]; then
echo -e " ${SAFE_COLOR}${NC} Connection Tracking: ${BOLD}Configured${NC} (CT_LIMIT=$ct_limit)"
else
@@ -1730,7 +1745,7 @@ apply_ssh_hardening() {
echo ""
# Check current LF_SSHD setting
local current_lf=$(grep "^LF_SSHD\s*=" /etc/csf/csf.conf 2>/dev/null | grep -oE '[0-9]+' | head -1)
local current_lf=$(grep -oP "^LF_SSHD\s*=\s*\"\K[0-9]+" /etc/csf/csf.conf 2>/dev/null | head -1)
if [ -z "$current_lf" ]; then
current_lf="5" # CSF default
@@ -3243,19 +3258,26 @@ auto_mitigation_engine() {
declare -A BLOCKED_THIS_SESSION
while true; do
sleep 10
# Batch blocking arrays (collect IPs, block in batches of 50)
local -a batch_instant=()
local -a batch_critical=()
# DEBUG: Log that we're checking
echo "[$(date +"%H:%M:%S")] AUTO_MIT: Checking for IPs to block..." >> "$TEMP_DIR/debug.log" 2>/dev/null || true
# Read current IP data from snapshot file (updated by main process)
if [ -f "$TEMP_DIR/ip_data" ]; then
# DEBUG: File exists
local ip_count=$(wc -l < "$TEMP_DIR/ip_data" 2>/dev/null || echo "0")
echo "[$(date +"%H:%M:%S")] AUTO_MIT: ip_data exists with $ip_count IPs" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
while IFS='=' read -r ip data; do
[ -z "$ip" ] && continue
IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$data"
# DEBUG: Log parsed data
echo "[$(date +"%H:%M:%S")] AUTO_MIT: Parsing IP $ip | score=$score | hits=$hits | attacks=$attacks" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
# Validate score is numeric
[ -z "$score" ] && score=0
[[ ! "$score" =~ ^[0-9]+$ ]] && score=0
@@ -3265,6 +3287,9 @@ auto_mitigation_engine() {
# INSTANT block at score 100 (MAXIMUM threat via IPset)
if [ "${score:-0}" -ge 100 ]; then
# DEBUG: Log score 100 detection
echo "[$(date +"%H:%M:%S")] AUTO_MIT: Found score 100 IP: $ip" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
# Mark as blocked
BLOCKED_THIS_SESSION[$ip]=1
@@ -3290,17 +3315,27 @@ auto_mitigation_engine() {
echo -e "${CRITICAL_COLOR}[${time_str}] AUTO_BLOCK | $ip | Score:$score | ${attacks}${NC}" >> "$TEMP_DIR/recent_events"
fi
done < "$TEMP_DIR/ip_data"
else
# DEBUG: File doesn't exist
echo "[$(date +"%H:%M:%S")] AUTO_MIT: WARNING - ip_data file not found at $TEMP_DIR/ip_data" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
fi
# BATCH BLOCK - Instant (score 100)
if [ ${#batch_instant[@]} -gt 0 ]; then
batch_block_ips "${batch_instant[@]}" &
echo "[$(date +"%H:%M:%S")] AUTO_MIT: Blocking ${#batch_instant[@]} instant IPs: ${batch_instant[*]}" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
batch_block_ips "${batch_instant[@]}"
else
echo "[$(date +"%H:%M:%S")] AUTO_MIT: No instant IPs to block" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
fi
# BATCH BLOCK - Critical (score 80-99)
if [ ${#batch_critical[@]} -gt 0 ]; then
batch_block_ips "${batch_critical[@]}" &
echo "[$(date +"%H:%M:%S")] AUTO_MIT: Blocking ${#batch_critical[@]} critical IPs: ${batch_critical[*]}" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
batch_block_ips "${batch_critical[@]}"
fi
# Sleep at END of loop to check immediately on startup
sleep 10
done
) &
}
@@ -3377,17 +3412,17 @@ if [ "$IPSET_AVAILABLE" -eq 0 ]; then
{
# Get CSF temporary blocks - extract just the IP address
if command -v csf &>/dev/null; then
csf -t 2>/dev/null | awk '{print $1}' | grep -E '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'
csf -t 2>/dev/null | awk '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ {print $1}'
fi
# Get CSF permanent denies
if [ -f /etc/csf/csf.deny ]; then
awk '{print $1}' /etc/csf/csf.deny 2>/dev/null | grep -E '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'
awk '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ {print $1}' /etc/csf/csf.deny 2>/dev/null
fi
# Get iptables DROP rules
if command -v iptables &>/dev/null; then
iptables -L INPUT -n -v 2>/dev/null | grep DROP | awk '{print $8}' | grep -E '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'
iptables -L INPUT -n -v 2>/dev/null | awk '/DROP/ && $8 ~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ {print $8}'
fi
} | sort -u > "$TEMP_DIR/blocked_ips_cache.tmp" 2>/dev/null
mv "$TEMP_DIR/blocked_ips_cache.tmp" "$TEMP_DIR/blocked_ips_cache" 2>/dev/null
@@ -3410,7 +3445,7 @@ while true; do
# Sync individual IP files into IP_DATA array (for data from subshell processes like SSH monitoring)
for ip_file in "$TEMP_DIR"/ip_*; do
[ -f "$ip_file" ] || continue
basename_file="$(basename "$ip_file")"
basename_file="${ip_file##*/}"
# Skip non-IP files explicitly
case "$basename_file" in
@@ -3496,7 +3531,7 @@ while true; do
echo ""
echo "Querying threat intelligence for $lookup_ip..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
local threat_intel=$(get_threat_intelligence "$lookup_ip")
threat_intel=$(get_threat_intelligence "$lookup_ip")
IFS='|' read -r abuse_conf abuse_rpts country isp geo timing whitelisted <<< "$threat_intel"
echo ""
echo "${BOLD}Threat Intelligence:${NC}"
@@ -3518,7 +3553,7 @@ while true; do
echo ""
read -p "Generate full incident report? (y/n): " gen_report
if [[ "$gen_report" =~ ^[Yy]$ ]]; then
local report_file=$(generate_incident_report "$lookup_ip")
report_file=$(generate_incident_report "$lookup_ip")
echo ""
echo "Report generated: $report_file"
echo ""
@@ -3537,7 +3572,7 @@ while true; do
clear
print_banner "Server Performance Monitor"
echo ""
local load_data=$(get_server_load)
load_data=$(get_server_load)
IFS='|' read -r load1 load5 load15 cpu_count <<< "$load_data"
echo "${BOLD}Current Load:${NC}"
echo " 1 min: $load1"