subhamb04 commited on
Commit
ddd9009
·
verified ·
1 Parent(s): d0c8e92

Upload folder using huggingface_hub

Browse files
Files changed (11) hide show
  1. .gitignore +122 -0
  2. README.md +100 -12
  3. alerts_export.csv +8 -0
  4. app.py +88 -0
  5. config.py +12 -0
  6. enrichment.py +19 -0
  7. llm_classifier.py +31 -0
  8. log_generator.py +18 -0
  9. playbook.py +14 -0
  10. requirements.txt +4 -0
  11. utils.py +15 -0
.gitignore ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ------------------------------
2
+ # Python
3
+ # ------------------------------
4
+ __pycache__/
5
+ *.py[cod]
6
+ *$py.class
7
+
8
+ # C extensions
9
+ *.so
10
+ *.pyd
11
+ *.dll
12
+
13
+ # ------------------------------
14
+ # Environments
15
+ # ------------------------------
16
+ .venv/
17
+ venv/
18
+ env/
19
+ ENV/
20
+ .venv*/
21
+ venv*/
22
+ env*/
23
+ ENV*/
24
+ .python-version
25
+
26
+ # ------------------------------
27
+ # Distribution / packaging
28
+ # ------------------------------
29
+ .Python
30
+ build/
31
+ dist/
32
+ downloads/
33
+ eggs/
34
+ .eggs/
35
+ sdist/
36
+ wheels/
37
+ share/python-wheels/
38
+ *.egg-info/
39
+ .installed.cfg
40
+ *.egg
41
+ MANIFEST
42
+ pip-wheel-metadata/
43
+ pip-log.txt
44
+ pip-delete-this-directory.txt
45
+
46
+ # ------------------------------
47
+ # Unit test / coverage reports
48
+ # ------------------------------
49
+ htmlcov/
50
+ .tox/
51
+ .nox/
52
+ .coverage
53
+ .coverage.*
54
+ .cache
55
+ nosetests.xml
56
+ coverage.xml
57
+ *.cover
58
+ *.py,cover
59
+ .pytest_cache/
60
+ junit*.xml
61
+
62
+ # ------------------------------
63
+ # Type checkers / linters
64
+ # ------------------------------
65
+ .mypy_cache/
66
+ .dmypy.json
67
+ dmypy.json
68
+ .pyre/
69
+ .pytype/
70
+ .ruff_cache/
71
+
72
+ # ------------------------------
73
+ # PyInstaller
74
+ # ------------------------------
75
+ *.manifest
76
+ *.spec
77
+
78
+ # ------------------------------
79
+ # Jupyter
80
+ # ------------------------------
81
+ .ipynb_checkpoints/
82
+
83
+ # ------------------------------
84
+ # Logs and runtime files
85
+ # ------------------------------
86
+ logs/
87
+ *.log
88
+ *.pid
89
+ *.pid.lock
90
+
91
+ # ------------------------------
92
+ # Local environment variables & secrets
93
+ # ------------------------------
94
+ .env
95
+ .env.*
96
+ !.env.example
97
+
98
+ # ------------------------------
99
+ # Editors / IDEs / Tooling
100
+ # ------------------------------
101
+ .idea/
102
+ *.iml
103
+ .vscode/
104
+ .history/
105
+ .cursor/
106
+ *.code-workspace
107
+
108
+ # ------------------------------
109
+ # OS-specific
110
+ # ------------------------------
111
+ .DS_Store
112
+ Thumbs.db
113
+ ehthumbs.db
114
+ Desktop.ini
115
+
116
+ # ------------------------------
117
+ # Optional local data & temp
118
+ # ------------------------------
119
+ tmp/
120
+ temp/
121
+ data/
122
+
README.md CHANGED
@@ -1,12 +1,100 @@
1
- ---
2
- title: Actionable
3
- emoji: 🦀
4
- colorFrom: red
5
- colorTo: red
6
- sdk: gradio
7
- sdk_version: 5.45.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: actionable
3
+ app_file: app.py
4
+ sdk: gradio
5
+ sdk_version: 5.44.1
6
+ ---
7
+ ## SOC Dashboard – Live Random Alert Streaming
8
+
9
+ A lightweight SOC-style dashboard that streams synthetic security alerts, enriches them with threat intel and GeoIP, classifies them using an LLM, and surfaces recommended actions. Built with Gradio for a simple, responsive UI.
10
+
11
+ ### Features
12
+ - **Live alert stream**: Generates up to 10 synthetic logs per session.
13
+ - **Enrichment**: Adds IP reputation and GeoIP context.
14
+ - **AI classification**: Uses a Gemini-compatible OpenAI client to categorize alerts, set priority, and suggest actions.
15
+ - **Export**: Saves the current session’s alerts to `alerts_export.csv`.
16
+ - **Playbooks**: Simulates actions (e.g., block IP, quarantine host) based on AI suggestions.
17
+
18
+ ### Repository Structure
19
+ - `app.py`: Gradio UI and app orchestration.
20
+ - `config.py`: Environment loading and Gemini-compatible OpenAI client initialization.
21
+ - `enrichment.py`: Threat intel and GeoIP enrichment.
22
+ - `llm_classifier.py`: Prompting and parsing for LLM classification.
23
+ - `log_generator.py`: Synthetic log generation.
24
+ - `playbook.py`: Maps AI-recommended actions to simulated playbooks.
25
+ - `utils.py`: Helper utilities for cleaning/parsing model output.
26
+ - `alerts_export.csv`: Created after exporting from the UI.
27
+
28
+ ### Requirements
29
+ - Python 3.9+
30
+ - Pip
31
+
32
+ Python dependencies are listed in `requirements.txt`:
33
+ - `pandas`
34
+ - `gradio`
35
+ - `python-dotenv`
36
+ - `openai`
37
+
38
+ ### Environment Variables
39
+ Create a `.env` file in the project root with your Gemini API key. This project uses the OpenAI SDK pointed at Google’s Gemini-compatible endpoint.
40
+
41
+ Example `.env`:
42
+ ```
43
+ GOOGLE_API_KEY=your_gemini_api_key_here
44
+ ```
45
+
46
+ ### Installation
47
+ 1. Clone or download this repository.
48
+ 2. Open a terminal in the project directory.
49
+ 3. Create and activate a virtual environment (recommended).
50
+ - Windows (PowerShell):
51
+ ```powershell
52
+ py -m venv .venv
53
+ .\.venv\Scripts\Activate.ps1
54
+ ```
55
+ - macOS/Linux (bash):
56
+ ```bash
57
+ python3 -m venv .venv
58
+ source .venv/bin/activate
59
+ ```
60
+ 4. Install dependencies:
61
+ ```bash
62
+ pip install -r requirements.txt
63
+ ```
64
+ 5. Create the `.env` file as shown above.
65
+
66
+ ### Running Locally
67
+ Start the Gradio app:
68
+ ```bash
69
+ python app.py
70
+ ```
71
+ Gradio will print a local URL (e.g., `http://127.0.0.1:7860`). Open it in your browser.
72
+
73
+ ### Using the App
74
+ 1. Click **Start Streaming** to begin generating alerts (up to 10 per session).
75
+ 2. Watch the table populate with enriched and classified alerts.
76
+ 3. Click **Stop Streaming** to halt early.
77
+ 4. Click **Export Alerts** to save the current table to `alerts_export.csv` in the project root, then download it from the UI.
78
+ 5. Click **Run Playbooks** to simulate actions suggested by the AI; results appear in the text box.
79
+
80
+ ### How It Works
81
+ - `log_generator.generate_random_log` produces timestamped events with random IPs and messages.
82
+ - `enrichment.enrich_alert` augments each log with IP reputation and GeoIP info from in-memory lookups.
83
+ - `llm_classifier.classify_alert` sends a structured prompt to the Gemini-compatible endpoint via the OpenAI SDK and returns `{ category, priority, action }`.
84
+ - `app.py` builds the session table and wires up the Gradio UI for starting/stopping, exporting, and running playbooks.
85
+
86
+ ### Troubleshooting
87
+ - **No output / classification errors**: Verify `.env` contains a valid `GOOGLE_API_KEY` and you have network connectivity.
88
+ - **Package errors**: Re-create/activate the virtual environment and re-run `pip install -r requirements.txt`.
89
+ - **Port in use**: Set a different port when launching Gradio:
90
+ ```python
91
+ # in app.py main block
92
+ demo.queue().launch(server_port=7861)
93
+ ```
94
+
95
+ ### Notes
96
+ - Exported CSV only includes alerts from the current session.
97
+ - The playbook executions are simulated; no real systems are modified.
98
+ - IP reputation and GeoIP data are in-memory examples for demonstration.
99
+
100
+
alerts_export.csv ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ Timestamp,Event,Source IP,Reputation,Location,Category,Priority,Action
2
+ 2025-09-15 22:35:39,Suspicious admin privilege escalation,185.234.219.45,malicious,Russia,System Compromise / Post-Exploitation,High,"Immediately isolate the affected system(s) from the network, initiate full incident response procedures, block the malicious source IP (185.234.219.45) at the perimeter, and begin forensic analysis to determine the scope and root cause of the privilege escalation and C2 communication."
3
+ 2025-09-15 22:36:39,Normal login from corporate network,10.0.0.15,suspicious,China,Compromised Host,High,"Initiate incident response: Immediately isolate host 10.0.0.15 from the network, investigate the specific 'unusual activity' flagged by the honeypot, review associated login details, and begin forensic analysis to determine the scope and impact of the compromise."
4
+ 2025-09-15 22:37:39,Excessive DNS queries from single host,185.234.219.45,malicious,Russia,Malware (Command & Control),High,"Immediately isolate the affected internal host. Block the malicious source IP (185.234.219.45) at all perimeter security devices (firewall, DNS sinkhole, IPS/IDS). Initiate a full incident response investigation on the affected host to determine the compromise vector, malware payload, scope of infection, and potential data exfiltration. Scan the internal network for other indicators of compromise related to this C2 server."
5
+ 2025-09-15 22:38:39,Normal login from corporate network,192.168.1.10,clean,Private Network (Internal),Benign,Low,Close alert as a normal/expected event.
6
+ 2025-09-15 22:39:39,Multiple failed login attempts,192.168.1.10,clean,Private Network (Internal),Brute Force Attempt,Medium,"Investigate the source host (192.168.1.10) to identify the system/user. Review local logs on 192.168.1.10 for any signs of compromise or unusual activity. Identify the target account(s) of the failed login attempts and check their respective authentication logs (e.g., Active Directory, application logs) for the full scope of the attempts (count, frequency, targeted usernames). Contact the identified user/owner of 192.168.1.10 to rule out user error (e.g., forgotten password, misconfigured application)."
7
+ 2025-09-15 22:40:39,Normal login from corporate network,10.0.0.15,suspicious,China,Initial Access / Unauthorized Access Attempt,High,Initiate full incident response. Investigate potential IP spoofing or internal host compromise of 10.0.0.15. Immediately isolate the host 10.0.0.15. Review all authentication logs for success/failure related to this timestamp and identify the targeted user account. Conduct comprehensive network and endpoint forensics on 10.0.0.15. Alert Security Operations Center (SOC) team lead and relevant stakeholders.
8
+ 2025-09-15 22:41:39,User downloaded large file from unknown domain,192.168.1.10,clean,Private Network (Internal),Benign,Low,"Initiate an investigation into the unknown domain's legitimacy and current reputation (e.g., check with additional threat intelligence sources, perform a sandbox analysis if feasible). Engage directly with the user (192.168.1.10) to understand the purpose and necessity of the large file download. Review organizational policies concerning downloads from unclassified or untrusted sources."
app.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ import pandas as pd
3
+ import gradio as gr
4
+ from datetime import datetime
5
+
6
+ from enrichment import enrich_alert
7
+ from llm_classifier import classify_alert
8
+ from log_generator import generate_random_log
9
+ from playbook import run_playbook
10
+
11
+ stop_streaming = False
12
+ results_store = []
13
+
14
+ def stream_logs():
15
+ global stop_streaming, results_store
16
+ stop_streaming = False
17
+ results_store = []
18
+
19
+ base_time = datetime.now()
20
+
21
+ for i in range(10):
22
+ if stop_streaming:
23
+ break
24
+
25
+ log = generate_random_log(base_time, i)
26
+ enriched = enrich_alert(log.copy())
27
+ ai_result = classify_alert(enriched)
28
+
29
+ row = {
30
+ "Timestamp": enriched.get("timestamp"),
31
+ "Event": enriched.get("event"),
32
+ "Source IP": enriched.get("source_ip"),
33
+ "Reputation": enriched.get("ip_reputation"),
34
+ "Location": enriched.get("geo_location"),
35
+ "Category": ai_result.get("category"),
36
+ "Priority": ai_result.get("priority"),
37
+ "Action": ai_result.get("action"),
38
+ }
39
+ results_store.append(row)
40
+
41
+ yield pd.DataFrame(results_store)
42
+ time.sleep(3)
43
+
44
+ def stop_logs():
45
+ global stop_streaming
46
+ stop_streaming = True
47
+ return None
48
+
49
+ def export_alerts():
50
+ global results_store
51
+ if not results_store:
52
+ return None
53
+ df = pd.DataFrame(results_store)
54
+ export_path = "alerts_export.csv"
55
+ df.to_csv(export_path, index=False)
56
+ return export_path
57
+
58
+ def execute_playbooks():
59
+ global results_store
60
+ if not results_store:
61
+ return "No alerts to act on."
62
+ actions = [run_playbook(alert) for alert in results_store]
63
+ return "\n".join(actions)
64
+
65
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
66
+ gr.Markdown("## 🛡️ Actionable : SOC Dashboard – Live Alert Prioritization & Triage")
67
+
68
+ with gr.Row():
69
+ start_btn = gr.Button("▶ Start Streaming", variant="primary")
70
+ stop_btn = gr.Button("⏹ Stop Streaming", variant="stop")
71
+ export_btn = gr.Button("💾 Export Alerts", variant="huggingface")
72
+ playbook_btn = gr.Button("⚡ Run Playbooks")
73
+
74
+ output_table = gr.Dataframe(
75
+ headers=["Timestamp", "Event", "Source IP", "Reputation", "Location", "Category", "Priority", "Action"],
76
+ wrap=True
77
+ )
78
+
79
+ download_file = gr.File(label="Download Exported Alerts")
80
+ playbook_output = gr.Textbox(label="Playbook Execution Log", lines=8)
81
+
82
+ start_btn.click(fn=stream_logs, outputs=output_table)
83
+ stop_btn.click(fn=stop_logs, outputs=None)
84
+ export_btn.click(fn=export_alerts, outputs=download_file)
85
+ playbook_btn.click(fn=execute_playbooks, outputs=playbook_output)
86
+
87
+ if __name__ == "__main__":
88
+ demo.queue().launch()
config.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from openai import OpenAI
4
+
5
+ load_dotenv(override=True)
6
+
7
+ GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
8
+
9
+ client = OpenAI(
10
+ api_key=GOOGLE_API_KEY,
11
+ base_url="https://generativelanguage.googleapis.com/v1beta/openai/"
12
+ )
enrichment.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ threat_intel_db = {
2
+ "185.234.219.45": {"reputation": "malicious", "reason": "Known command & control server"},
3
+ "10.0.0.15": {"reputation": "suspicious", "reason": "Unusual activity flagged in honeypot"},
4
+ }
5
+
6
+ geoip_db = {
7
+ "192.168.1.10": "Private Network (Internal)",
8
+ "185.234.219.45": "Russia",
9
+ "10.0.0.15": "China",
10
+ "172.16.0.5": "Private Network (Internal)"
11
+ }
12
+
13
+ def enrich_alert(log_entry):
14
+ ip = log_entry.get("source_ip", "")
15
+ threat_info = threat_intel_db.get(ip, {"reputation": "clean", "reason": "No malicious activity reported"})
16
+ log_entry["ip_reputation"] = threat_info["reputation"]
17
+ log_entry["intel_note"] = threat_info["reason"]
18
+ log_entry["geo_location"] = geoip_db.get(ip, "Unknown Location")
19
+ return log_entry
llm_classifier.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from config import client
2
+ from utils import clean_ai_response, parse_json_safe
3
+
4
+ def classify_alert(log_entry):
5
+ prompt = f"""
6
+ You are a SOC (Security Operations Center) AI assistant.
7
+ Analyze the following security alert and classify it.
8
+
9
+ Alert details:
10
+ Timestamp: {log_entry.get('timestamp')}
11
+ Source IP: {log_entry.get('source_ip')}
12
+ Event: {log_entry.get('event')}
13
+ Threat Intelligence: Reputation = {log_entry.get('ip_reputation')}, Note = {log_entry.get('intel_note')}
14
+ GeoIP: {log_entry.get('geo_location')}
15
+
16
+ Tasks:
17
+ 1. Categorize the attack type (e.g., brute force, malware, data exfiltration, benign).
18
+ 2. Assign a priority (High, Medium, Low).
19
+ 3. Suggest next action.
20
+
21
+ Return response in JSON with keys: category, priority, action.
22
+ """
23
+
24
+ response = client.chat.completions.create(
25
+ model="gemini-2.5-flash",
26
+ messages=[{"role": "user", "content": prompt}],
27
+ )
28
+
29
+ raw_output = response.choices[0].message.content
30
+ cleaned = clean_ai_response(raw_output)
31
+ return parse_json_safe(cleaned)
log_generator.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ from datetime import datetime, timedelta
3
+ ips = ["192.168.1.10", "185.234.219.45", "10.0.0.15", "172.16.0.5"]
4
+ events = [
5
+ "Multiple failed login attempts",
6
+ "Unusual outbound traffic to known bad IP",
7
+ "User downloaded large file from unknown domain",
8
+ "Normal login from corporate network",
9
+ "Suspicious admin privilege escalation",
10
+ "Excessive DNS queries from single host"
11
+ ]
12
+
13
+ def generate_random_log(base_time, i):
14
+ return {
15
+ "timestamp": (base_time + timedelta(minutes=i)).strftime("%Y-%m-%d %H:%M:%S"),
16
+ "source_ip": random.choice(ips),
17
+ "event": random.choice(events)
18
+ }
playbook.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def run_playbook(alert):
2
+ action = alert.get("Action", "").lower()
3
+ ip = alert.get("Source IP", "Unknown")
4
+
5
+ if "block ip" in action:
6
+ return f"🔒 Simulated: Blocking IP {ip} in firewall"
7
+ elif "quarantine" in action:
8
+ return f"🛡️ Simulated: Quarantining host {ip} via EDR"
9
+ elif "escalate" in action or "alert" in action:
10
+ return f"📢 Simulated: Escalating alert for {ip} to Tier-2 SOC"
11
+ elif "no action" in action or "benign" in action:
12
+ return f"✅ Simulated: No action needed for {ip}"
13
+ else:
14
+ return f"⚙️ Simulated: Generic action executed for {ip}"
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ pandas
2
+ gradio
3
+ python-dotenv
4
+ openai
utils.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import json
3
+
4
+ def clean_ai_response(text: str) -> str:
5
+ if not text:
6
+ return text
7
+ text = re.sub(r"^```[a-zA-Z]*\n?", "", text.strip())
8
+ text = re.sub(r"\n?```$", "", text.strip())
9
+ return text.strip()
10
+
11
+ def parse_json_safe(text: str) -> dict:
12
+ try:
13
+ return json.loads(text)
14
+ except Exception:
15
+ return {"category": "Unknown", "priority": "Unknown", "action": "Parsing failed"}