import gradio as gr import pandas as pd import plotly.express as px import plotly.graph_objects as go from datasets import load_dataset import json import re # --------------------------------------------------------------------------- # Load datasets # --------------------------------------------------------------------------- def load_data(lang="en"): """Load the bug-bounty-pentest dataset for the given language.""" repo = f"AYI-NEDJIMI/bug-bounty-pentest-{lang}" try: ds = load_dataset(repo, split="train") return ds.to_pandas() except Exception as e: print(f"Error loading {repo}: {e}") return pd.DataFrame() df_en = load_data("en") df_fr = load_data("fr") DATASETS = {"EN": df_en, "FR": df_fr} # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def get_df(lang): return DATASETS.get(lang, df_en) def safe(val): """Return a cleaned string representation; handle NaN / None / lists.""" if val is None or (isinstance(val, float) and pd.isna(val)): return "" if isinstance(val, list): return "\n".join(str(v) for v in val) return str(val) def safe_json_list(val): """Try to interpret a value as a list (JSON string or Python list).""" if val is None or (isinstance(val, float) and pd.isna(val)): return [] if isinstance(val, list): return val s = str(val).strip() if s.startswith("["): try: return json.loads(s) except Exception: pass return [s] if s else [] def bullet_list(items): """Format a list as a Markdown bullet list.""" lst = safe_json_list(items) if not lst: return "_No items_" return "\n".join(f"- {item}" for item in lst) def filter_type(df, entry_type): subset = df[df["type"] == entry_type] if "type" in df.columns else pd.DataFrame() return subset.reset_index(drop=True) def unique_vals(df, col): if col not in df.columns: return [] vals = df[col].dropna().unique().tolist() return sorted(set(str(v) for v in vals)) # --------------------------------------------------------------------------- # CSS # --------------------------------------------------------------------------- CUSTOM_CSS = """ footer.custom-footer { text-align: center; padding: 18px 10px 10px 10px; font-size: 0.92em; color: #888; border-top: 1px solid #ddd; margin-top: 20px; } footer.custom-footer a { color: #5b7ff5; text-decoration: none; margin: 0 8px; } footer.custom-footer a:hover { text-decoration: underline; } .disclaimer-box { background: #fff3cd; border-left: 4px solid #ffc107; padding: 12px 16px; margin: 10px 0; border-radius: 4px; font-size: 0.95em; color: #856404; } """ FOOTER_HTML = """ """ # --------------------------------------------------------------------------- # Tab builders # --------------------------------------------------------------------------- def build_methodologies(lang): df = filter_type(get_df(lang), "methodology") if df.empty: return "No methodology entries found." parts = [] for _, row in df.iterrows(): name = safe(row.get("methodology_name", "")) org = safe(row.get("organization", "")) phases = bullet_list(row.get("phases")) scope = safe(row.get("scope", "")) targets = safe(row.get("target_types", "")) deliverables = bullet_list(row.get("deliverables")) parts.append( f"## {name}\n" f"**Organization:** {org}\n\n" f"**Scope:** {scope}\n\n" f"**Target Types:** {targets}\n\n" f"### Phases\n{phases}\n\n" f"### Deliverables\n{deliverables}\n\n---\n" ) return "\n".join(parts) if parts else "No methodology entries found." def build_checklists(lang, target_type_filter="All"): df = filter_type(get_df(lang), "checklist") if df.empty: return "No checklist entries found.", [] targets = ["All"] + unique_vals(df, "target_type") if target_type_filter and target_type_filter != "All": df = df[df["target_type"].astype(str) == target_type_filter] parts = [] for _, row in df.iterrows(): cname = safe(row.get("checklist_name", "")) ttype = safe(row.get("target_type", "")) items = bullet_list(row.get("items")) parts.append( f"## {cname}\n**Target Type:** {ttype}\n\n### Items\n{items}\n\n---\n" ) md = "\n".join(parts) if parts else "No checklists match the filter." return md, targets def build_techniques(lang, category_filter="All"): df = filter_type(get_df(lang), "technique") if df.empty: return "No technique entries found.", [] cats = ["All"] + unique_vals(df, "category") if category_filter and category_filter != "All": df = df[df["category"].astype(str) == category_filter] parts = [] for _, row in df.iterrows(): tname = safe(row.get("technique_name", "")) cat = safe(row.get("category", "")) impact = safe(row.get("impact", "")) steps = bullet_list(row.get("exploitation_steps")) payloads = bullet_list(row.get("payload_examples")) remed = safe(row.get("remediation", "")) bounty = safe(row.get("bounty_range_usd", "")) cvss = safe(row.get("cvss_range", "")) parts.append( f"## {tname}\n" f"**Category:** {cat}  |  **Impact:** {impact}  |  " f"**CVSS:** {cvss}  |  **Bounty Range (USD):** {bounty}\n\n" f"### Exploitation Steps\n{steps}\n\n" f"### Payload Examples\n{payloads}\n\n" f"### Remediation\n{remed}\n\n---\n" ) md = "\n".join(parts) if parts else "No techniques match the filter." return md, cats def build_platforms(lang): df = filter_type(get_df(lang), "platform") if df.empty: return "No platform entries found." parts = [] for _, row in df.iterrows(): pname = safe(row.get("platform_name", "")) payout = safe(row.get("payout_model", "")) programs = bullet_list(row.get("top_programs")) vtype = safe(row.get("vulnerability_type", "")) bounty = safe(row.get("bounty_range_usd", "")) parts.append( f"## {pname}\n" f"**Payout Model:** {payout}\n\n" f"**Average Bounty Range:** {bounty}\n\n" f"### Top Programs\n{programs}\n\n---\n" ) return "\n".join(parts) if parts else "No platform entries found." def build_reports(lang, vuln_filter="All"): df = filter_type(get_df(lang), "report_template") if df.empty: return "No report template entries found.", [] vtypes = ["All"] + unique_vals(df, "vulnerability_type") if vuln_filter and vuln_filter != "All": df = df[df["vulnerability_type"].astype(str) == vuln_filter] parts = [] for _, row in df.iterrows(): title = safe(row.get("title_template", "")) vtype = safe(row.get("vulnerability_type", "")) steps = bullet_list(row.get("steps_to_reproduce")) impact = safe(row.get("impact", "")) remed = safe(row.get("remediation", "")) bounty = safe(row.get("bounty_range_usd", "")) cvss = safe(row.get("cvss_range", "")) parts.append( f"## {title}\n" f"**Vulnerability Type:** {vtype}  |  **CVSS:** {cvss}  |  " f"**Bounty Range (USD):** {bounty}\n\n" f"### Steps to Reproduce\n{steps}\n\n" f"### Impact\n{impact}\n\n" f"### Remediation\n{remed}\n\n---\n" ) md = "\n".join(parts) if parts else "No report templates match the filter." return md, vtypes def build_tools(lang, category_filter="All"): df = filter_type(get_df(lang), "tool") if df.empty: return "No tool entries found.", [] cats = ["All"] + unique_vals(df, "category") if category_filter and category_filter != "All": df = df[df["category"].astype(str) == category_filter] parts = [] for _, row in df.iterrows(): name = safe(row.get("name", "")) desc = safe(row.get("description", "")) cat = safe(row.get("category", "")) usage = bullet_list(row.get("usage_examples")) parts.append( f"## {name}\n**Category:** {cat}\n\n{desc}\n\n" f"### Usage Examples\n{usage}\n\n---\n" ) md = "\n".join(parts) if parts else "No tools match the filter." return md, cats def build_qa(lang): df = filter_type(get_df(lang), "qa") if df.empty: return "No Q&A entries found." parts = [] for _, row in df.iterrows(): q = safe(row.get("question", "")) a = safe(row.get("answer", "")) diff = safe(row.get("difficulty", "")) badge = "" if diff: badge = f" `{diff}`" parts.append(f"### ❓ {q}{badge}\n\n{a}\n\n---\n") return "\n".join(parts) if parts else "No Q&A entries found." # --------------------------------------------------------------------------- # Statistics # --------------------------------------------------------------------------- def build_statistics(lang): df = get_df(lang) if df.empty: return None, None, None, "No data available for statistics." figs = [] # 1. Entry type distribution if "type" in df.columns: type_counts = df["type"].value_counts().reset_index() type_counts.columns = ["Type", "Count"] fig1 = px.pie(type_counts, names="Type", values="Count", title="Entry Type Distribution", color_discrete_sequence=px.colors.qualitative.Set2) fig1.update_layout(template="plotly_white") else: fig1 = go.Figure() figs.append(fig1) # 2. Technique category distribution tech_df = filter_type(df, "technique") if not tech_df.empty and "category" in tech_df.columns: cat_counts = tech_df["category"].value_counts().reset_index() cat_counts.columns = ["Category", "Count"] fig2 = px.bar(cat_counts, x="Category", y="Count", title="Technique Distribution by Category", color="Category", color_discrete_sequence=px.colors.qualitative.Vivid) fig2.update_layout(template="plotly_white", showlegend=False) else: fig2 = go.Figure() figs.append(fig2) # 3. Bounty range chart bounty_rows = df.dropna(subset=["bounty_range_usd"]) if "bounty_range_usd" in df.columns else pd.DataFrame() if not bounty_rows.empty: labels = [] lows = [] highs = [] for _, row in bounty_rows.iterrows(): label = safe(row.get("technique_name") or row.get("title_template") or row.get("platform_name") or row.get("name") or "") br = safe(row.get("bounty_range_usd", "")) nums = re.findall(r"[\d,]+", br.replace(",", "")) if len(nums) >= 2: try: lows.append(int(nums[0])) highs.append(int(nums[1])) labels.append(label[:40]) except ValueError: pass elif len(nums) == 1: try: lows.append(0) highs.append(int(nums[0])) labels.append(label[:40]) except ValueError: pass if labels: bdf = pd.DataFrame({"Label": labels, "Min": lows, "Max": highs}) bdf = bdf.sort_values("Max", ascending=True).tail(20) fig3 = go.Figure() fig3.add_trace(go.Bar(y=bdf["Label"], x=bdf["Min"], orientation="h", name="Min", marker_color="#66bb6a")) fig3.add_trace(go.Bar(y=bdf["Label"], x=bdf["Max"] - bdf["Min"], orientation="h", name="Max", marker_color="#ef5350", base=bdf["Min"])) fig3.update_layout(title="Bounty Ranges (USD) – Top 20", barmode="stack", template="plotly_white", height=max(400, len(bdf)*28), yaxis=dict(automargin=True)) else: fig3 = go.Figure() else: fig3 = go.Figure() figs.append(fig3) # 4. Difficulty distribution for Q&A qa_df = filter_type(df, "qa") if not qa_df.empty and "difficulty" in qa_df.columns: diff_counts = qa_df["difficulty"].value_counts().reset_index() diff_counts.columns = ["Difficulty", "Count"] fig4 = px.pie(diff_counts, names="Difficulty", values="Count", title="Q&A Difficulty Distribution", color_discrete_sequence=px.colors.qualitative.Pastel) fig4.update_layout(template="plotly_white") else: fig4 = go.Figure() figs.append(fig4) total = len(df) type_summary = "" if "type" in df.columns: for t, c in df["type"].value_counts().items(): type_summary += f"- **{t}**: {c} entries\n" summary = f"### Dataset Summary ({lang})\n**Total entries:** {total}\n\n{type_summary}" return figs[0], figs[1], figs[2], figs[3], summary # --------------------------------------------------------------------------- # Gradio UI # --------------------------------------------------------------------------- def create_app(): with gr.Blocks( title="Bug Bounty & Pentesting Explorer", css=CUSTOM_CSS, theme=gr.themes.Soft(primary_hue="red", secondary_hue="purple"), ) as app: gr.Markdown( "# 🛡️ Bug Bounty & Pentesting Explorer\n" "Browse **146 entries** covering methodologies, checklists, attack techniques, " "platforms, report templates, tools, and Q&A — in **French** and **English**." ) gr.HTML('
⚠️ Disclaimer: All content is intended for authorized security testing and educational purposes only. Unauthorized access to computer systems is illegal.
') lang = gr.Radio(["EN", "FR"], value="EN", label="🌐 Language / Langue", interactive=True) # ---- Tabs ---- with gr.Tabs(): # 1. Methodologies with gr.Tab("📋 Methodologies"): meth_output = gr.Markdown() def refresh_meth(l): return build_methodologies(l) lang.change(refresh_meth, inputs=lang, outputs=meth_output) app.load(refresh_meth, inputs=lang, outputs=meth_output) # 2. Checklists with gr.Tab("✅ Checklists"): cl_filter = gr.Dropdown(choices=["All", "Web", "API", "Mobile", "Cloud", "AD"], value="All", label="Filter by Target Type", interactive=True) cl_output = gr.Markdown() def refresh_cl(l, f): md, targets = build_checklists(l, f) return md cl_filter.change(refresh_cl, inputs=[lang, cl_filter], outputs=cl_output) lang.change(refresh_cl, inputs=[lang, cl_filter], outputs=cl_output) app.load(refresh_cl, inputs=[lang, cl_filter], outputs=cl_output) # 3. Attack Techniques with gr.Tab("⚔️ Attack Techniques"): tech_filter = gr.Dropdown(choices=["All"], value="All", label="Filter by Category", interactive=True) tech_output = gr.Markdown() def refresh_tech(l, f): md, cats = build_techniques(l, f) return md, gr.update(choices=cats, value=f if f in cats else "All") def init_tech(l): md, cats = build_techniques(l, "All") return md, gr.update(choices=cats, value="All") tech_filter.change(refresh_tech, inputs=[lang, tech_filter], outputs=[tech_output, tech_filter]) lang.change(init_tech, inputs=lang, outputs=[tech_output, tech_filter]) app.load(init_tech, inputs=lang, outputs=[tech_output, tech_filter]) # 4. Platforms with gr.Tab("🏢 Platforms"): plat_output = gr.Markdown() def refresh_plat(l): return build_platforms(l) lang.change(refresh_plat, inputs=lang, outputs=plat_output) app.load(refresh_plat, inputs=lang, outputs=plat_output) # 5. Report Templates with gr.Tab("📝 Report Templates"): rpt_filter = gr.Dropdown(choices=["All"], value="All", label="Filter by Vulnerability Type", interactive=True) rpt_output = gr.Markdown() def refresh_rpt(l, f): md, vtypes = build_reports(l, f) return md, gr.update(choices=vtypes, value=f if f in vtypes else "All") def init_rpt(l): md, vtypes = build_reports(l, "All") return md, gr.update(choices=vtypes, value="All") rpt_filter.change(refresh_rpt, inputs=[lang, rpt_filter], outputs=[rpt_output, rpt_filter]) lang.change(init_rpt, inputs=lang, outputs=[rpt_output, rpt_filter]) app.load(init_rpt, inputs=lang, outputs=[rpt_output, rpt_filter]) # 6. Tools with gr.Tab("🔧 Tools"): tool_filter = gr.Dropdown(choices=["All"], value="All", label="Filter by Category", interactive=True) tool_output = gr.Markdown() def refresh_tools(l, f): md, cats = build_tools(l, f) return md, gr.update(choices=cats, value=f if f in cats else "All") def init_tools(l): md, cats = build_tools(l, "All") return md, gr.update(choices=cats, value="All") tool_filter.change(refresh_tools, inputs=[lang, tool_filter], outputs=[tool_output, tool_filter]) lang.change(init_tools, inputs=lang, outputs=[tool_output, tool_filter]) app.load(init_tools, inputs=lang, outputs=[tool_output, tool_filter]) # 7. Q&A with gr.Tab("❓ Q&A"): qa_output = gr.Markdown() def refresh_qa(l): return build_qa(l) lang.change(refresh_qa, inputs=lang, outputs=qa_output) app.load(refresh_qa, inputs=lang, outputs=qa_output) # 8. Statistics with gr.Tab("📊 Statistics"): stats_summary = gr.Markdown() stats_fig1 = gr.Plot(label="Entry Type Distribution") stats_fig2 = gr.Plot(label="Technique Category Distribution") stats_fig3 = gr.Plot(label="Bounty Ranges (USD)") stats_fig4 = gr.Plot(label="Q&A Difficulty Distribution") def refresh_stats(l): f1, f2, f3, f4, summary = build_statistics(l) return f1, f2, f3, f4, summary lang.change(refresh_stats, inputs=lang, outputs=[stats_fig1, stats_fig2, stats_fig3, stats_fig4, stats_summary]) app.load(refresh_stats, inputs=lang, outputs=[stats_fig1, stats_fig2, stats_fig3, stats_fig4, stats_summary]) # Footer gr.HTML(FOOTER_HTML) return app # --------------------------------------------------------------------------- # Launch # --------------------------------------------------------------------------- if __name__ == "__main__": app = create_app() app.launch()