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 # --------------------------------------------------------------------------- DATASETS = { "FR": "AYI-NEDJIMI/devsecops-pipeline-fr", "EN": "AYI-NEDJIMI/devsecops-pipeline-en", } _cache = {} def _load(lang: str) -> pd.DataFrame: if lang not in _cache: try: ds = load_dataset(DATASETS[lang], split="train") _cache[lang] = ds.to_pandas() except Exception as e: print(f"Error loading {lang} dataset: {e}") _cache[lang] = pd.DataFrame() return _cache[lang] def _safe(df: pd.DataFrame, col: str): """Return column values if it exists, else empty Series.""" if col in df.columns: return df[col].fillna("") return pd.Series([""] * len(df), index=df.index) def _filter_type(lang, t): df = _load(lang) if "type" in df.columns: return df[df["type"] == t].reset_index(drop=True) return df # --------------------------------------------------------------------------- # Helper – build a display DataFrame with only the requested columns # --------------------------------------------------------------------------- def _display(df, cols): present = [c for c in cols if c in df.columns] out = df[present].copy() if present else pd.DataFrame() return out # --------------------------------------------------------------------------- # Tab builders # --------------------------------------------------------------------------- def tab_practices(lang, maturity_filter): df = _filter_type(lang, "practice") if maturity_filter and maturity_filter != "All": df = df[_safe(df, "maturity_level") == maturity_filter] cols = ["practice_name", "description", "benefits", "tools", "metrics"] return _display(df, cols) def tab_sast(lang): df = _filter_type(lang, "sast_tool") cols = ["name", "vendor", "supported_languages", "ci_integration", "strengths", "weaknesses"] return _display(df, cols) def tab_dast(lang): df = _filter_type(lang, "dast_tool") cols = ["name", "vendor", "scan_types", "api_support", "strengths", "weaknesses"] return _display(df, cols) def tab_sca(lang): df = _filter_type(lang, "sca_tool") cols = ["name", "vendor", "package_managers_supported", "vulnerability_db", "strengths", "weaknesses"] return _display(df, cols) def tab_pipeline(lang, ci_filter): df = _filter_type(lang, "pipeline_template") if ci_filter and ci_filter != "All": df = df[_safe(df, "ci_platform") == ci_filter] cols = ["pipeline_name", "stages", "yaml_example", "security_gates"] return _display(df, cols) def tab_container(lang): df = _filter_type(lang, "container_security") cols = ["category", "name", "implementation", "best_practices", "common_misconfigurations"] return _display(df, cols) def tab_secret(lang): df = _filter_type(lang, "secret_management") cols = ["name", "vendor", "features", "integration", "strengths", "weaknesses"] return _display(df, cols) def tab_qa(lang): df = _filter_type(lang, "qa") cols = ["question", "answer", "difficulty"] return _display(df, cols) # --------------------------------------------------------------------------- # Pipeline detail – show yaml_example in code block # --------------------------------------------------------------------------- def pipeline_detail(lang, ci_filter): df = _filter_type(lang, "pipeline_template") if ci_filter and ci_filter != "All": df = df[_safe(df, "ci_platform") == ci_filter] parts = [] for _, row in df.iterrows(): name = row.get("pipeline_name", "N/A") stages = row.get("stages", "N/A") gates = row.get("security_gates", "N/A") yaml_ex = row.get("yaml_example", "") parts.append(f"### {name}\n\n**Stages:** {stages}\n\n**Security Gates:** {gates}\n\n```yaml\n{yaml_ex}\n```\n\n---\n") return "\n".join(parts) if parts else "No pipeline templates found." # --------------------------------------------------------------------------- # Statistics charts # --------------------------------------------------------------------------- def stats_type_chart(lang): df = _load(lang) if "type" not in df.columns: return go.Figure() counts = df["type"].value_counts().reset_index() counts.columns = ["type", "count"] fig = px.bar(counts, x="type", y="count", title="Entries by Type", color="type", text_auto=True) fig.update_layout(showlegend=False) return fig def stats_ci_chart(lang): df = _load(lang) if "ci_platform" not in df.columns: return go.Figure() sub = df[df["ci_platform"].notna() & (df["ci_platform"] != "")] if sub.empty: return go.Figure() counts = sub["ci_platform"].value_counts().reset_index() counts.columns = ["ci_platform", "count"] fig = px.pie(counts, names="ci_platform", values="count", title="Pipeline Templates by CI Platform") return fig def stats_maturity_chart(lang): df = _load(lang) if "maturity_level" not in df.columns: return go.Figure() sub = df[df["maturity_level"].notna() & (df["maturity_level"] != "")] if sub.empty: return go.Figure() counts = sub["maturity_level"].value_counts().reset_index() counts.columns = ["maturity_level", "count"] fig = px.bar(counts, x="maturity_level", y="count", title="Practices by Maturity Level", color="maturity_level", text_auto=True) fig.update_layout(showlegend=False) return fig # --------------------------------------------------------------------------- # Dynamic dropdown choices # --------------------------------------------------------------------------- def _maturity_choices(lang): df = _filter_type(lang, "practice") if "maturity_level" in df.columns: vals = sorted(df["maturity_level"].dropna().unique().tolist()) else: vals = [] return ["All"] + vals def _ci_choices(lang): df = _filter_type(lang, "pipeline_template") if "ci_platform" in df.columns: vals = sorted(df["ci_platform"].dropna().unique().tolist()) else: vals = [] return ["All"] + vals # --------------------------------------------------------------------------- # Footer HTML # --------------------------------------------------------------------------- FOOTER_HTML = """

