COMPREHENSIVE FIX: pipefail grep errors + UUOC patterns

CRITICAL FIXES (set -eo pipefail safety):
Lines 1517, 1522, 1527, 1533, 1546: detect_server_ips() grep commands
- Added || true to all grep calls that could find no matches
- Without this, grep returns 1 on empty results, causing script exit

Lines 2277, 3654, 4179: Additional grep without error handling
- Line 2277: private IP counting - added || true to grep
- Line 3654: domain extraction - added || echo "" fallback
- Line 4179: domain log filtering - added || true to grep

EFFICIENCY IMPROVEMENTS (remove UUOC - Useless Use of Cat):
Lines 1471, 1477, 1481, 1487: detect_botnets() function
- Replaced: cat file | awk ...
- With: awk ... < file (direct file input)
- Eliminates unnecessary process spawning
- More efficient and standard practice

IMPACT:
- Script will no longer crash when grep finds no matches
- Cleaner, more efficient code following bash best practices
- All pipefail edge cases now handled safely
This commit is contained in:
Developer
2026-04-23 18:30:40 -04:00
parent 907e90f78a
commit 50a996bce3
+13 -13
View File
@@ -1468,23 +1468,23 @@ detect_botnets() {
# Group IPs by similar behavior patterns # Group IPs by similar behavior patterns
# Pattern 1: Multiple IPs hitting same URLs in coordinated manner # Pattern 1: Multiple IPs hitting same URLs in coordinated manner
cat "$TEMP_DIR/parsed_logs.txt" | awk -F'|' '{print $1"|"$3}' | \ awk -F'|' '{print $1"|"$3}' < "$TEMP_DIR/parsed_logs.txt" | \
sort | uniq -c | awk '$1 > 10 {print $2}' | \ sort | uniq -c | awk '$1 > 10 {print $2}' | \
cut -d'|' -f2 | sort | uniq -c | sort -rn | \ cut -d'|' -f2 | sort | uniq -c | sort -rn | \
awk '$1 > 5 {print $2}' > "$TEMP_DIR/coordinated_urls.txt" awk '$1 > 5 {print $2}' > "$TEMP_DIR/coordinated_urls.txt"
# Pattern 2: IPs with similar User-Agents hitting multiple domains # Pattern 2: IPs with similar User-Agents hitting multiple domains
cat "$TEMP_DIR/parsed_logs.txt" | awk -F'|' '{print $1"|"$6}' | \ awk -F'|' '{print $1"|"$6}' < "$TEMP_DIR/parsed_logs.txt" | \
sort | uniq > "$TEMP_DIR/ip_ua_pairs.txt" sort | uniq > "$TEMP_DIR/ip_ua_pairs.txt"
# Pattern 3: Detect IP ranges (Class C networks) with suspicious activity # Pattern 3: Detect IP ranges (Class C networks) with suspicious activity
cat "$TEMP_DIR/parsed_logs.txt" | awk -F'|' '{print $1}' | \ awk -F'|' '{print $1}' < "$TEMP_DIR/parsed_logs.txt" | \
awk -F'.' '{print $1"."$2"."$3".0/24"}' | \ awk -F'.' '{print $1"."$2"."$3".0/24"}' | \
sort | uniq -c | sort -rn | awk '$1 > 20' > "$TEMP_DIR/suspicious_networks.txt" sort | uniq -c | sort -rn | awk '$1 > 20' > "$TEMP_DIR/suspicious_networks.txt"
# Pattern 4: Rapid fire requests (DDoS indicators) # Pattern 4: Rapid fire requests (DDoS indicators)
# Extract timestamp and count requests per IP per minute # Extract timestamp and count requests per IP per minute
cat "$TEMP_DIR/parsed_logs.txt" | awk -F'|' '{ awk -F'|' '{
ip = $1 ip = $1
timestamp = $8 timestamp = $8
# Extract date/time components (handles format: DD/MMM/YYYY:HH:MM:SS) # Extract date/time components (handles format: DD/MMM/YYYY:HH:MM:SS)
@@ -1493,7 +1493,7 @@ detect_botnets() {
time_key = ts[3] ts[2] ts[1] "_" ts[4] ts[5] time_key = ts[3] ts[2] ts[1] "_" ts[4] ts[5]
print ip "|" time_key print ip "|" time_key
} }
}' | \ }' < "$TEMP_DIR/parsed_logs.txt" | \
sort | uniq -c | \ sort | uniq -c | \
awk '$1 > 50 {print $1 " " $2}' | \ awk '$1 > 50 {print $1 " " $2}' | \
awk -F'|' '{print $1}' | \ awk -F'|' '{print $1}' | \
@@ -1514,23 +1514,23 @@ detect_server_ips() {
# Method 1: Get all IPs from network interfaces # Method 1: Get all IPs from network interfaces
if command -v hostname >/dev/null 2>&1; then if command -v hostname >/dev/null 2>&1; then
hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' >> "$TEMP_DIR/server_ips.txt" hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' >> "$TEMP_DIR/server_ips.txt" || true
fi fi
# Method 2: Parse ip addr output # Method 2: Parse ip addr output
if command -v ip >/dev/null 2>&1; then if command -v ip >/dev/null 2>&1; then
ip addr show 2>/dev/null | grep -oP 'inet \K[\d.]+' >> "$TEMP_DIR/server_ips.txt" ip addr show 2>/dev/null | grep -oP 'inet \K[\d.]+' >> "$TEMP_DIR/server_ips.txt" || true
fi fi
# Method 3: Try ifconfig as fallback # Method 3: Try ifconfig as fallback
if command -v ifconfig >/dev/null 2>&1; then if command -v ifconfig >/dev/null 2>&1; then
ifconfig 2>/dev/null | grep -oP 'inet (addr:)?\K[\d.]+' >> "$TEMP_DIR/server_ips.txt" ifconfig 2>/dev/null | grep -oP 'inet (addr:)?\K[\d.]+' >> "$TEMP_DIR/server_ips.txt" || true
fi fi
# Method 4: Get public IP from external services (with timeout) # Method 4: Get public IP from external services (with timeout)
# Try multiple services for reliability # Try multiple services for reliability
for service in "ifconfig.me/ip" "icanhazip.com" "ipecho.net/plain" "api.ipify.org"; do for service in "ifconfig.me/ip" "icanhazip.com" "ipecho.net/plain" "api.ipify.org"; do
public_ip=$(curl -s --max-time 3 "$service" 2>/dev/null | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$') public_ip=$(curl -s --max-time 3 "$service" 2>/dev/null | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' || true)
if [ -n "$public_ip" ]; then if [ -n "$public_ip" ]; then
echo "$public_ip" >> "$TEMP_DIR/server_ips.txt" echo "$public_ip" >> "$TEMP_DIR/server_ips.txt"
break break
@@ -1543,7 +1543,7 @@ detect_server_ips() {
fi fi
# Remove duplicates and empty lines # Remove duplicates and empty lines
sort -u "$TEMP_DIR/server_ips.txt" | grep -v '^$' > "$TEMP_DIR/server_ips_final.txt" sort -u "$TEMP_DIR/server_ips.txt" | grep -v '^$' > "$TEMP_DIR/server_ips_final.txt" || true
mv "$TEMP_DIR/server_ips_final.txt" "$TEMP_DIR/server_ips.txt" mv "$TEMP_DIR/server_ips_final.txt" "$TEMP_DIR/server_ips.txt"
server_ip_count=$(wc -l < "$TEMP_DIR/server_ips.txt" 2>/dev/null || echo 0) server_ip_count=$(wc -l < "$TEMP_DIR/server_ips.txt" 2>/dev/null || echo 0)
@@ -2274,7 +2274,7 @@ generate_report() {
bot_requests=$(cat "$TEMP_DIR/classified_bots.txt" | awk -F'|' '$9 != "unknown"' | wc -l) bot_requests=$(cat "$TEMP_DIR/classified_bots.txt" | awk -F'|' '$9 != "unknown"' | wc -l)
# Count private/internal IPs (excluded from threat analysis) # Count private/internal IPs (excluded from threat analysis)
private_ips=$(cat "$TEMP_DIR/parsed_logs.txt" | awk -F'|' '{print $1}' | sort -u | grep -E '^(127\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|169\.254\.)' | wc -l) private_ips=$(cat "$TEMP_DIR/parsed_logs.txt" | awk -F'|' '{print $1}' | sort -u | grep -E '^(127\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|169\.254\.)' || true | wc -l)
# Count server's own IPs in the logs # Count server's own IPs in the logs
server_ip_hits=0 server_ip_hits=0
@@ -3651,7 +3651,7 @@ show_detailed_recommendations() {
awk -F'|' '$1 >= 70 {printf " • %s (score: %s)\n", $2, $1}' "$TEMP_DIR/threat_scores.txt" 2>/dev/null | head -10 awk -F'|' '$1 >= 70 {printf " • %s (score: %s)\n", $2, $1}' "$TEMP_DIR/threat_scores.txt" 2>/dev/null | head -10
;; ;;
htaccess_domain) htaccess_domain)
local target_domain=$(echo "$action_title" | grep -oP 'to \K[^ ]+' 2>/dev/null) local target_domain=$(echo "$action_title" | grep -oP 'to \K[^ ]+' 2>/dev/null || echo "")
echo "Target Domain: $target_domain" echo "Target Domain: $target_domain"
if [ -s "$TEMP_DIR/domain_threats_sorted.txt" ]; then if [ -s "$TEMP_DIR/domain_threats_sorted.txt" ]; then
grep "^$target_domain|" "$TEMP_DIR/domain_threats_sorted.txt" 2>/dev/null | while IFS='|' read -r domain total_req bot_req bot_pct high_risk attacks ips; do grep "^$target_domain|" "$TEMP_DIR/domain_threats_sorted.txt" 2>/dev/null | while IFS='|' read -r domain total_req bot_req bot_pct high_risk attacks ips; do
@@ -4176,7 +4176,7 @@ execute_htaccess_domain_blocking() {
print_info "Adding bot blocking rules..." print_info "Adding bot blocking rules..."
# Get high-risk IPs for this domain # Get high-risk IPs for this domain
local block_ips=$(cat "$TEMP_DIR/parsed_logs.txt" 2>/dev/null | grep "^[^|]*|$target_domain|" 2>/dev/null | cut -d'|' -f1 | sort -u | while read ip; do local block_ips=$(cat "$TEMP_DIR/parsed_logs.txt" 2>/dev/null | grep "^[^|]*|$target_domain|" 2>/dev/null || true | cut -d'|' -f1 | sort -u | while read ip; do
# Check if this IP has high threat score # Check if this IP has high threat score
if grep -q "|$ip$" "$TEMP_DIR/threat_scores.txt" 2>/dev/null; then if grep -q "|$ip$" "$TEMP_DIR/threat_scores.txt" 2>/dev/null; then
local score=$(grep "|$ip$" "$TEMP_DIR/threat_scores.txt" 2>/dev/null | cut -d'|' -f1 || echo "0") local score=$(grep "|$ip$" "$TEMP_DIR/threat_scores.txt" 2>/dev/null | cut -d'|' -f1 || echo "0")