Spaces:
Running
Running
import streamlit as st | |
import pandas as pd | |
import plotly.express as px | |
import plotly.graph_objects as go | |
import numpy as np | |
from datetime import datetime, timedelta | |
def render_alerts(): | |
st.title("Alert Management") | |
# Alert Overview | |
st.subheader("Alert Overview") | |
# Alert metrics | |
col1, col2, col3, col4, col5 = st.columns(5) | |
with col1: | |
st.metric( | |
label="Active Alerts", | |
value="27", | |
delta="4", | |
delta_color="inverse" | |
) | |
with col2: | |
st.metric( | |
label="Critical", | |
value="8", | |
delta="2", | |
delta_color="inverse" | |
) | |
with col3: | |
st.metric( | |
label="High", | |
value="12", | |
delta="3", | |
delta_color="inverse" | |
) | |
with col4: | |
st.metric( | |
label="Medium", | |
value="5", | |
delta="-1", | |
delta_color="normal" | |
) | |
with col5: | |
st.metric( | |
label="Low", | |
value="2", | |
delta="0", | |
delta_color="normal" | |
) | |
# Filters for alerts | |
with st.container(): | |
st.markdown("### Alert Filters") | |
filter_col1, filter_col2, filter_col3, filter_col4 = st.columns(4) | |
with filter_col1: | |
severity_filter = st.multiselect( | |
"Severity", | |
["Critical", "High", "Medium", "Low"], | |
default=["Critical", "High", "Medium", "Low"] | |
) | |
with filter_col2: | |
status_filter = st.multiselect( | |
"Status", | |
["New", "In Progress", "Resolved", "False Positive"], | |
default=["New", "In Progress"] | |
) | |
with filter_col3: | |
date_range = st.selectbox( | |
"Time Range", | |
["Last 24 Hours", "Last 7 Days", "Last 30 Days", "Custom Range"], | |
index=1 | |
) | |
with filter_col4: | |
category_filter = st.multiselect( | |
"Category", | |
["Data Breach", "Ransomware", "Credentials", "PII", "Brand Abuse", "Source Code", "Other"], | |
default=["Data Breach", "Credentials", "PII"] | |
) | |
# Alert list | |
st.markdown("### Active Alerts") | |
# Sample alert data | |
alerts = [ | |
{ | |
"id": "ALERT-2025-04081", | |
"timestamp": "2025-04-08 14:32:21", | |
"severity": "Critical", | |
"category": "Data Breach", | |
"description": "Patient records from Memorial Hospital found on dark web marketplace.", | |
"status": "New", | |
"source": "AlphaBay Market" | |
}, | |
{ | |
"id": "ALERT-2025-04082", | |
"timestamp": "2025-04-08 10:15:43", | |
"severity": "Critical", | |
"category": "Ransomware", | |
"description": "Company mentioned in ransomware group's leak site as new victim.", | |
"status": "New", | |
"source": "BlackCat Leak Site" | |
}, | |
{ | |
"id": "ALERT-2025-04083", | |
"timestamp": "2025-04-08 08:42:19", | |
"severity": "High", | |
"category": "Credentials", | |
"description": "123 employee credentials found in new breach compilation.", | |
"status": "In Progress", | |
"source": "BreachForums" | |
}, | |
{ | |
"id": "ALERT-2025-04071", | |
"timestamp": "2025-04-07 22:03:12", | |
"severity": "High", | |
"category": "PII", | |
"description": "Customer PII being offered for sale on hacking forum.", | |
"status": "In Progress", | |
"source": "XSS Forum" | |
}, | |
{ | |
"id": "ALERT-2025-04072", | |
"timestamp": "2025-04-07 18:37:56", | |
"severity": "Medium", | |
"category": "Brand Abuse", | |
"description": "Phishing campaign using company brand assets detected.", | |
"status": "New", | |
"source": "Telegram Channel" | |
}, | |
{ | |
"id": "ALERT-2025-04073", | |
"timestamp": "2025-04-07 14:21:08", | |
"severity": "Medium", | |
"category": "Source Code", | |
"description": "Fragments of internal source code shared in paste site.", | |
"status": "In Progress", | |
"source": "DeepPaste" | |
}, | |
{ | |
"id": "ALERT-2025-04063", | |
"timestamp": "2025-04-06 20:15:37", | |
"severity": "Low", | |
"category": "Credentials", | |
"description": "Legacy system credentials posted in hacking forum.", | |
"status": "New", | |
"source": "RaidForums" | |
} | |
] | |
# Create a dataframe for the alerts | |
alert_df = pd.DataFrame(alerts) | |
# Apply colors to severity column | |
def color_severity(val): | |
color_map = { | |
'Critical': '#E74C3C', | |
'High': '#F1C40F', | |
'Medium': '#3498DB', | |
'Low': '#2ECC71' | |
} | |
return f'background-color: {color_map.get(val, "#ECF0F1")}' | |
# Style the dataframe | |
styled_df = alert_df.style.applymap(color_severity, subset=['severity']) | |
# Display the table | |
st.dataframe(styled_df, use_container_width=True, height=300) | |
# Action buttons for alerts | |
action_col1, action_col2, action_col3, action_col4, action_col5 = st.columns(5) | |
with action_col1: | |
st.button("Investigate", key="investigate_alert") | |
with action_col2: | |
st.button("Mark as Resolved", key="resolve_alert") | |
with action_col3: | |
st.button("Assign to Analyst", key="assign_alert") | |
with action_col4: | |
st.button("Mark as False Positive", key="false_positive") | |
with action_col5: | |
st.button("Generate Report", key="generate_report") | |
# Alert visualization | |
st.markdown("### Alert Analytics") | |
# Tabs for different alert visualizations | |
tab1, tab2, tab3 = st.tabs(["Alert Trend", "Category Distribution", "Source Analysis"]) | |
with tab1: | |
# Alert trend over time | |
st.subheader("Alert Trend (Last 30 Days)") | |
# Generate dates for the past 30 days | |
dates = [(datetime.now() - timedelta(days=i)).strftime('%Y-%m-%d') for i in range(30, 0, -1)] | |
# Sample data for alert trends | |
critical_alerts = np.random.randint(5, 12, 30) | |
high_alerts = np.random.randint(8, 20, 30) | |
medium_alerts = np.random.randint(12, 25, 30) | |
low_alerts = np.random.randint(15, 30, 30) | |
trend_data = pd.DataFrame({ | |
'Date': dates, | |
'Critical': critical_alerts, | |
'High': high_alerts, | |
'Medium': medium_alerts, | |
'Low': low_alerts | |
}) | |
# Create a stacked area chart | |
fig = go.Figure() | |
fig.add_trace(go.Scatter( | |
x=trend_data['Date'], y=trend_data['Critical'], | |
mode='lines', | |
line=dict(width=0.5, color='#E74C3C'), | |
stackgroup='one', | |
name='Critical' | |
)) | |
fig.add_trace(go.Scatter( | |
x=trend_data['Date'], y=trend_data['High'], | |
mode='lines', | |
line=dict(width=0.5, color='#F1C40F'), | |
stackgroup='one', | |
name='High' | |
)) | |
fig.add_trace(go.Scatter( | |
x=trend_data['Date'], y=trend_data['Medium'], | |
mode='lines', | |
line=dict(width=0.5, color='#3498DB'), | |
stackgroup='one', | |
name='Medium' | |
)) | |
fig.add_trace(go.Scatter( | |
x=trend_data['Date'], y=trend_data['Low'], | |
mode='lines', | |
line=dict(width=0.5, color='#2ECC71'), | |
stackgroup='one', | |
name='Low' | |
)) | |
fig.update_layout( | |
paper_bgcolor='rgba(26, 26, 26, 0)', | |
plot_bgcolor='rgba(26, 26, 26, 0)', | |
legend=dict( | |
orientation="h", | |
yanchor="bottom", | |
y=1.02, | |
xanchor="right", | |
x=1 | |
), | |
margin=dict(l=0, r=0, t=30, b=0), | |
xaxis=dict( | |
showgrid=False, | |
title=None, | |
tickfont=dict(color='#ECF0F1') | |
), | |
yaxis=dict( | |
showgrid=True, | |
gridcolor='rgba(44, 62, 80, 0.3)', | |
title="Alert Count", | |
tickfont=dict(color='#ECF0F1') | |
), | |
height=400 | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
with tab2: | |
# Alert distribution by category | |
st.subheader("Alert Category Distribution") | |
# Sample data for categories | |
categories = ['Data Breach', 'Credentials', 'PII', 'Ransomware', 'Brand Abuse', 'Source Code', 'Infrastructure', 'Other'] | |
counts = [35, 28, 18, 12, 8, 6, 4, 2] | |
category_data = pd.DataFrame({ | |
'Category': categories, | |
'Count': counts | |
}) | |
# Create a horizontal bar chart | |
fig = px.bar( | |
category_data, | |
y='Category', | |
x='Count', | |
orientation='h', | |
color='Count', | |
color_continuous_scale=['#2ECC71', '#3498DB', '#F1C40F', '#E74C3C'], | |
height=400 | |
) | |
fig.update_layout( | |
paper_bgcolor='rgba(26, 26, 26, 0)', | |
plot_bgcolor='rgba(26, 26, 26, 0)', | |
coloraxis_showscale=False, | |
xaxis=dict( | |
title="Number of Alerts", | |
showgrid=True, | |
gridcolor='rgba(44, 62, 80, 0.3)', | |
tickfont=dict(color='#ECF0F1') | |
), | |
yaxis=dict( | |
title=None, | |
showgrid=False, | |
tickfont=dict(color='#ECF0F1') | |
), | |
margin=dict(l=0, r=0, t=30, b=0) | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
with tab3: | |
# Alert sources analysis | |
st.subheader("Alert Sources") | |
# Sample data for sources | |
sources = ['Dark Web Markets', 'Hacking Forums', 'Paste Sites', 'Telegram Channels', 'Ransomware Blogs', 'IRC Channels', 'Social Media'] | |
source_counts = [32, 27, 18, 15, 10, 7, 4] | |
source_data = pd.DataFrame({ | |
'Source': sources, | |
'Count': source_counts | |
}) | |
# Create a pie chart | |
fig = px.pie( | |
source_data, | |
values='Count', | |
names='Source', | |
hole=0.4, | |
color_discrete_sequence=['#E74C3C', '#F1C40F', '#3498DB', '#2ECC71', '#9B59B6', '#E67E22', '#1ABC9C'] | |
) | |
fig.update_layout( | |
paper_bgcolor='rgba(26, 26, 26, 0)', | |
plot_bgcolor='rgba(26, 26, 26, 0)', | |
showlegend=True, | |
legend=dict( | |
orientation="h", | |
yanchor="bottom", | |
y=-0.2, | |
xanchor="center", | |
x=0.5, | |
font=dict(color='#ECF0F1') | |
), | |
margin=dict(l=0, r=0, t=30, b=0), | |
height=400 | |
) | |
st.plotly_chart(fig, use_container_width=True) | |
# Alert rules configuration | |
st.markdown("---") | |
st.subheader("Alert Rules Configuration") | |
# Tabs for different rule categories | |
rule_tab1, rule_tab2 = st.tabs(["Active Rules", "Rule Editor"]) | |
with rule_tab1: | |
# Sample data for alert rules | |
alert_rules = pd.DataFrame({ | |
"Rule Name": [ | |
"Critical Data Breach Detection", | |
"Ransomware Victim Monitoring", | |
"Employee Credential Exposure", | |
"Source Code Leak Detection", | |
"Brand Impersonation Alert", | |
"Executive PII Monitoring", | |
"Infrastructure Exposure" | |
], | |
"Category": ["Data Breach", "Ransomware", "Credentials", "Source Code", "Brand Abuse", "PII", "Infrastructure"], | |
"Severity": ["Critical", "Critical", "High", "High", "Medium", "Critical", "Medium"], | |
"Sources": ["All", "Leak Sites", "Paste Sites, Forums", "Paste Sites, Forums", "All", "All", "Forums, Markets"], | |
"Status": ["Active", "Active", "Active", "Active", "Active", "Active", "Active"] | |
}) | |
# Display rules table | |
st.dataframe(alert_rules, use_container_width=True) | |
# Rule action buttons | |
rule_col1, rule_col2, rule_col3, rule_col4 = st.columns(4) | |
with rule_col1: | |
st.button("Create New Rule", key="new_rule") | |
with rule_col2: | |
st.button("Edit Selected", key="edit_rule") | |
with rule_col3: | |
st.button("Duplicate", key="duplicate_rule") | |
with rule_col4: | |
st.button("Disable", key="disable_rule") | |
with rule_tab2: | |
# Rule editor form | |
with st.form("rule_editor"): | |
st.markdown("### Rule Editor") | |
rule_name = st.text_input("Rule Name", value="New Alert Rule") | |
editor_col1, editor_col2 = st.columns(2) | |
with editor_col1: | |
rule_category = st.selectbox( | |
"Category", | |
["Data Breach", "Ransomware", "Credentials", "PII", "Brand Abuse", "Source Code", "Infrastructure", "Other"] | |
) | |
rule_severity = st.selectbox( | |
"Severity", | |
["Critical", "High", "Medium", "Low"] | |
) | |
with editor_col2: | |
rule_sources = st.multiselect( | |
"Monitoring Sources", | |
["Dark Web Markets", "Hacking Forums", "Paste Sites", "Leak Sites", "Telegram Channels", "IRC Channels", "Social Media", "All"], | |
default=["All"] | |
) | |
rule_status = st.selectbox( | |
"Status", | |
["Active", "Disabled"] | |
) | |
st.markdown("### Rule Conditions") | |
condition_type = st.selectbox( | |
"Condition Type", | |
["Keyword Match", "Regular Expression", "Data Pattern", "Complex Query"] | |
) | |
if condition_type == "Keyword Match": | |
keywords = st.text_area("Keywords (one per line)", height=100) | |
keyword_options = st.columns(3) | |
with keyword_options[0]: | |
case_sensitive = st.checkbox("Case Sensitive", value=False) | |
with keyword_options[1]: | |
whole_word = st.checkbox("Whole Word Only", value=False) | |
with keyword_options[2]: | |
proximity = st.checkbox("Proximity Search", value=False) | |
elif condition_type == "Regular Expression": | |
regex_pattern = st.text_area("Regular Expression Pattern", height=100) | |
regex_options = st.columns(2) | |
with regex_options[0]: | |
test_regex = st.button("Test RegEx") | |
with regex_options[1]: | |
validate_regex = st.button("Validate Pattern") | |
elif condition_type == "Data Pattern": | |
data_patterns = st.multiselect( | |
"Data Patterns to Detect", | |
["Email Addresses", "Credit Card Numbers", "Social Security Numbers", "Phone Numbers", "IP Addresses", "API Keys", "Passwords"] | |
) | |
elif condition_type == "Complex Query": | |
complex_query = st.text_area("Complex Query", height=100, | |
placeholder="Example: (keyword1 OR keyword2) AND (keyword3) NOT (keyword4)") | |
st.markdown("### Response Actions") | |
notification_channels = st.multiselect( | |
"Notification Channels", | |
["Email", "Slack", "API Webhook", "SMS"], | |
default=["Email", "Slack"] | |
) | |
auto_actions = st.multiselect( | |
"Automated Actions", | |
["Create Incident Ticket", "Add to Watchlist", "Block in Firewall", "None"], | |
default=["Create Incident Ticket"] | |
) | |
submit_rule = st.form_submit_button("Save Rule") | |
if submit_rule: | |
st.success("Alert rule saved successfully!") | |
# Alert notification settings | |
st.markdown("---") | |
st.subheader("Alert Notification Settings") | |
# Notification channels | |
notif_col1, notif_col2 = st.columns(2) | |
with notif_col1: | |
st.markdown("### Notification Channels") | |
with st.container(): | |
st.checkbox("Email Notifications", value=True) | |
st.text_input("Email Recipients", value="[email protected], [email protected]") | |
st.checkbox("Slack Notifications", value=True) | |
st.text_input("Slack Channel", value="#security-alerts") | |
st.checkbox("SMS Notifications", value=False) | |
st.text_input("Phone Numbers", placeholder="+1234567890, +0987654321") | |
st.checkbox("API Webhook", value=False) | |
st.text_input("Webhook URL", placeholder="https://api.example.com/webhook") | |
with notif_col2: | |
st.markdown("### Notification Schedule") | |
with st.container(): | |
notify_critical = st.radio( | |
"Critical Alerts", | |
["Immediate", "Hourly Digest", "Daily Digest"], | |
index=0 | |
) | |
notify_high = st.radio( | |
"High Alerts", | |
["Immediate", "Hourly Digest", "Daily Digest"], | |
index=1 | |
) | |
notify_medium = st.radio( | |
"Medium Alerts", | |
["Immediate", "Hourly Digest", "Daily Digest"], | |
index=2 | |
) | |
notify_low = st.radio( | |
"Low Alerts", | |
["Immediate", "Hourly Digest", "Daily Digest"], | |
index=2 | |
) | |
# Save alert settings button | |
st.button("Save Notification Settings", type="primary", key="save_notif") | |