DevSecOps Pipeline Explorer — Built by AYI-NEDJIMI Consultants

🌐 ayinedjimi-consultants.fr | LinkedIn | GitHub | X / Twitter

""" # --------------------------------------------------------------------------- # Gradio App # --------------------------------------------------------------------------- def build_app(): with gr.Blocks( title="DevSecOps Pipeline Explorer", theme=gr.themes.Soft(), ) as demo: gr.Markdown("# DevSecOps Pipeline Explorer\nExplore 130 DevSecOps practices, tools, pipeline templates, and more.") lang = gr.Radio(["EN", "FR"], value="EN", label="Language / Langue", interactive=True) with gr.Tabs(): # ---- 1. Practices ---- with gr.Tab("DevSecOps Practices"): maturity_dd = gr.Dropdown(choices=_maturity_choices("EN"), value="All", label="Filter by Maturity Level") practices_table = gr.Dataframe(value=tab_practices("EN", "All"), label="Practices", wrap=True) btn_practices = gr.Button("Refresh") def _refresh_practices(l, m): return tab_practices(l, m) btn_practices.click(_refresh_practices, [lang, maturity_dd], practices_table) lang.change(lambda l: gr.update(choices=_maturity_choices(l), value="All"), lang, maturity_dd) lang.change(lambda l: tab_practices(l, "All"), lang, practices_table) maturity_dd.change(_refresh_practices, [lang, maturity_dd], practices_table) # ---- 2. SAST ---- with gr.Tab("SAST Tools"): sast_table = gr.Dataframe(value=tab_sast("EN"), label="SAST Tools", wrap=True) lang.change(lambda l: tab_sast(l), lang, sast_table) # ---- 3. DAST ---- with gr.Tab("DAST Tools"): dast_table = gr.Dataframe(value=tab_dast("EN"), label="DAST Tools", wrap=True) lang.change(lambda l: tab_dast(l), lang, dast_table) # ---- 4. SCA ---- with gr.Tab("SCA Tools"): sca_table = gr.Dataframe(value=tab_sca("EN"), label="SCA Tools", wrap=True) lang.change(lambda l: tab_sca(l), lang, sca_table) # ---- 5. Pipeline Templates ---- with gr.Tab("Pipeline Templates"): ci_dd = gr.Dropdown(choices=_ci_choices("EN"), value="All", label="Filter by CI Platform") pipeline_table = gr.Dataframe(value=tab_pipeline("EN", "All"), label="Pipeline Templates", wrap=True) pipeline_md = gr.Markdown(value=pipeline_detail("EN", "All"), label="YAML Details") btn_pipeline = gr.Button("Refresh") def _refresh_pipeline(l, c): return tab_pipeline(l, c), pipeline_detail(l, c) btn_pipeline.click(_refresh_pipeline, [lang, ci_dd], [pipeline_table, pipeline_md]) lang.change(lambda l: gr.update(choices=_ci_choices(l), value="All"), lang, ci_dd) lang.change(lambda l: (tab_pipeline(l, "All"), pipeline_detail(l, "All")), lang, [pipeline_table, pipeline_md]) ci_dd.change(lambda l, c: (tab_pipeline(l, c), pipeline_detail(l, c)), [lang, ci_dd], [pipeline_table, pipeline_md]) # ---- 6. Container Security ---- with gr.Tab("Container Security"): container_table = gr.Dataframe(value=tab_container("EN"), label="Container Security", wrap=True) lang.change(lambda l: tab_container(l), lang, container_table) # ---- 7. Secret Management ---- with gr.Tab("Secret Management"): secret_table = gr.Dataframe(value=tab_secret("EN"), label="Secret Management", wrap=True) lang.change(lambda l: tab_secret(l), lang, secret_table) # ---- 8. Q&A ---- with gr.Tab("Q&A"): qa_table = gr.Dataframe(value=tab_qa("EN"), label="Q&A", wrap=True) lang.change(lambda l: tab_qa(l), lang, qa_table) # ---- 9. Statistics ---- with gr.Tab("Statistics"): chart_type = gr.Plot(value=stats_type_chart("EN"), label="By Type") chart_ci = gr.Plot(value=stats_ci_chart("EN"), label="By CI Platform") chart_maturity = gr.Plot(value=stats_maturity_chart("EN"), label="By Maturity Level") lang.change(lambda l: stats_type_chart(l), lang, chart_type) lang.change(lambda l: stats_ci_chart(l), lang, chart_ci) lang.change(lambda l: stats_maturity_chart(l), lang, chart_maturity) gr.HTML(FOOTER_HTML) return demo if __name__ == "__main__": demo = build_app() demo.launch()