AYI-NEDJIMI's picture
Initial SOC Analyst Explorer Space
5675313 verified
Raw
History Blame Contribute Delete
18.7 kB
import gradio as gr
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from datasets import load_dataset
# ---------------------------------------------------------------------------
# Data loading
# ---------------------------------------------------------------------------
def load_data(lang="en"):
repo = "AYI-NEDJIMI/soc-analyst-fr" if lang == "fr" else "AYI-NEDJIMI/soc-analyst-en"
try:
ds = load_dataset(repo, split="train")
df = ds.to_pandas()
except Exception as e:
print(f"Error loading {repo}: {e}")
df = pd.DataFrame()
return df
DF_EN = load_data("en")
DF_FR = load_data("fr")
def get_df(lang):
return DF_FR.copy() if lang == "fr" else DF_EN.copy()
def safe_col(df, col):
"""Return column values or empty series."""
if col in df.columns:
return df[col]
return pd.Series(dtype=str)
def unique_vals(df, col):
if col not in df.columns:
return []
vals = df[col].dropna().unique().tolist()
return sorted([str(v) for v in vals if str(v).strip()])
# ---------------------------------------------------------------------------
# Labels (i18n)
# ---------------------------------------------------------------------------
LABELS = {
"en": {
"title": "🛡️ SOC Analyst & SOC 2 Explorer",
"subtitle": "Explore 147 entries: playbooks, SIEM use cases, triage procedures, SOC 2 controls, tools & Q&A",
"lang_label": "Language",
"tab_playbooks": "SOC Playbooks",
"tab_siem": "SIEM Use Cases",
"tab_triage": "Triage Procedures",
"tab_soc2": "SOC 2 Controls",
"tab_tools": "Tools",
"tab_qa": "Q&A",
"tab_stats": "Statistics",
"filter_alert_type": "Alert Type",
"filter_severity": "Severity",
"filter_siem": "SIEM Platform",
"filter_alert_cat": "Alert Category",
"filter_trust": "Trust Criteria",
"filter_tool_cat": "Category",
"filter_difficulty": "Difficulty",
"all": "All",
"no_results": "No results found.",
"search": "Search keyword",
},
"fr": {
"title": "🛡️ SOC Analyst & SOC 2 Explorer",
"subtitle": "Explorez 147 entrées : playbooks, cas d'usage SIEM, procédures de triage, contrôles SOC 2, outils & Q&R",
"lang_label": "Langue",
"tab_playbooks": "Playbooks SOC",
"tab_siem": "Cas d'usage SIEM",
"tab_triage": "Procédures de triage",
"tab_soc2": "Contrôles SOC 2",
"tab_tools": "Outils",
"tab_qa": "Q&R",
"tab_stats": "Statistiques",
"filter_alert_type": "Type d'alerte",
"filter_severity": "Sévérité",
"filter_siem": "Plateforme SIEM",
"filter_alert_cat": "Catégorie d'alerte",
"filter_trust": "Critère de confiance",
"filter_tool_cat": "Catégorie",
"filter_difficulty": "Difficulté",
"all": "Tous",
"no_results": "Aucun résultat.",
"search": "Mot-clé de recherche",
},
}
def L(key, lang):
return LABELS.get(lang, LABELS["en"]).get(key, key)
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def fmt_list(val):
"""Format a value that might be a list, string, or None into readable text."""
if val is None or (isinstance(val, float) and pd.isna(val)):
return ""
if isinstance(val, list):
return "\n".join(f"• {v}" for v in val)
s = str(val).strip()
if not s or s.lower() == "nan":
return ""
return s
def fmt_code(val):
if val is None or (isinstance(val, float) and pd.isna(val)):
return ""
s = str(val).strip()
if not s or s.lower() == "nan":
return ""
return f"```\n{s}\n```"
def filter_df(df, type_col, type_val, filters: dict):
sub = df[df[type_col] == type_val].copy() if type_col and type_val else df.copy()
for col, val in filters.items():
if val and val not in ("All", "Tous", "") and col in sub.columns:
sub = sub[sub[col].astype(str) == val]
return sub
# ---------------------------------------------------------------------------
# Tab renderers
# ---------------------------------------------------------------------------
def render_playbooks(lang, alert_type, severity):
df = get_df(lang)
sub = filter_df(df, "type", "playbook", {})
if alert_type and alert_type not in ("All", "Tous"):
sub = sub[safe_col(sub, "alert_type").astype(str) == alert_type]
if severity and severity not in ("All", "Tous"):
sub = sub[safe_col(sub, "severity").astype(str) == severity]
if sub.empty:
return L("no_results", lang)
blocks = []
for _, row in sub.iterrows():
title = fmt_list(row.get("alert_type", "Playbook"))
sev = fmt_list(row.get("severity", ""))
blocks.append(f"## {title} \n**Severity:** {sev}\n")
for field, label in [("triage_steps", "Triage Steps"), ("investigation_steps", "Investigation Steps"),
("containment_actions", "Containment Actions"), ("mitre_techniques", "MITRE Techniques"),
("tools_required", "Tools Required"), ("sla_minutes", "SLA (minutes)")]:
v = fmt_list(row.get(field))
if v:
blocks.append(f"### {label}\n{v}\n")
blocks.append("---\n")
return "\n".join(blocks)
def render_siem(lang, platform):
df = get_df(lang)
sub = filter_df(df, "type", "siem_usecase", {})
if platform and platform not in ("All", "Tous"):
sub = sub[safe_col(sub, "siem_platform").astype(str) == platform]
if sub.empty:
return L("no_results", lang)
blocks = []
for _, row in sub.iterrows():
name = fmt_list(row.get("use_case_name", ""))
plat = fmt_list(row.get("siem_platform", ""))
blocks.append(f"## {name} \n**Platform:** {plat}\n")
for field, label in [("detection_logic", "Detection Logic"), ("data_sources", "Data Sources")]:
v = fmt_list(row.get(field))
if v:
blocks.append(f"### {label}\n{v}\n")
q = fmt_code(row.get("query_example"))
if q:
blocks.append(f"### Query Example\n{q}\n")
blocks.append("---\n")
return "\n".join(blocks)
def render_triage(lang, alert_cat):
df = get_df(lang)
sub = filter_df(df, "type", "triage", {})
if alert_cat and alert_cat not in ("All", "Tous"):
sub = sub[safe_col(sub, "alert_category").astype(str) == alert_cat]
if sub.empty:
return L("no_results", lang)
blocks = []
for _, row in sub.iterrows():
cat = fmt_list(row.get("alert_category", ""))
blocks.append(f"## {cat}\n")
for field, label in [("priority_matrix", "Priority Matrix"), ("escalation_path", "Escalation Path"),
("sla_minutes", "SLA (minutes)"), ("triage_steps", "Triage Steps")]:
v = fmt_list(row.get(field))
if v:
blocks.append(f"### {label}\n{v}\n")
blocks.append("---\n")
return "\n".join(blocks)
def render_soc2(lang, trust):
df = get_df(lang)
sub = filter_df(df, "type", "soc2_control", {})
if trust and trust not in ("All", "Tous"):
sub = sub[safe_col(sub, "trust_criteria").astype(str) == trust]
if sub.empty:
return L("no_results", lang)
blocks = []
for _, row in sub.iterrows():
cid = fmt_list(row.get("control_id", ""))
cname = fmt_list(row.get("control_name", ""))
blocks.append(f"## {cid} — {cname}\n")
tc = fmt_list(row.get("trust_criteria", ""))
if tc:
blocks.append(f"**Trust Criteria:** {tc}\n")
ev = fmt_list(row.get("evidence_required", ""))
if ev:
blocks.append(f"### Evidence Required\n{ev}\n")
blocks.append("---\n")
return "\n".join(blocks)
def render_tools(lang, category):
df = get_df(lang)
sub = filter_df(df, "type", "tool", {})
if category and category not in ("All", "Tous"):
sub = sub[safe_col(sub, "category").astype(str) == category]
if sub.empty:
return L("no_results", lang)
blocks = []
for _, row in sub.iterrows():
name = fmt_list(row.get("name", ""))
vendor = fmt_list(row.get("vendor", ""))
blocks.append(f"## {name} \n**Vendor:** {vendor}\n")
for field, label in [("features", "Features"), ("strengths", "Strengths"),
("weaknesses", "Weaknesses"), ("category", "Category")]:
v = fmt_list(row.get(field))
if v:
blocks.append(f"### {label}\n{v}\n")
blocks.append("---\n")
return "\n".join(blocks)
def render_qa(lang, difficulty, keyword):
df = get_df(lang)
sub = filter_df(df, "type", "qa", {})
if difficulty and difficulty not in ("All", "Tous"):
sub = sub[safe_col(sub, "difficulty").astype(str) == difficulty]
if keyword and keyword.strip():
kw = keyword.strip().lower()
mask = sub.apply(lambda r: kw in str(r.get("question", "")).lower() or kw in str(r.get("answer", "")).lower(), axis=1)
sub = sub[mask]
if sub.empty:
return L("no_results", lang)
blocks = []
for _, row in sub.iterrows():
q = fmt_list(row.get("question", ""))
a = fmt_list(row.get("answer", ""))
d = fmt_list(row.get("difficulty", ""))
blocks.append(f"### Q: {q}\n**Difficulty:** {d}\n\n**A:** {a}\n\n---\n")
return "\n".join(blocks)
# ---------------------------------------------------------------------------
# Statistics
# ---------------------------------------------------------------------------
def render_stats(lang):
df = get_df(lang)
figs = []
# Severity distribution (playbooks + triage)
if "severity" in df.columns:
sev = df["severity"].dropna()
if not sev.empty:
counts = sev.value_counts().reset_index()
counts.columns = ["Severity", "Count"]
fig1 = px.bar(counts, x="Severity", y="Count", title="Distribution by Severity",
color="Severity", color_discrete_sequence=px.colors.qualitative.Set2)
figs.append(fig1)
# SIEM platform distribution
if "siem_platform" in df.columns:
sp = df["siem_platform"].dropna()
if not sp.empty:
counts = sp.value_counts().reset_index()
counts.columns = ["Platform", "Count"]
fig2 = px.pie(counts, names="Platform", values="Count", title="SIEM Platform Distribution",
color_discrete_sequence=px.colors.qualitative.Pastel)
figs.append(fig2)
# Trust criteria distribution
if "trust_criteria" in df.columns:
tc = df["trust_criteria"].dropna()
if not tc.empty:
counts = tc.value_counts().reset_index()
counts.columns = ["Trust Criteria", "Count"]
fig3 = px.bar(counts, x="Trust Criteria", y="Count", title="SOC 2 Trust Criteria Distribution",
color="Trust Criteria", color_discrete_sequence=px.colors.qualitative.Bold)
figs.append(fig3)
# Entry type distribution
if "type" in df.columns:
tp = df["type"].dropna()
if not tp.empty:
counts = tp.value_counts().reset_index()
counts.columns = ["Type", "Count"]
fig4 = px.pie(counts, names="Type", values="Count", title="Entry Type Distribution",
color_discrete_sequence=px.colors.qualitative.Vivid)
figs.append(fig4)
return figs
# ---------------------------------------------------------------------------
# Dropdown choice helpers
# ---------------------------------------------------------------------------
def choices_for(col, type_val, lang, include_all=True):
df = get_df(lang)
sub = df[df["type"] == type_val] if "type" in df.columns and type_val else df
vals = unique_vals(sub, col)
if include_all:
vals = [L("all", lang)] + vals
return vals
# ---------------------------------------------------------------------------
# Footer
# ---------------------------------------------------------------------------
FOOTER = """
<div style="text-align:center; padding:20px; margin-top:30px; border-top:1px solid #444; font-size:0.9em; color:#aaa;">
<p><strong>SOC Analyst & SOC 2 Explorer</strong> — Built by <a href="https://ayinedjimi-consultants.fr" target="_blank">AYI-NEDJIMI Consultants</a></p>
<p>
<a href="https://ayinedjimi-consultants.fr" target="_blank">🌐 Website</a> &nbsp;|&nbsp;
<a href="https://www.linkedin.com/in/ayinedjimi/" target="_blank">💼 LinkedIn</a> &nbsp;|&nbsp;
<a href="https://github.com/AYI-NEDJIMI" target="_blank">🐙 GitHub</a> &nbsp;|&nbsp;
<a href="https://x.com/AYI_NEDJIMI" target="_blank">🐦 X / Twitter</a>
</p>
</div>
"""
# ---------------------------------------------------------------------------
# Build the Gradio App
# ---------------------------------------------------------------------------
def build_app():
with gr.Blocks(
title="SOC Analyst & SOC 2 Explorer",
theme=gr.themes.Soft(primary_hue="blue", secondary_hue="cyan"),
) as demo:
gr.Markdown("# 🛡️ SOC Analyst & SOC 2 Explorer")
gr.Markdown("Explore **147 entries**: playbooks, SIEM use cases, triage procedures, SOC 2 controls, tools & Q&A. \nDatasets: [`soc-analyst-en`](https://huggingface.co/datasets/AYI-NEDJIMI/soc-analyst-en) | [`soc-analyst-fr`](https://huggingface.co/datasets/AYI-NEDJIMI/soc-analyst-fr)")
lang = gr.Radio(choices=["en", "fr"], value="en", label="Language / Langue", interactive=True)
# ---- TAB 1: Playbooks ----
with gr.Tab("SOC Playbooks"):
with gr.Row():
pb_alert = gr.Dropdown(choices=choices_for("alert_type", "playbook", "en"), label="Alert Type", value="All", interactive=True)
pb_sev = gr.Dropdown(choices=choices_for("severity", "playbook", "en"), label="Severity", value="All", interactive=True)
pb_out = gr.Markdown(value=render_playbooks("en", "All", "All"))
pb_alert.change(render_playbooks, [lang, pb_alert, pb_sev], pb_out)
pb_sev.change(render_playbooks, [lang, pb_alert, pb_sev], pb_out)
# ---- TAB 2: SIEM Use Cases ----
with gr.Tab("SIEM Use Cases"):
siem_plat = gr.Dropdown(choices=choices_for("siem_platform", "siem_usecase", "en"), label="SIEM Platform", value="All", interactive=True)
siem_out = gr.Markdown(value=render_siem("en", "All"))
siem_plat.change(render_siem, [lang, siem_plat], siem_out)
# ---- TAB 3: Triage Procedures ----
with gr.Tab("Triage Procedures"):
tri_cat = gr.Dropdown(choices=choices_for("alert_category", "triage", "en"), label="Alert Category", value="All", interactive=True)
tri_out = gr.Markdown(value=render_triage("en", "All"))
tri_cat.change(render_triage, [lang, tri_cat], tri_out)
# ---- TAB 4: SOC 2 Controls ----
with gr.Tab("SOC 2 Controls"):
soc2_trust = gr.Dropdown(choices=choices_for("trust_criteria", "soc2_control", "en"), label="Trust Criteria", value="All", interactive=True)
soc2_out = gr.Markdown(value=render_soc2("en", "All"))
soc2_trust.change(render_soc2, [lang, soc2_trust], soc2_out)
# ---- TAB 5: Tools ----
with gr.Tab("Tools"):
tool_cat = gr.Dropdown(choices=choices_for("category", "tool", "en"), label="Category", value="All", interactive=True)
tool_out = gr.Markdown(value=render_tools("en", "All"))
tool_cat.change(render_tools, [lang, tool_cat], tool_out)
# ---- TAB 6: Q&A ----
with gr.Tab("Q&A"):
with gr.Row():
qa_diff = gr.Dropdown(choices=choices_for("difficulty", "qa", "en"), label="Difficulty", value="All", interactive=True)
qa_kw = gr.Textbox(label="Search keyword", value="", interactive=True)
qa_out = gr.Markdown(value=render_qa("en", "All", ""))
qa_diff.change(render_qa, [lang, qa_diff, qa_kw], qa_out)
qa_kw.submit(render_qa, [lang, qa_diff, qa_kw], qa_out)
# ---- TAB 7: Statistics ----
with gr.Tab("Statistics"):
stats_btn = gr.Button("Refresh Statistics")
stats_plots = [gr.Plot() for _ in range(4)]
def update_stats(lang_val):
figs = render_stats(lang_val)
# Pad to 4 outputs
while len(figs) < 4:
figs.append(go.Figure())
return figs[:4]
stats_btn.click(update_stats, [lang], stats_plots)
# Auto-load on start
demo.load(update_stats, [lang], stats_plots)
# ---- Language toggle: refresh all dropdowns & content ----
def on_lang_change(lang_val):
return (
# Dropdown choices
gr.update(choices=choices_for("alert_type", "playbook", lang_val), value=L("all", lang_val)),
gr.update(choices=choices_for("severity", "playbook", lang_val), value=L("all", lang_val)),
render_playbooks(lang_val, L("all", lang_val), L("all", lang_val)),
gr.update(choices=choices_for("siem_platform", "siem_usecase", lang_val), value=L("all", lang_val)),
render_siem(lang_val, L("all", lang_val)),
gr.update(choices=choices_for("alert_category", "triage", lang_val), value=L("all", lang_val)),
render_triage(lang_val, L("all", lang_val)),
gr.update(choices=choices_for("trust_criteria", "soc2_control", lang_val), value=L("all", lang_val)),
render_soc2(lang_val, L("all", lang_val)),
gr.update(choices=choices_for("category", "tool", lang_val), value=L("all", lang_val)),
render_tools(lang_val, L("all", lang_val)),
gr.update(choices=choices_for("difficulty", "qa", lang_val), value=L("all", lang_val)),
render_qa(lang_val, L("all", lang_val), ""),
)
lang.change(
on_lang_change,
[lang],
[pb_alert, pb_sev, pb_out, siem_plat, siem_out, tri_cat, tri_out,
soc2_trust, soc2_out, tool_cat, tool_out, qa_diff, qa_out],
)
# Footer
gr.HTML(FOOTER)
return demo
if __name__ == "__main__":
demo = build_app()
demo.launch()