diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..570d8c0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "workbench.colorCustomizations": { + "terminal.background": "#00000000", + "minimap.background": "#00000000", + "scrollbar.shadow": "#00000000" + } +} \ No newline at end of file diff --git a/ProjectMindmapv0.5.png b/ProjectMindmapv0.5.png new file mode 100644 index 0000000..f7e89e1 Binary files /dev/null and b/ProjectMindmapv0.5.png differ diff --git a/ProjectMindmapv0.5.png:Zone.Identifier b/ProjectMindmapv0.5.png:Zone.Identifier new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md index 9107d70..94f8d8c 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Common options: - `--limit N`: process only the first N lines. - `--format jsonl|csv`: output for enriched events (default: `jsonl`). - `--color auto|always|never`: terminal color policy. +- `--ai-malicious-report`: after CTI summarization, ask the LLM for a detailed malicious-activity report (saved under `reports/`). LLM request control: @@ -69,6 +70,7 @@ Create a `.env` (see variables below). Keys are optional; the tool runs offline - Enriched events: `data/processed/.jsonl` (or `.csv` with `--format csv`). - Reports: `data/processed/reports/report.txt` and `report.md` summarizing activity and suspicious IPs; may include a brief AI note if LLM is enabled. +- Malicious AI report (optional): `data/processed/reports/malicious_ai_report.txt|md` if `--ai-malicious-report` is used and malicious CTI signals are present. - CTI cache: `data/cache/cti_cache.json` (auto‑created and reused to minimize network calls). ## Testing @@ -80,6 +82,15 @@ Notes: - If you used the local venv above, run tests via `.venv/bin/pytest -q`. - A PyPDF2 deprecation warning may appear; it’s harmless and can be ignored. +## UI Dashboard + +An optional Streamlit dashboard is included for exploration and client-friendly viewing. + +- Install UI deps (already part of `requirements.txt`). +- Run the UI: `scripts/run_ui.sh` (or `streamlit run ui/app.py`). +- Select an enriched `.jsonl` file from `data/processed/` or upload one. +- View status distribution, sample enriched events, and CTI attributes. + ## Troubleshooting - `.txt` auto‑detection: the CLI reads a small sample and parses with `parse_line`. If none match, the file is copied as plain text rather than parsed as logs. diff --git a/data/processed-test/access_log.jsonl b/data/processed-test/access_log.jsonl new file mode 100644 index 0000000..90a2dfd --- /dev/null +++ b/data/processed-test/access_log.jsonl @@ -0,0 +1,185 @@ +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/assets/public/images/products/fan_facemask.jpg", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/assets/public/images/products/melon_bike.jpeg", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/assets/public/images/products/lemon_juice.jpg", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/assets/public/images/products/permafrost.jpg", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/assets/public/images/products/green_smoothie.jpg", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/assets/public/images/products/fruit_press.jpg", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/assets/public/images/products/eggfruit_juice.jpg", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/assets/public/images/products/carrot_juice.jpeg", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/assets/public/images/products/artwork2.jpg", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/socket.io/?EIO=4&transport=polling&t=PZvmEN_&sid=dCq-gfyMWN1lgOZ1AAAC", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/assets/public/images/products/banana_juice.jpg", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/assets/public/images/products/apple_pressings.jpg", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/assets/public/images/products/apple_juice.jpg", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/socket.io/?EIO=4&transport=polling&t=PZvmELD&sid=dCq-gfyMWN1lgOZ1AAAC", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/api/Quantitys/", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/rest/products/search?q=", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/api/Challenges/?name=Score%20Board", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/api/Challenges/?name=Score%20Board", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/rest/languages", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/socket.io/?EIO=4&transport=polling&t=PZvmEGF&sid=dCq-gfyMWN1lgOZ1AAAC", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "POST", "path": "/socket.io/?EIO=4&transport=polling&t=PZvmEG8&sid=dCq-gfyMWN1lgOZ1AAAC", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/rest/admin/application-configuration", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/assets/public/images/JuiceShop_Logo.png", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:45+00:00", "method": "GET", "path": "/rest/admin/application-configuration", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:44+00:00", "method": "GET", "path": "/rest/admin/application-version", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:44+00:00", "method": "GET", "path": "/rest/admin/application-configuration", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:44+00:00", "method": "GET", "path": "/rest/admin/application-version", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:44+00:00", "method": "GET", "path": "/socket.io/?EIO=4&transport=polling&t=PZvmECK", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:44+00:00", "method": "GET", "path": "/assets/i18n/en.json", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:44+00:00", "method": "GET", "path": "/styles.css", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:44+00:00", "method": "GET", "path": "/main.js", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:44+00:00", "method": "GET", "path": "/vendor.js", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:44+00:00", "method": "GET", "path": "/polyfills.js", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:44+00:00", "method": "GET", "path": "/runtime.js", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/", "proto": "TLSv1.3", "status": 304, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/socket.io/?EIO=4&transport=websocket&sid=JMIIi7MdmkRu-2SAAAAA", "proto": "TLSv1.3", "status": 101, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/assets/public/images/products/fan_facemask.jpg", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/assets/public/images/products/melon_bike.jpeg", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/assets/public/images/products/lemon_juice.jpg", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/assets/public/images/products/permafrost.jpg", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/assets/public/images/products/green_smoothie.jpg", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/assets/public/images/products/fruit_press.jpg", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/assets/public/images/products/eggfruit_juice.jpg", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/assets/public/images/products/carrot_juice.jpeg", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/assets/public/images/products/banana_juice.jpg", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/assets/public/images/products/artwork2.jpg", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/assets/public/images/products/apple_pressings.jpg", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/assets/public/images/products/apple_juice.jpg", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/socket.io/?EIO=4&transport=polling&t=PZvmDs7&sid=JMIIi7MdmkRu-2SAAAAA", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/api/Quantitys/", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/rest/products/search?q=", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/api/Challenges/?name=Score%20Board", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/api/Challenges/?name=Score%20Board", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/MaterialIcons-Regular.woff2", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/socket.io/?EIO=4&transport=polling&t=PZvmDh-&sid=JMIIi7MdmkRu-2SAAAAA", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "POST", "path": "/socket.io/?EIO=4&transport=polling&t=PZvmDhz&sid=JMIIi7MdmkRu-2SAAAAA", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:43+00:00", "method": "GET", "path": "/rest/languages", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:42+00:00", "method": "GET", "path": "/assets/public/images/JuiceShop_Logo.png", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:42+00:00", "method": "GET", "path": "/rest/admin/application-configuration", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:42+00:00", "method": "GET", "path": "/rest/admin/application-configuration", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:42+00:00", "method": "GET", "path": "/rest/admin/application-version", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:42+00:00", "method": "GET", "path": "/rest/admin/application-configuration", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:42+00:00", "method": "GET", "path": "/rest/admin/application-version", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:41+00:00", "method": "GET", "path": "/assets/i18n/en.json", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:41+00:00", "method": "GET", "path": "/socket.io/?EIO=4&transport=polling&t=PZvmDQz", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:41+00:00", "method": "GET", "path": "/assets/public/favicon_js.ico", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:40+00:00", "method": "GET", "path": "/styles.css", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:40+00:00", "method": "GET", "path": "/vendor.js", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:40+00:00", "method": "GET", "path": "/main.js", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:40+00:00", "method": "GET", "path": "/polyfills.js", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:40+00:00", "method": "GET", "path": "/runtime.js", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-30T06:53:39+00:00", "method": "GET", "path": "/", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:38:16+00:00", "method": "INDEX", "path": "/", "proto": "", "status": 301, "size": null, "ref": null, "ua": "nikto", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:38:16+00:00", "method": "", "path": "", "proto": "", "status": 400, "size": null, "ref": null, "ua": null, "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:38:15+00:00", "method": "TRACK", "path": "/", "proto": "", "status": 301, "size": null, "ref": null, "ua": "nikto", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:38:15+00:00", "method": "TRACK", "path": "/", "proto": "", "status": 301, "size": null, "ref": null, "ua": "nikto", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:38:15+00:00", "method": "TRACE", "path": "/", "proto": "", "status": 405, "size": null, "ref": null, "ua": "nikto", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:38:15+00:00", "method": "TRACE", "path": "/", "proto": "", "status": 405, "size": null, "ref": null, "ua": "nikto", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:38:15+00:00", "method": "PROPFIND", "path": "/", "proto": "", "status": 400, "size": null, "ref": null, "ua": "nikto", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:38:15+00:00", "method": "DEBUG", "path": "/", "proto": "", "status": 301, "size": null, "ref": null, "ua": "nikto", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:38:15+00:00", "method": "NVFYPLCD", "path": "/", "proto": "", "status": 301, "size": null, "ref": null, "ua": "nikto", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:38:15+00:00", "method": "OPTIONS", "path": "/", "proto": "", "status": 301, "size": null, "ref": null, "ua": "nikto", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:38:15+00:00", "method": "", "path": "", "proto": "", "status": 400, "size": null, "ref": null, "ua": null, "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:38:15+00:00", "method": "", "path": "", "proto": "", "status": 400, "size": null, "ref": null, "ua": null, "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:38:15+00:00", "method": "PUT", "path": "/nikto-test-dfmcL5fa.html", "proto": "", "status": 301, "size": null, "ref": null, "ua": "nikto", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/group", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/grid", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/greybox", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/green", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/Graphics", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/graphics", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/graph", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/grants", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/granted", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/grant", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/grafik", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/gracias", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/gr", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/gps", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/gprs", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/gpl", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/gpapp", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/gp", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/government", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/goto", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/googlebot", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/google_sitemap", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/google", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/goods_script", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/goods", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/gone", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/golf", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/gold", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/goaway", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/go", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/glossary", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/globes_admin", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/globals", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/globalnav", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/global.asax", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/global.asa", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/Global", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/global", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/glimpse", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/glance_config", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/gl", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/gitweb", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/git", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/gifts", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/giftregs", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/giftreg_manage", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:27+00:00", "method": "GET", "path": "/giftoptions", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/giftcert", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gift", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gifs", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gif", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gid", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gg", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gfx", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gfen", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gettxt", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/getout", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/getjobid", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/getFile.cfm", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/get-file", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/getfile", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/getconfig", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/getaccess", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/get_file", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/get", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gestione", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gestion", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gest", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/geronimo", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/german", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/geoip", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/geo", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gentoo", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/generic", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/generator", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/generateditems", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/general", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gen", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/geeklog", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gdform", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gccallback", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gbook", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gb", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gateway", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gate", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/garbage", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/ganglia", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gaming", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/Games", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/games", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gamercard", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/game", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gallery2", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gallery", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/galleries", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/galerie", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/galeria", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gaestebuch", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gadgets", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} +{"ip": "18.237.3.202", "time": "2025-08-29T12:36:26+00:00", "method": "GET", "path": "/gadget", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "dirbuster", "severity": "unknown", "iocs": ["18.237.3.202"], "rationale": "LLM disabled"} diff --git a/data/processed-test/new_log.jsonl b/data/processed-test/new_log.jsonl new file mode 100644 index 0000000..90debed --- /dev/null +++ b/data/processed-test/new_log.jsonl @@ -0,0 +1,15 @@ +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/sitemap.xml", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/sitemap.xml", "proto": "", "status": 301, "size": null, "ref": null, "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/sitemap.xml", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/91.0", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/sitemap.xml", "proto": "", "status": 301, "size": null, "ref": null, "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/91.0", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/sitemap.xml", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12A366 Safari/600.1.4", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/sitemap.xml", "proto": "", "status": 301, "size": null, "ref": null, "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12A366 Safari/600.1.4", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/sitemap.xml", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/sitemap.xml", "proto": "", "status": 301, "size": null, "ref": null, "ua": "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/sitemap.xml", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/sitemap.xml", "proto": "", "status": 301, "size": null, "ref": null, "ua": "Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/sitemap.xml", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "msnbot/1.1 (+http://search.msn.com/msnbot.htm)", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/robots.txt", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/robots.txt", "proto": "", "status": 301, "size": null, "ref": null, "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/sitemap.xml", "proto": "", "status": 301, "size": null, "ref": null, "ua": "msnbot/1.1 (+http://search.msn.com/msnbot.htm)", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} +{"ip": "5.135.75.243", "time": "2025-09-04T06:22:10+00:00", "method": "GET", "path": "/robots.txt", "proto": "TLSv1.3", "status": 200, "size": null, "ref": null, "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/91.0", "severity": "unknown", "iocs": ["5.135.75.243"], "rationale": "LLM disabled"} diff --git a/requirements.txt b/requirements.txt index 5ef8a99..3c757bf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,3 +11,6 @@ pytest-cov>=5.0.0 rich>=13.7.1 uvloop; platform_system != 'Windows' markdown>=3.6 +streamlit>=1.34.0 +pandas>=2.2.2 +altair>=5.3.0 diff --git a/scripts/run_ui.sh b/scripts/run_ui.sh new file mode 100755 index 0000000..54af48a --- /dev/null +++ b/scripts/run_ui.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ -d .venv ]; then + . .venv/bin/activate +fi + +exec streamlit run ui/app.py + diff --git a/src/cli.py b/src/cli.py index c0ae19a..acfd5f5 100644 --- a/src/cli.py +++ b/src/cli.py @@ -20,8 +20,13 @@ from .enrichers.llm_enricher import enrich_log_records from .enrichers.cti_service import cti_for_ips from .parsers.ua_analysis import detect_suspicious_user_agent -from .reports.report_builder import build_text_report, build_markdown_report +from .reports.report_builder import ( + build_text_report, + build_markdown_report, + build_malicious_ai_report, +) from .config import get_settings +from .groq_client import GroqRotatingClient rich_traceback_install(show_locals=False) @@ -46,6 +51,7 @@ def process_log( cti_max: int | None = None, cti_batch_size: int | None = None, cti_batch_pause: float = 0.0, + ai_malicious_report: bool = False, ) -> Path: console.rule("[bold cyan]🔎 Parsing Log") console.log(f"Parsing log: [bold]{path}") @@ -104,6 +110,63 @@ def process_log( ) console.log(f"Reports saved: [bold]{txt_path}[/], [bold]{md_path}[/]") + # Optional: generate a detailed malicious activity report using LLM + if ai_malicious_report and use_llm and suspicious_rows: + try: + + # Select IPs with strongest malicious indicators + def is_malicious(row: dict[str, object]) -> bool: + risk = str(row.get("risk", "unknown")).lower() + talos = str(row.get("talos_reputation", "")).lower() + vt_mal = int(row.get("vt_malicious") or 0) + vt_susp = int(row.get("vt_suspicious") or 0) + return ( + risk in {"high"} + or talos in {"untrusted", "malicious"} + or vt_mal >= 1 + or vt_susp >= 3 + ) + + malicious = [r for r in suspicious_rows if is_malicious(r)] + if malicious: + # Derive minimal per-IP context from enriched events (top paths/UA) + from collections import Counter as _C + per_ip_paths: dict[str, list[tuple[str, int]]] = {} + per_ip_ua: dict[str, str] = {} + for ip in {str(r.get("ip")) for r in malicious}: + paths = _C([str(e.get("path")) for e in enriched if str(e.get("ip")) == ip and e.get("path")]) + per_ip_paths[ip] = paths.most_common(5) + # pick any UA string observed + for e in enriched: + if str(e.get("ip")) == ip and (e.get("ua") or e.get("user_agent")): + per_ip_ua[ip] = str(e.get("ua") or e.get("user_agent")) + break + # Build prompt + insight_req = { + "malicious": malicious[:20], # cap to keep prompt small + "per_ip_top_paths": per_ip_paths, + "per_ip_ua": per_ip_ua, + } + client = GroqRotatingClient() + content = client.chat([ + { + "role": "system", + "content": ( + "You are a senior SOC analyst. Draft a concise but detailed incident note summarizing malicious " + "activity detected in logs corroborated by CTI (AbuseIPDB, Talos, VirusTotal). " + "Include: IP(s), CTI signals, notable paths, suspected TTPs, and recommended actions (blocking, WAF rules, triage). " + "Use clear sections and bullets." + ), + }, + {"role": "user", "content": json.dumps(insight_req)}, + ]) + rpt_txt, rpt_md = build_malicious_ai_report(reports_dir, content) + console.log(f"Malicious AI report saved: [bold]{rpt_txt}[/], [bold]{rpt_md}[/]") + else: + console.log("[dim]No strong malicious CTI signals; skipping detailed AI report.") + except Exception as e: # pragma: no cover - network/env specific + console.log(f"[dim]Malicious AI report unavailable: {e}") + return out_path @@ -234,8 +297,6 @@ def summarize_and_cti( ai_insight: str | None = None if use_llm: try: - from .groq_client import GroqRotatingClient - client = GroqRotatingClient() insight_req = { "total_requests": total_requests, @@ -264,8 +325,6 @@ def process_pdf(path: Path, out_dir: Path, use_llm: bool) -> Path: out_path.write_text(text, encoding="utf-8") # Optional: one-shot summary with LLM if use_llm and text.strip(): - from .groq_client import GroqRotatingClient - client = GroqRotatingClient() summary = client.chat([ {"role": "system", "content": "Summarize the key findings in 5 bullets."}, @@ -347,6 +406,7 @@ def main(argv: List[str] | None = None) -> int: parser.add_argument("--format", choices=["jsonl", "csv"], default="jsonl", help="Output format for logs") parser.add_argument("--no-cti", action="store_true", help="Disable CTI lookups") parser.add_argument("--no-reports", action="store_true", help="Do not build text/markdown reports") + parser.add_argument("--ai-malicious-report", action="store_true", help="Generate detailed AI report for malicious CTI signals") parser.add_argument("--color", choices=["auto", "always", "never"], default="auto", help="Terminal color policy") # LLM request controls parser.add_argument("--llm-sample", type=int, default=200, help="Limit LLM calls by sampling this many groups (0=all)") @@ -427,6 +487,7 @@ def _looks_like_log_file(p: Path, sample_lines: int = 200) -> bool: cti_max=(None if args.cti_max in (None, 0) else max(0, int(args.cti_max))), cti_batch_size=(None if getattr(args, 'cti_batch_size', 0) in (None, 0) else max(1, int(args.cti_batch_size))), cti_batch_pause=float(getattr(args, 'cti_batch_pause', 0.0) or 0.0), + ai_malicious_report=bool(args.ai_malicious_report), ) # Load enriched to drive summary/preview enriched_records = [json.loads(l) for l in (out_dir / f"{path.stem}.jsonl").read_text(encoding="utf-8").splitlines()] if args.format == "jsonl" else None diff --git a/src/enrichers/cti_providers.py b/src/enrichers/cti_providers.py index 6f76f7e..87b0133 100644 --- a/src/enrichers/cti_providers.py +++ b/src/enrichers/cti_providers.py @@ -14,6 +14,22 @@ class AbuseIPDBResult: url: str +@dataclass +class TalosResult: + ip: str + reputation: Optional[str] + owner: Optional[str] + url: str + + +@dataclass +class VirusTotalResult: + ip: str + malicious: Optional[int] + suspicious: Optional[int] + url: str + + def fetch_abuseipdb(ip: str, timeout: float = 15.0) -> AbuseIPDBResult: # Lazy imports to keep tests independent of optional deps try: @@ -78,3 +94,59 @@ def _extract_text(patterns): country=country, url=url, ) + + +def fetch_talos(ip: str, timeout: float = 15.0) -> TalosResult: + try: + import httpx # type: ignore + except Exception: # pragma: no cover + httpx = None # type: ignore + try: + from bs4 import BeautifulSoup # type: ignore + except Exception: # pragma: no cover + BeautifulSoup = None # type: ignore + + url = f"https://talosintelligence.com/reputation_center/lookup?search={ip}" + if httpx is None or BeautifulSoup is None: # pragma: no cover + return TalosResult(ip=ip, reputation=None, owner=None, url=url) + try: + with httpx.Client(follow_redirects=True, timeout=timeout) as client: + resp = client.get(url) + resp.raise_for_status() + html = resp.text + except Exception: # pragma: no cover + return TalosResult(ip=ip, reputation=None, owner=None, url=url) + soup = BeautifulSoup(html, "html.parser") + text = soup.get_text(" ", strip=True) + rep = None + owner = None + # Heuristic patterns + m = re.search(r"Web Reputation\s*:?\s*([A-Za-z]+)", text, re.IGNORECASE) + if m: + rep = m.group(1).strip() + m = re.search(r"Owner\s*:?\s*([\w\s\-\.,]+)", text, re.IGNORECASE) + if m: + owner = m.group(1).strip() + return TalosResult(ip=ip, reputation=rep, owner=owner, url=url) + + +def fetch_virustotal(ip: str, api_key: Optional[str], timeout: float = 15.0) -> VirusTotalResult: + url = f"https://www.virustotal.com/api/v3/ip_addresses/{ip}" + if not api_key: # pragma: no cover + return VirusTotalResult(ip=ip, malicious=None, suspicious=None, url=url) + try: + import httpx # type: ignore + except Exception: # pragma: no cover + return VirusTotalResult(ip=ip, malicious=None, suspicious=None, url=url) + try: + with httpx.Client(timeout=timeout, headers={"x-apikey": api_key}) as client: + r = client.get(url) + if r.status_code >= 400: + return VirusTotalResult(ip=ip, malicious=None, suspicious=None, url=url) + data = r.json() + stats = data.get("data", {}).get("attributes", {}).get("last_analysis_stats", {}) + mal = stats.get("malicious") + susp = stats.get("suspicious") + return VirusTotalResult(ip=ip, malicious=mal, suspicious=susp, url=url) + except Exception: # pragma: no cover + return VirusTotalResult(ip=ip, malicious=None, suspicious=None, url=url) diff --git a/src/parsers/ua_analysis.py b/src/parsers/ua_analysis.py index 74c00ae..90e6315 100644 --- a/src/parsers/ua_analysis.py +++ b/src/parsers/ua_analysis.py @@ -1,7 +1,7 @@ from __future__ import annotations import re -from typing import List, Tuple +from typing import List, Tuple, Optional SUSPICIOUS_AGENTS = [ @@ -19,12 +19,12 @@ ] -def detect_suspicious_user_agent(ua: str | None) -> Tuple[bool, str | None]: +def detect_suspicious_user_agent(ua: Optional[str], patterns: Optional[List[str]] = None) -> Tuple[bool, Optional[str]]: if not ua: return False, None ua_l = ua.lower() - for pat in SUSPICIOUS_AGENTS: + pats = patterns if patterns else SUSPICIOUS_AGENTS + for pat in pats: if re.search(pat, ua_l): return True, pat return False, None - diff --git a/src/reports/report_builder.py b/src/reports/report_builder.py index bc5f85d..06bb121 100644 --- a/src/reports/report_builder.py +++ b/src/reports/report_builder.py @@ -32,8 +32,8 @@ def build_markdown_report( if not suspicious: lines.append("No suspicious IPs identified.\n") else: - lines.append(_md_row(["IP", "Risk", "Abuse Score", "Total Reports", "Country", "Requests", "4xx", "Suspicious UA", "One-line Explain"])) - lines.append(_md_row(["---"] * 9)) + lines.append(_md_row(["IP", "Risk", "Abuse Score", "Total Reports", "Country", "Requests", "4xx", "Suspicious UA", "Talos", "VT (mal/susp)", "One-line Explain"])) + lines.append(_md_row(["---"] * 11)) for s in suspicious: lines.append( _md_row([ @@ -45,6 +45,8 @@ def build_markdown_report( str(s.get("requests", "")), str(s.get("errors_4xx", "")), "yes" if s.get("ua_suspicious") else "no", + str(s.get("talos_reputation", "")), + f"{s.get('vt_malicious','')}/{s.get('vt_suspicious','')}", str(s.get("ai_one_liner", "")), ]) ) @@ -80,7 +82,8 @@ def build_text_report( lines.append( f"- {s.get('ip')} | risk={s.get('risk')} | score={s.get('abuse_confidence_score')} | " f"reports={s.get('total_reports')} | country={s.get('country')} | req={s.get('requests')} | " - f"4xx={s.get('errors_4xx')} | UA suspicious={'yes' if s.get('ua_suspicious') else 'no'}\n" + f"4xx={s.get('errors_4xx')} | UA suspicious={'yes' if s.get('ua_suspicious') else 'no'} | " + f"talos={s.get('talos_reputation')} | vt={s.get('vt_malicious')}/{s.get('vt_suspicious')}\n" ) if s.get("ai_one_liner"): lines.append(f" AI: {s.get('ai_one_liner')}\n") @@ -88,3 +91,32 @@ def build_text_report( path.write_text("".join(lines), encoding="utf-8") return path + +def build_malicious_ai_report( + out_dir: Path, + content: str, + *, + title: str = "Malicious Activity AI Report", +) -> tuple[Path, Path]: + """Write a detailed AI-written malicious activity report to txt and md. + + Returns: (txt_path, md_path) + """ + out_dir.mkdir(parents=True, exist_ok=True) + txt_path = out_dir / "malicious_ai_report.txt" + md_path = out_dir / "malicious_ai_report.md" + + # Text version + lines_txt: List[str] = [] + lines_txt.append(f"{title}\n") + lines_txt.append("=" * len(title) + "\n\n") + lines_txt.append(content.strip() + "\n") + txt_path.write_text("".join(lines_txt), encoding="utf-8") + + # Markdown version + lines_md: List[str] = [] + lines_md.append(f"# {title}\n\n") + lines_md.append(content.strip() + "\n") + md_path.write_text("".join(lines_md), encoding="utf-8") + + return txt_path, md_path diff --git a/tests/cli/test_ai_malicious_report.py b/tests/cli/test_ai_malicious_report.py new file mode 100644 index 0000000..1f7e09a --- /dev/null +++ b/tests/cli/test_ai_malicious_report.py @@ -0,0 +1,41 @@ +import os +from pathlib import Path + +from src import cli + + +class DummyGroq: + def chat(self, messages): + return "DUMMY MALICIOUS REPORT" + + +def test_ai_malicious_report_offline_blocklist(tmp_path, monkeypatch): + # Prepare a log with one IP that will be escalated via offline blocklist + log = tmp_path / "access_log.txt" + log.write_text( + '\n'.join([ + '10.9.9.9 - - [10/Oct/2000:13:55:36 -0700] "GET /a HTTP/1.1" 404 0 "-" "sqlmap/1.7"', + '10.9.9.9 - - [10/Oct/2000:13:55:40 -0700] "GET /b HTTP/1.1" 404 0 "-" "sqlmap/1.7"', + ]), + encoding="utf-8", + ) + # Create offline blocklist and point env var to it so risk escalates to high + bl = tmp_path / "blocklist.txt" + bl.write_text("10.9.9.9\n", encoding="utf-8") + monkeypatch.setenv("OFFLINE_IP_BLOCKLIST", str(bl)) + # Ensure LLM path is taken; set a dummy key and monkeypatch client + monkeypatch.setenv("GROQ_API_KEYS", "dummy-key") + monkeypatch.setattr(cli, "GroqRotatingClient", lambda: DummyGroq()) + + rc = cli.main([ + str(log), + "--out", str(tmp_path), + "--no-cti", # avoid live CTI calls + "--ai-malicious-report", + "--color", "never", + ]) + assert rc == 0 + rpt = tmp_path / "reports" / "malicious_ai_report.txt" + assert rpt.exists() + assert "DUMMY MALICIOUS REPORT" in rpt.read_text(encoding="utf-8") + diff --git a/tests/enrichers/test_cti_providers_parsing.py b/tests/enrichers/test_cti_providers_parsing.py new file mode 100644 index 0000000..027fafd --- /dev/null +++ b/tests/enrichers/test_cti_providers_parsing.py @@ -0,0 +1,98 @@ +import types + +import pytest + +from src.enrichers.cti_providers import ( + fetch_abuseipdb, + fetch_talos, + fetch_virustotal, +) + + +class _Resp: + def __init__(self, text: str = "", status: int = 200, json_data=None): + self.text = text + self.status_code = status + self._json = json_data or {} + + def raise_for_status(self): + if self.status_code >= 400: + raise RuntimeError("http error") + + def json(self): + return self._json + + +class _Client: + def __init__(self, *, text: str = "", status: int = 200, json_data=None, **_: object): + self._resp = _Resp(text=text, status=status, json_data=json_data) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc, tb): + return False + + def get(self, url: str): # noqa: ARG002 - exercised by provider code + return self._resp + + +def test_fetch_abuseipdb_parses_html(monkeypatch): + html = """ + +
Abuse Confidence Score: 90
+
Total Reports: 123
+
Country: United States
+ + """ + + # Patch httpx.Client to our stub + import httpx # type: ignore + + monkeypatch.setattr(httpx, "Client", lambda **kwargs: _Client(text=html)) + + res = fetch_abuseipdb("1.2.3.4") + assert res.ip == "1.2.3.4" + assert res.abuse_confidence_score == 90 + assert res.total_reports == 123 + assert res.country == "United States" + assert "abuseipdb" in res.url + + +def test_fetch_talos_parses_html(monkeypatch): + html = """ + +
Web Reputation: Malicious
+
Owner: Example ISP, Inc.
+ + """ + import httpx # type: ignore + + monkeypatch.setattr(httpx, "Client", lambda **kwargs: _Client(text=html)) + + res = fetch_talos("5.6.7.8") + assert res.ip == "5.6.7.8" + assert res.reputation == "Malicious" + assert res.owner == "Example ISP, Inc." + assert "talos" in res.url + + +def test_fetch_virustotal_parses_json(monkeypatch): + payload = { + "data": { + "attributes": { + "last_analysis_stats": {"malicious": 2, "suspicious": 3} + } + } + } + + import httpx # type: ignore + + monkeypatch.setattr(httpx, "Client", lambda **kwargs: _Client(json_data=payload)) + + res = fetch_virustotal("9.9.9.9", api_key="dummy") + assert res.ip == "9.9.9.9" + assert res.malicious == 2 + assert res.suspicious == 3 + assert "virustotal" in res.url + diff --git a/tests/groq_client/test_groq_rotating_client.py b/tests/groq_client/test_groq_rotating_client.py new file mode 100644 index 0000000..70a5819 --- /dev/null +++ b/tests/groq_client/test_groq_rotating_client.py @@ -0,0 +1,49 @@ +from types import SimpleNamespace + +import pytest + +import src.groq_client as gc + + +class FakeCompletions: + def __init__(self, content: str): + self._content = content + + def create(self, model, messages, temperature): # noqa: ARG002 + return SimpleNamespace(choices=[SimpleNamespace(message=SimpleNamespace(content=self._content))]) + + +class FakeChat: + def __init__(self, content: str): + self.completions = FakeCompletions(content) + + +class FakeGroq: + def __init__(self, api_key: str): + # Expose api_key for assertions via _next_client + self.api_key = api_key + self.chat = FakeChat("ok") + + +def test_next_client_rotates(monkeypatch): + # Patch Groq class to our fake + monkeypatch.setattr(gc, "Groq", FakeGroq) + client = gc.GroqRotatingClient(api_keys=["k1", "k2"], model="m") + + c1 = client._next_client() + c2 = client._next_client() + c3 = client._next_client() + assert getattr(c1, "api_key", None) == "k1" + assert getattr(c2, "api_key", None) == "k2" + assert getattr(c3, "api_key", None) == "k1" + + +def test_chat_success_path(monkeypatch): + monkeypatch.setattr(gc, "Groq", FakeGroq) + client = gc.GroqRotatingClient(api_keys=["kX"], model="m") + out = client.chat([ + {"role": "system", "content": "s"}, + {"role": "user", "content": "u"}, + ]) + assert out == "ok" + diff --git a/tests/parsers/test_text_extractor_pdf.py b/tests/parsers/test_text_extractor_pdf.py new file mode 100644 index 0000000..0abe108 --- /dev/null +++ b/tests/parsers/test_text_extractor_pdf.py @@ -0,0 +1,19 @@ +from pathlib import Path + +from PyPDF2 import PdfWriter + +from src.parsers.text_extractor import extract_text_from_pdf + + +def test_extract_text_from_pdf_blank(tmp_path: Path): + pdf_path = tmp_path / "blank.pdf" + writer = PdfWriter() + writer.add_blank_page(width=72, height=72) + with pdf_path.open("wb") as f: + writer.write(f) + + text = extract_text_from_pdf(pdf_path) + # Blank page yields empty string but exercises the code path + assert isinstance(text, str) + assert text == "" + diff --git a/ui/app.py b/ui/app.py new file mode 100644 index 0000000..ec5556d --- /dev/null +++ b/ui/app.py @@ -0,0 +1,96 @@ +import json +from pathlib import Path + +import pandas as pd +import streamlit as st + + +st.set_page_config(page_title="LogCTI Dashboard", page_icon="🛡️", layout="wide") +st.title("Log + CTI Dashboard 🛡️") + + +@st.cache_data(show_spinner=False) +def load_jsonl(path: Path) -> pd.DataFrame: + rows = [] + for line in path.read_text(encoding="utf-8").splitlines(): + try: + rows.append(json.loads(line)) + except Exception: + continue + return pd.DataFrame(rows) + + +def list_processed_files(base: Path) -> list[Path]: + if not base.exists(): + return [] + return sorted([p for p in base.glob("*.jsonl")], key=lambda p: p.stat().st_mtime, reverse=True) + + +col1, col2 = st.columns([2, 1]) +with col2: + base_dir = st.text_input("Processed dir", value=str(Path("data/processed").resolve())) + base_path = Path(base_dir) + files = list_processed_files(base_path) + file_names = [f.name for f in files] + selected = st.selectbox("Enriched file", options=file_names) if files else None + uploaded = st.file_uploader("...or upload enriched .jsonl", type=["jsonl"]) # optional + +df = pd.DataFrame() +if uploaded is not None: + df = pd.DataFrame([json.loads(l) for l in uploaded.getvalue().decode("utf-8").splitlines() if l.strip()]) +elif selected: + df = load_jsonl(base_path / selected) + +if df.empty: + st.info("Select or upload an enriched JSONL file to explore results.") + st.stop() + +# Metrics +total_requests = len(df) +unique_ips = df["ip"].nunique() if "ip" in df.columns else 0 +status_counts = df["status"].astype(str).value_counts() if "status" in df.columns else pd.Series(dtype=int) + +with col1: + m1, m2, m3 = st.columns(3) + m1.metric("Total requests", f"{total_requests}") + m2.metric("Unique IPs", f"{unique_ips}") + if not status_counts.empty: + m3.metric("Top status", f"{status_counts.index[0]}: {int(status_counts.iloc[0])}") + +st.subheader("Status distribution") +if not status_counts.empty: + st.bar_chart(status_counts) +else: + st.write("No status data available.") + +# Suspicious IPs table (if present in records as enriched by pipeline) +cols = [ + "ip", + "severity", + "status", + "path", + "ua", + "rationale", +] +present_cols = [c for c in cols if c in df.columns] +st.subheader("Sample of enriched events") +st.dataframe(df[present_cols].head(100), use_container_width=True) + +# Aggregate suspicious overview from records if they contain CTI annotations +cti_cols = [ + "ip", + "risk", + "abuse_confidence_score", + "total_reports", + "country", + "talos_reputation", + "vt_malicious", + "vt_suspicious", +] +present_cti = [c for c in cti_cols if c in df.columns] +if present_cti: + st.subheader("CTI Signals (per record view)") + st.dataframe(df[present_cti].dropna(how="all").head(200), use_container_width=True) + +st.caption("Tip: generate enriched JSONL via `python -m src.cli --out data/processed`.") +