QA scan found duplicate show_progress function in analyze-historical-attacks.sh
that's already available in lib/common-functions.sh.
Changes:
- Added source for lib/common-functions.sh
- Removed local show_progress() definition
- Added comment noting function is now sourced
This reduces code duplication and ensures consistent progress display
across all toolkit scripts.
Bug Reports from User:
1. "line 162: count * 100 / total: division by 0"
2. Empty report - no IP details displayed, only headers
Root Causes:
Issue 1: Division by Zero (line 162)
- show_progress() called with total="unknown"
- Attempted: count * 100 / "unknown" → division error
- Happened when processing logs of unknown size
Issue 2: Empty Report Output
- ALL echo statements used >> "$OUTPUT_FILE" inside { } block
- The { } > "$OUTPUT_FILE" already redirects EVERYTHING to file
- Using >> INSIDE redirected block caused output to go nowhere
- Result: Only headers written, no IP data
Example of broken code (lines 280-390):
{
echo "Header" # Goes to file ✅
echo "Data" >> "$OUTPUT_FILE" # ❌ WRONG! Tries to append while already redirected
} > "$OUTPUT_FILE"
Fixes Applied:
1. show_progress() function (lines 159-168):
Before:
percent=$((count * 100 / total)) # Crashes if total="unknown"
After:
if [ "$total" = "unknown" ] || [ "$total" -eq 0 ]; then
echo "Processing: $count lines..." # No percentage
else
percent=$((count * 100 / total)) # Safe
fi
2. Removed ALL >> "$OUTPUT_FILE" inside output block:
- Used sed to remove 32 instances
- Now all echo statements write to stdout
- The { } > "$OUTPUT_FILE" captures everything correctly
Testing:
Before:
- Division by zero error ❌
- Empty report (no IP details) ❌
After:
- No division errors ✅
- Full report with IP details ✅
- Syntax validated ✅
Impact:
- Report now displays complete IP analysis
- Shows attack types, sample URLs, reputation
- No more math errors during processing
Bug Found During Logic Review:
The URL sample storage was supposed to keep max 3 URLs per IP,
but was actually storing 4 URLs.
Root Cause (lines 254-263):
The logic counted delimiters AFTER checking the limit:
url_count = delimiters in string # 0 for first URL, 1 for second, 2 for third
if url_count < 3: add URL # Allows 0,1,2 → stores 3 URLs ✅
But on 4th URL:
url_count = 2 (two delimiters)
if 2 < 3: add URL # TRUE! Stores 4th URL ❌
The check needs to count EXISTING URLs, not delimiters.
Fix Applied:
Count URLs correctly by adding 1 to delimiter count:
url_count = (delimiters + 1) # Actual URL count
if url_count < 3: add URL # Only adds if <3 URLs exist
Testing:
Before:
5 URLs attempted → stored 4 URLs ❌
After:
5 URLs attempted → stored 3 URLs ✅
/test1.php||/test2.php||/test3.php
URLs 4 and 5 correctly skipped
QA Check Results:
✅ No CRITICAL issues
✅ No syntax errors
✅ All logic tests pass
- 3 minor issues (duplicate function, no parameter validation)
These are acceptable for a tool script
Issue:
User reported: "it seems to just list all possible hits"
- Old format listed every individual attack hit
- No grouping or organization by IP
- Hard to understand what each IP actually did
- No reputation context
User Request:
"show an IP, saying what it did, saying how many times it did it,
and what its reputation is"
Solution:
Completely rewrote output format to group by IP with summaries:
New Output Format:
================================================================================
ATTACKING IPs - DETAILED BREAKDOWN
================================================================================
[1] 192.168.1.100
Attacks: 15 | Avg Score: 87 | Threat Level: CRITICAL
Attack Types: WEBSHELL(8), SQLI(5), XSS(2)
Reputation: AbuseIPDB 85% confidence (142 reports) | China
Sample Targets:
- /wp-admin/alfa-rex.php
- /admin.php?id=1' union select...
- /upload.php?file=../../../../etc/passwd
[2] 45.83.66.23
Attacks: 8 | Avg Score: 92 | Threat Level: CRITICAL
Attack Types: CMD(5), TRAVERSAL(3)
Sample Targets:
- /cgi-bin/admin.cgi?cmd=cat%20/etc/passwd
- /../../../etc/shadow
Changes Made:
1. Added IP-level tracking (lines 151-153):
- IP_ATTACK_DETAILS: Store all attack types per IP
- IP_ATTACK_COUNT: Count total attacks per IP
- IP_SAMPLE_URLS: Store first 3 sample URLs per IP
2. Track data during scan (lines 240-260):
- Aggregate attack types per IP
- Keep sample URLs for context
- Count occurrences of each attack type
3. New output section (lines 284-352):
- Sort IPs by cumulative threat score (worst first)
- Calculate average score per IP
- Count attack type occurrences: "SQLI(5), XSS(2)"
- Show reputation from AbuseIPDB (if available)
- Display sample target URLs for context
- Limit to top 50 attacking IPs
4. Improved summary stats (lines 360-381):
- Added "Unique attacking IPs" count
- Condensed attack type summary to top 10
- Removed redundant "Top Signatures" section
5. Source IP reputation library (line 30):
- Optional: loads get_threat_intelligence() if available
- Gracefully skips reputation if not available
Benefits:
✅ Clean per-IP summary (not a flood of individual hits)
✅ Shows what each IP did and how many times
✅ Includes reputation context from AbuseIPDB
✅ Sample URLs provide attack pattern examples
✅ Sorted by threat level (worst attackers first)
✅ Much easier to understand and act on
The code block writing to $OUTPUT_FILE was using 'local' variables
but was not inside a function. The 'local' keyword is only valid inside
functions in bash.
Fixed:
- Removed all 'local' keywords (changed to regular variables)
- Code is in global scope redirected to file, not in a function
- Variables are properly scoped within the { } block
This was causing errors:
line 190: local: can only be used in a function
line 203: local: can only be used in a function
etc.
Now all variables use proper global scope within the output redirection block.
✅ Syntax validated