CRITICAL FIX: Add error handling to all file reads

Multiple lines: Protected all file reads with error handling
- Line 508: parsed_logs.txt wc -l with 2>/dev/null || echo 0
- Line 642: classified_bots.txt wc -l with 2>/dev/null || echo 0
- Line 1627: classified_bots.txt cat with 2>/dev/null
- Line 1913: parsed_logs.txt cat with 2>/dev/null
- Line 1967: parsed_logs.txt cat with 2>/dev/null
- Lines 2004, 2008, 2014: classified_bots.txt cats with 2>/dev/null and || true
- Lines 1354, 1380: attack_vectors_raw.txt reads with conditional checks

This prevents script exit when files don't exist due to set -e behavior.
This commit is contained in:
Developer
2026-04-23 20:03:35 -04:00
parent 54e4d5b67f
commit a805676be5
+33 -31
View File
@@ -505,7 +505,7 @@ parse_logs() {
fi
local line_count
line_count=$(wc -l < "$TEMP_DIR/parsed_logs.txt")
line_count=$(wc -l < "$TEMP_DIR/parsed_logs.txt" 2>/dev/null || echo "0")
local file_size_kb
file_size_kb=$(du -k "$TEMP_DIR/parsed_logs.txt" 2>/dev/null | cut -f1 || echo "0")
@@ -639,7 +639,7 @@ classify_bots() {
fi
local classified_count
classified_count=$(wc -l < "$TEMP_DIR/classified_bots.txt")
classified_count=$(wc -l < "$TEMP_DIR/classified_bots.txt" 2>/dev/null || echo "0")
local file_size_kb
file_size_kb=$(du -k "$TEMP_DIR/classified_bots.txt" 2>/dev/null | cut -f1 || echo "0")
@@ -1351,33 +1351,35 @@ analyze_domain_targeting_percentage() {
printf "%s|%d|%d\n", domain, ip_count, request_count_per_domain[domain]
}
}
' "$TEMP_DIR/attack_vectors_raw.txt" "$TEMP_DIR/parsed_logs.txt" | sort -t'|' -k3 -rn > "$TEMP_DIR/domain_targeting.txt"
' <([ -f "$TEMP_DIR/attack_vectors_raw.txt" ] && cat "$TEMP_DIR/attack_vectors_raw.txt" || echo "") "$TEMP_DIR/parsed_logs.txt" | sort -t'|' -k3 -rn > "$TEMP_DIR/domain_targeting.txt" || true
# Also create per-domain attack type breakdown
# Format: domain|attack_type|ip|count
awk -F'|' '
{
ip = $1
domain = $2
attack_type = $5
if [ -f "$TEMP_DIR/attack_vectors_raw.txt" ]; then
awk -F'|' '
{
ip = $1
domain = $2
attack_type = $5
# Store as domain -> attack_type -> ip -> count
attack_data[domain][attack_type][ip]++
attack_totals[domain][attack_type]++
}
END {
for (domain in attack_data) {
domain_file = tmpdir "/domain_attacks_" domain ".txt"
for (attack_type in attack_data[domain]) {
total = attack_totals[domain][attack_type]
for (ip in attack_data[domain][attack_type]) {
count = attack_data[domain][attack_type][ip]
printf "%s|%d|%d\n", attack_type "|" ip, count, total
# Store as domain -> attack_type -> ip -> count
attack_data[domain][attack_type][ip]++
attack_totals[domain][attack_type]++
}
END {
for (domain in attack_data) {
domain_file = tmpdir "/domain_attacks_" domain ".txt"
for (attack_type in attack_data[domain]) {
total = attack_totals[domain][attack_type]
for (ip in attack_data[domain][attack_type]) {
count = attack_data[domain][attack_type][ip]
printf "%s|%d|%d\n", attack_type "|" ip, count, total
}
}
}
}
}
' < "$TEMP_DIR/attack_vectors_raw.txt"
' -v tmpdir="$TEMP_DIR" < "$TEMP_DIR/attack_vectors_raw.txt"
fi
print_success "Domain attack pattern analysis complete"
}
@@ -1624,13 +1626,13 @@ analyze_time_series() {
print_info "Analyzing time-series patterns..."
# Extract hourly bot traffic
cat "$TEMP_DIR/classified_bots.txt" | awk -F'|' '$9 != "unknown" {
cat "$TEMP_DIR/classified_bots.txt" 2>/dev/null | awk -F'|' '$9 != "unknown" {
timestamp = $8
if (match(timestamp, /([0-9]{2})\/([A-Za-z]{3})\/([0-9]{4}):([0-9]{2}):([0-9]{2}):([0-9]{2})/, ts)) {
hour = ts[4]
print hour
}
}' | sort | uniq -c > "$TEMP_DIR/hourly_bot_traffic.txt"
}' | sort | uniq -c > "$TEMP_DIR/hourly_bot_traffic.txt" || true
# Extract hourly attack traffic
if [ -f "$TEMP_DIR/attack_vectors_raw.txt" ]; then
@@ -1910,7 +1912,7 @@ detect_false_positives() {
print_info "Detecting legitimate services (false positives)..."
# Known monitoring service patterns and legitimate CDNs
cat "$TEMP_DIR/parsed_logs.txt" | awk -F'|' '{
cat "$TEMP_DIR/parsed_logs.txt" 2>/dev/null | awk -F'|' '{
ip = $1
domain = $2
url = $3
@@ -1964,7 +1966,7 @@ generate_statistics() {
# OPTIMIZATION: Use single-pass AWK to generate multiple stats from parsed logs
# This reads the uncompressed file ONCE instead of 4+ separate reads
cat "$TEMP_DIR/parsed_logs.txt" | awk -F'|' -v tmpdir="$TEMP_DIR" '
cat "$TEMP_DIR/parsed_logs.txt" 2>/dev/null | awk -F'|' -v tmpdir="$TEMP_DIR" '
{
# Count by domain (for top sites)
domains[$2]++
@@ -2001,17 +2003,17 @@ generate_statistics() {
sort -rn "$TEMP_DIR/top_urls_raw.txt" | head -5 > "$TEMP_DIR/top_urls.txt"
# Top 5 bots by request count (single decompression)
cat "$TEMP_DIR/classified_bots.txt" | awk -F'|' '$9 != "unknown" {print $10}' | \
sort | uniq -c | sort -rn | head -5 > "$TEMP_DIR/top_bots.txt"
cat "$TEMP_DIR/classified_bots.txt" 2>/dev/null | awk -F'|' '$9 != "unknown" {print $10}' | \
sort | uniq -c | sort -rn | head -5 > "$TEMP_DIR/top_bots.txt" || true
# Traffic breakdown by bot type (single decompression)
cat "$TEMP_DIR/classified_bots.txt" | awk -F'|' '{print $9}' | \
sort | uniq -c | sort -rn > "$TEMP_DIR/traffic_breakdown.txt"
cat "$TEMP_DIR/classified_bots.txt" 2>/dev/null | awk -F'|' '{print $9}' | \
sort | uniq -c | sort -rn > "$TEMP_DIR/traffic_breakdown.txt" || true
# Per-domain traffic sources (OPTIMIZED: read uncompressed file once, use grep)
if [ -f "$TEMP_DIR/all_domains.txt" ]; then
# Create indexed bot traffic file (decompress once)
cat "$TEMP_DIR/classified_bots.txt" | awk -F'|' '{print $2"|"$9}' > "$TEMP_DIR/domain_bot_types.txt"
cat "$TEMP_DIR/classified_bots.txt" 2>/dev/null | awk -F'|' '{print $2"|"$9}' > "$TEMP_DIR/domain_bot_types.txt" || true
while read -r domain; do
echo "$domain" > "$TEMP_DIR/domain_${domain}_stats.txt"