Simple SIEM — Log Analyzer + Threat Dashboard
A small SIEM built for a 3-endpoint environment: a web server, a firewall/router, and an SSH authentication log source. A Python script parses and correlates suspicious activity across all three; a static HTML/CSS/JS dashboard visualizes the resulting alerts and malicious IPs.
siem_project/
├── logs/
│ ├── endpoint1_web_access.log # sample nginx-style access log
│ ├── endpoint2_firewall.log # sample SRC/DST/PORT/ACTION firewall log
│ └── endpoint3_auth.log # sample sshd auth log
├── siem_analyzer.py # Part 1 — the analyzer
├── dashboard/
│ ├── index.html # Part 2 — the dashboard
│ ├── style.css
│ ├── script.js
│ ├── siem_report.json # generated by siem_analyzer.py
│ └── data.js # generated by siem_analyzer.py (offline fallback)
├── README.md # this file
└── PUBLISH_TO_GHAYMAH.md # Part 3 — publishing guide
Part 1 — siem_analyzer.py
Pure standard library, no pip install needed.
python3 siem_analyzer.py
# or point it at different files:
python3 siem_analyzer.py --logs-dir ./logs --out-dir ./dashboard
It parses all three log formats, runs the detection rules below, prints a
console summary, and writes siem_report.json + data.js into dashboard/.
Detection rules
| Rule | Source | Logic | Severity |
|---|---|---|---|
SQL_INJECTION / XSS / PATH_TRAVERSAL / COMMAND_INJECTION |
web | Regex signatures against the (URL-decoded) request path | critical/high |
RECON_SCANNER |
web | ≥3 requests with a known scanner user-agent (sqlmap, nikto, nmap…) | medium |
HIGH_REQUEST_RATE |
web | ≥15 requests from one IP within 60s | medium/high |
PORT_SCAN |
firewall | ≥8 distinct destination ports from one IP within 120s | high |
BRUTE_FORCE_SSH |
auth | ≥5 failed logins from one IP within 300s | high |
ACCOUNT_COMPROMISE_SUSPECTED |
auth | A successful login from an IP right after it triggered a brute-force alert | critical |
BLACKLISTED_IP_ACTIVITY |
any | IP matches a seeded threat-intel list | critical |
MULTI_VECTOR_ATTACK |
correlation | Same source IP triggered alerts on ≥2 different endpoints | critical |
That last rule is the actual point of a SIEM: no single log tells the whole
story, but seeing the same IP port-scan the firewall, brute-force SSH,
and throw SQLi at the web app is what turns three noisy logs into one
clear "this IP is attacking us" signal. All thresholds live in the
THRESHOLDS dict at the top of the script if you want to tune them.
Part 2 — the dashboard
Open dashboard/index.html directly in a browser — it works out of the box
because it falls back to the embedded data.js snapshot. For the "live"
experience (auto re-fetching siem_report.json when you click Refresh),
serve the folder instead:
cd dashboard
python3 -m http.server 8000
# open http://localhost:8000
What's on it:
- Stat cards — total alerts and the severity breakdown.
- Threat Radar — the top malicious IPs plotted by score (closer to center = more dangerous); a critical IP gets a pulsing ring.
- Severity Mix — a proportional bar of critical/high/medium/low.
- Top Malicious IPs — ranked list with a relative-score bar.
- Alerts table — searchable, filterable by severity/endpoint; click a row to expand the raw log line(s) behind that alert.
Re-running siem_analyzer.py regenerates both output files — refresh the
page (or click the in-app Refresh button if you're on a local server) to
see updated results.
A note on why the table doesn't use innerHTML
Alert descriptions and evidence are literally attacker-supplied log text —
some of the sample data contains a real <script> payload. script.js
builds every row with createElement/textContent, never string-built
HTML, so the console can't be XSS'd by the very payloads it's reporting on.
Regenerating with your own logs
Swap the three files in logs/ for real exports (keep the same filenames,
or pass --logs-dir) and adjust the regexes in siem_analyzer.py if your
log format differs from the nginx / iptables-style / sshd formats assumed
here.