AYI-NEDJIMI commited on
Commit
fb81c82
·
verified ·
1 Parent(s): b44aa43

Initial DevSecOps Pipeline Explorer Space

Browse files
Files changed (3) hide show
  1. README.md +17 -4
  2. app.py +299 -0
  3. requirements.txt +4 -0
README.md CHANGED
@@ -1,12 +1,25 @@
1
  ---
2
- title: Devsecops Pipeline Explorer
3
- emoji: 🦀
4
  colorFrom: blue
5
  colorTo: green
6
  sdk: gradio
7
- sdk_version: 6.5.1
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: DevSecOps Pipeline Explorer
3
+ emoji: 🔒
4
  colorFrom: blue
5
  colorTo: green
6
  sdk: gradio
7
+ sdk_version: "5.50.0"
8
  app_file: app.py
9
  pinned: false
10
  ---
11
 
12
+ # DevSecOps Pipeline Explorer
13
+
14
+ Explore 130 DevSecOps entries covering practices, SAST/DAST/SCA tools, pipeline templates, container security, secret management, and Q&A.
15
+
16
+ **Features:**
17
+ - Bilingual support (FR / EN)
18
+ - 9 tabs: Practices, SAST, DAST, SCA, Pipelines, Container Security, Secret Management, Q&A, Statistics
19
+ - Interactive filters for maturity level and CI platform
20
+ - Plotly charts for statistics
21
+ - YAML code blocks for pipeline templates
22
+
23
+ **Datasets:**
24
+ - [AYI-NEDJIMI/devsecops-pipeline-fr](https://huggingface.co/datasets/AYI-NEDJIMI/devsecops-pipeline-fr)
25
+ - [AYI-NEDJIMI/devsecops-pipeline-en](https://huggingface.co/datasets/AYI-NEDJIMI/devsecops-pipeline-en)
app.py ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import plotly.express as px
4
+ import plotly.graph_objects as go
5
+ from datasets import load_dataset
6
+
7
+ # ---------------------------------------------------------------------------
8
+ # Data Loading
9
+ # ---------------------------------------------------------------------------
10
+
11
+ DATASETS = {
12
+ "FR": "AYI-NEDJIMI/devsecops-pipeline-fr",
13
+ "EN": "AYI-NEDJIMI/devsecops-pipeline-en",
14
+ }
15
+
16
+ _cache = {}
17
+
18
+
19
+ def _load(lang: str) -> pd.DataFrame:
20
+ if lang not in _cache:
21
+ try:
22
+ ds = load_dataset(DATASETS[lang], split="train")
23
+ _cache[lang] = ds.to_pandas()
24
+ except Exception as e:
25
+ print(f"Error loading {lang} dataset: {e}")
26
+ _cache[lang] = pd.DataFrame()
27
+ return _cache[lang]
28
+
29
+
30
+ def _safe(df: pd.DataFrame, col: str):
31
+ """Return column values if it exists, else empty Series."""
32
+ if col in df.columns:
33
+ return df[col].fillna("")
34
+ return pd.Series([""] * len(df), index=df.index)
35
+
36
+
37
+ def _filter_type(lang, t):
38
+ df = _load(lang)
39
+ if "type" in df.columns:
40
+ return df[df["type"] == t].reset_index(drop=True)
41
+ return df
42
+
43
+
44
+ # ---------------------------------------------------------------------------
45
+ # Helper – build a display DataFrame with only the requested columns
46
+ # ---------------------------------------------------------------------------
47
+
48
+ def _display(df, cols):
49
+ present = [c for c in cols if c in df.columns]
50
+ out = df[present].copy() if present else pd.DataFrame()
51
+ return out
52
+
53
+
54
+ # ---------------------------------------------------------------------------
55
+ # Tab builders
56
+ # ---------------------------------------------------------------------------
57
+
58
+ def tab_practices(lang, maturity_filter):
59
+ df = _filter_type(lang, "practice")
60
+ if maturity_filter and maturity_filter != "All":
61
+ df = df[_safe(df, "maturity_level") == maturity_filter]
62
+ cols = ["practice_name", "description", "benefits", "tools", "metrics"]
63
+ return _display(df, cols)
64
+
65
+
66
+ def tab_sast(lang):
67
+ df = _filter_type(lang, "sast_tool")
68
+ cols = ["name", "vendor", "supported_languages", "ci_integration", "strengths", "weaknesses"]
69
+ return _display(df, cols)
70
+
71
+
72
+ def tab_dast(lang):
73
+ df = _filter_type(lang, "dast_tool")
74
+ cols = ["name", "vendor", "scan_types", "api_support", "strengths", "weaknesses"]
75
+ return _display(df, cols)
76
+
77
+
78
+ def tab_sca(lang):
79
+ df = _filter_type(lang, "sca_tool")
80
+ cols = ["name", "vendor", "package_managers_supported", "vulnerability_db", "strengths", "weaknesses"]
81
+ return _display(df, cols)
82
+
83
+
84
+ def tab_pipeline(lang, ci_filter):
85
+ df = _filter_type(lang, "pipeline_template")
86
+ if ci_filter and ci_filter != "All":
87
+ df = df[_safe(df, "ci_platform") == ci_filter]
88
+ cols = ["pipeline_name", "stages", "yaml_example", "security_gates"]
89
+ return _display(df, cols)
90
+
91
+
92
+ def tab_container(lang):
93
+ df = _filter_type(lang, "container_security")
94
+ cols = ["category", "name", "implementation", "best_practices", "common_misconfigurations"]
95
+ return _display(df, cols)
96
+
97
+
98
+ def tab_secret(lang):
99
+ df = _filter_type(lang, "secret_management")
100
+ cols = ["name", "vendor", "features", "integration", "strengths", "weaknesses"]
101
+ return _display(df, cols)
102
+
103
+
104
+ def tab_qa(lang):
105
+ df = _filter_type(lang, "qa")
106
+ cols = ["question", "answer", "difficulty"]
107
+ return _display(df, cols)
108
+
109
+
110
+ # ---------------------------------------------------------------------------
111
+ # Pipeline detail – show yaml_example in code block
112
+ # ---------------------------------------------------------------------------
113
+
114
+ def pipeline_detail(lang, ci_filter):
115
+ df = _filter_type(lang, "pipeline_template")
116
+ if ci_filter and ci_filter != "All":
117
+ df = df[_safe(df, "ci_platform") == ci_filter]
118
+ parts = []
119
+ for _, row in df.iterrows():
120
+ name = row.get("pipeline_name", "N/A")
121
+ stages = row.get("stages", "N/A")
122
+ gates = row.get("security_gates", "N/A")
123
+ yaml_ex = row.get("yaml_example", "")
124
+ parts.append(f"### {name}\n\n**Stages:** {stages}\n\n**Security Gates:** {gates}\n\n```yaml\n{yaml_ex}\n```\n\n---\n")
125
+ return "\n".join(parts) if parts else "No pipeline templates found."
126
+
127
+
128
+ # ---------------------------------------------------------------------------
129
+ # Statistics charts
130
+ # ---------------------------------------------------------------------------
131
+
132
+ def stats_type_chart(lang):
133
+ df = _load(lang)
134
+ if "type" not in df.columns:
135
+ return go.Figure()
136
+ counts = df["type"].value_counts().reset_index()
137
+ counts.columns = ["type", "count"]
138
+ fig = px.bar(counts, x="type", y="count", title="Entries by Type",
139
+ color="type", text_auto=True)
140
+ fig.update_layout(showlegend=False)
141
+ return fig
142
+
143
+
144
+ def stats_ci_chart(lang):
145
+ df = _load(lang)
146
+ if "ci_platform" not in df.columns:
147
+ return go.Figure()
148
+ sub = df[df["ci_platform"].notna() & (df["ci_platform"] != "")]
149
+ if sub.empty:
150
+ return go.Figure()
151
+ counts = sub["ci_platform"].value_counts().reset_index()
152
+ counts.columns = ["ci_platform", "count"]
153
+ fig = px.pie(counts, names="ci_platform", values="count", title="Pipeline Templates by CI Platform")
154
+ return fig
155
+
156
+
157
+ def stats_maturity_chart(lang):
158
+ df = _load(lang)
159
+ if "maturity_level" not in df.columns:
160
+ return go.Figure()
161
+ sub = df[df["maturity_level"].notna() & (df["maturity_level"] != "")]
162
+ if sub.empty:
163
+ return go.Figure()
164
+ counts = sub["maturity_level"].value_counts().reset_index()
165
+ counts.columns = ["maturity_level", "count"]
166
+ fig = px.bar(counts, x="maturity_level", y="count", title="Practices by Maturity Level",
167
+ color="maturity_level", text_auto=True)
168
+ fig.update_layout(showlegend=False)
169
+ return fig
170
+
171
+
172
+ # ---------------------------------------------------------------------------
173
+ # Dynamic dropdown choices
174
+ # ---------------------------------------------------------------------------
175
+
176
+ def _maturity_choices(lang):
177
+ df = _filter_type(lang, "practice")
178
+ if "maturity_level" in df.columns:
179
+ vals = sorted(df["maturity_level"].dropna().unique().tolist())
180
+ else:
181
+ vals = []
182
+ return ["All"] + vals
183
+
184
+
185
+ def _ci_choices(lang):
186
+ df = _filter_type(lang, "pipeline_template")
187
+ if "ci_platform" in df.columns:
188
+ vals = sorted(df["ci_platform"].dropna().unique().tolist())
189
+ else:
190
+ vals = []
191
+ return ["All"] + vals
192
+
193
+
194
+ # ---------------------------------------------------------------------------
195
+ # Footer HTML
196
+ # ---------------------------------------------------------------------------
197
+
198
+ FOOTER_HTML = """
199
+ <div style="text-align:center; padding:20px; margin-top:30px; border-top:1px solid #444; font-size:0.9em; color:#aaa;">
200
+ <p><strong>DevSecOps Pipeline Explorer</strong> — Built by AYI-NEDJIMI Consultants</p>
201
+ <p>
202
+ <a href="https://ayinedjimi-consultants.fr" target="_blank" style="margin:0 8px;">🌐 ayinedjimi-consultants.fr</a> |
203
+ <a href="https://www.linkedin.com/company/ayi-nedjimi" target="_blank" style="margin:0 8px;">LinkedIn</a> |
204
+ <a href="https://github.com/AYI-NEDJIMI" target="_blank" style="margin:0 8px;">GitHub</a> |
205
+ <a href="https://x.com/AYI_NEDJIMI" target="_blank" style="margin:0 8px;">X / Twitter</a>
206
+ </p>
207
+ </div>
208
+ """
209
+
210
+ # ---------------------------------------------------------------------------
211
+ # Gradio App
212
+ # ---------------------------------------------------------------------------
213
+
214
+ def build_app():
215
+ with gr.Blocks(
216
+ title="DevSecOps Pipeline Explorer",
217
+ theme=gr.themes.Soft(),
218
+ ) as demo:
219
+ gr.Markdown("# DevSecOps Pipeline Explorer\nExplore 130 DevSecOps practices, tools, pipeline templates, and more.")
220
+
221
+ lang = gr.Radio(["EN", "FR"], value="EN", label="Language / Langue", interactive=True)
222
+
223
+ with gr.Tabs():
224
+ # ---- 1. Practices ----
225
+ with gr.Tab("DevSecOps Practices"):
226
+ maturity_dd = gr.Dropdown(choices=_maturity_choices("EN"), value="All", label="Filter by Maturity Level")
227
+ practices_table = gr.Dataframe(value=tab_practices("EN", "All"), label="Practices", wrap=True)
228
+ btn_practices = gr.Button("Refresh")
229
+
230
+ def _refresh_practices(l, m):
231
+ return tab_practices(l, m)
232
+
233
+ btn_practices.click(_refresh_practices, [lang, maturity_dd], practices_table)
234
+ lang.change(lambda l: gr.update(choices=_maturity_choices(l), value="All"), lang, maturity_dd)
235
+ lang.change(lambda l: tab_practices(l, "All"), lang, practices_table)
236
+ maturity_dd.change(_refresh_practices, [lang, maturity_dd], practices_table)
237
+
238
+ # ---- 2. SAST ----
239
+ with gr.Tab("SAST Tools"):
240
+ sast_table = gr.Dataframe(value=tab_sast("EN"), label="SAST Tools", wrap=True)
241
+ lang.change(lambda l: tab_sast(l), lang, sast_table)
242
+
243
+ # ---- 3. DAST ----
244
+ with gr.Tab("DAST Tools"):
245
+ dast_table = gr.Dataframe(value=tab_dast("EN"), label="DAST Tools", wrap=True)
246
+ lang.change(lambda l: tab_dast(l), lang, dast_table)
247
+
248
+ # ---- 4. SCA ----
249
+ with gr.Tab("SCA Tools"):
250
+ sca_table = gr.Dataframe(value=tab_sca("EN"), label="SCA Tools", wrap=True)
251
+ lang.change(lambda l: tab_sca(l), lang, sca_table)
252
+
253
+ # ---- 5. Pipeline Templates ----
254
+ with gr.Tab("Pipeline Templates"):
255
+ ci_dd = gr.Dropdown(choices=_ci_choices("EN"), value="All", label="Filter by CI Platform")
256
+ pipeline_table = gr.Dataframe(value=tab_pipeline("EN", "All"), label="Pipeline Templates", wrap=True)
257
+ pipeline_md = gr.Markdown(value=pipeline_detail("EN", "All"), label="YAML Details")
258
+ btn_pipeline = gr.Button("Refresh")
259
+
260
+ def _refresh_pipeline(l, c):
261
+ return tab_pipeline(l, c), pipeline_detail(l, c)
262
+
263
+ btn_pipeline.click(_refresh_pipeline, [lang, ci_dd], [pipeline_table, pipeline_md])
264
+ lang.change(lambda l: gr.update(choices=_ci_choices(l), value="All"), lang, ci_dd)
265
+ lang.change(lambda l: (tab_pipeline(l, "All"), pipeline_detail(l, "All")), lang, [pipeline_table, pipeline_md])
266
+ ci_dd.change(lambda l, c: (tab_pipeline(l, c), pipeline_detail(l, c)), [lang, ci_dd], [pipeline_table, pipeline_md])
267
+
268
+ # ---- 6. Container Security ----
269
+ with gr.Tab("Container Security"):
270
+ container_table = gr.Dataframe(value=tab_container("EN"), label="Container Security", wrap=True)
271
+ lang.change(lambda l: tab_container(l), lang, container_table)
272
+
273
+ # ---- 7. Secret Management ----
274
+ with gr.Tab("Secret Management"):
275
+ secret_table = gr.Dataframe(value=tab_secret("EN"), label="Secret Management", wrap=True)
276
+ lang.change(lambda l: tab_secret(l), lang, secret_table)
277
+
278
+ # ---- 8. Q&A ----
279
+ with gr.Tab("Q&A"):
280
+ qa_table = gr.Dataframe(value=tab_qa("EN"), label="Q&A", wrap=True)
281
+ lang.change(lambda l: tab_qa(l), lang, qa_table)
282
+
283
+ # ---- 9. Statistics ----
284
+ with gr.Tab("Statistics"):
285
+ chart_type = gr.Plot(value=stats_type_chart("EN"), label="By Type")
286
+ chart_ci = gr.Plot(value=stats_ci_chart("EN"), label="By CI Platform")
287
+ chart_maturity = gr.Plot(value=stats_maturity_chart("EN"), label="By Maturity Level")
288
+ lang.change(lambda l: stats_type_chart(l), lang, chart_type)
289
+ lang.change(lambda l: stats_ci_chart(l), lang, chart_ci)
290
+ lang.change(lambda l: stats_maturity_chart(l), lang, chart_maturity)
291
+
292
+ gr.HTML(FOOTER_HTML)
293
+
294
+ return demo
295
+
296
+
297
+ if __name__ == "__main__":
298
+ demo = build_app()
299
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio==5.50.0
2
+ plotly
3
+ pandas
4
+ datasets