devsecops-radar 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. devsecops_radar-0.1.0/LICENSE +21 -0
  2. devsecops_radar-0.1.0/PKG-INFO +191 -0
  3. devsecops_radar-0.1.0/README.md +169 -0
  4. devsecops_radar-0.1.0/devsecops_radar/__init__.py +0 -0
  5. devsecops_radar-0.1.0/devsecops_radar/cli/__init__.py +0 -0
  6. devsecops_radar-0.1.0/devsecops_radar/cli/scanner.py +72 -0
  7. devsecops_radar-0.1.0/devsecops_radar/core/__init__.py +0 -0
  8. devsecops_radar-0.1.0/devsecops_radar/core/analyzer.py +45 -0
  9. devsecops_radar-0.1.0/devsecops_radar/core/database.py +54 -0
  10. devsecops_radar-0.1.0/devsecops_radar/core/parser.py +44 -0
  11. devsecops_radar-0.1.0/devsecops_radar/scanners/base.py +11 -0
  12. devsecops_radar-0.1.0/devsecops_radar/scanners/poutine.py +33 -0
  13. devsecops_radar-0.1.0/devsecops_radar/scanners/semgrep.py +33 -0
  14. devsecops_radar-0.1.0/devsecops_radar/scanners/trivy.py +37 -0
  15. devsecops_radar-0.1.0/devsecops_radar/scanners/zizmor.py +33 -0
  16. devsecops_radar-0.1.0/devsecops_radar/web/__init__.py +0 -0
  17. devsecops_radar-0.1.0/devsecops_radar/web/app.py +33 -0
  18. devsecops_radar-0.1.0/devsecops_radar.egg-info/PKG-INFO +191 -0
  19. devsecops_radar-0.1.0/devsecops_radar.egg-info/SOURCES.txt +25 -0
  20. devsecops_radar-0.1.0/devsecops_radar.egg-info/dependency_links.txt +1 -0
  21. devsecops_radar-0.1.0/devsecops_radar.egg-info/entry_points.txt +3 -0
  22. devsecops_radar-0.1.0/devsecops_radar.egg-info/requires.txt +4 -0
  23. devsecops_radar-0.1.0/devsecops_radar.egg-info/top_level.txt +1 -0
  24. devsecops_radar-0.1.0/pyproject.toml +38 -0
  25. devsecops_radar-0.1.0/setup.cfg +4 -0
  26. devsecops_radar-0.1.0/tests/test_cli.py +0 -0
  27. devsecops_radar-0.1.0/tests/test_scanners.py +70 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mdm
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,191 @@
1
+ Metadata-Version: 2.4
2
+ Name: devsecops-radar
3
+ Version: 0.1.0
4
+ Summary: Unified CI/CD Security Dashboard — Pipeline Sentinel
5
+ Author-email: Mehrdoost <mehrdoost@users.noreply.github.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Mehrdoost/devsecops-radar
8
+ Project-URL: Source, https://github.com/Mehrdoost/devsecops-radar
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Topic :: Security
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: flask>=3.0
18
+ Requires-Dist: semgrep>=1.0
19
+ Requires-Dist: pyyaml>=6.0
20
+ Requires-Dist: requests>=2.31
21
+ Dynamic: license-file
22
+
23
+ # 🛡️ Pipeline Sentinel
24
+
25
+ **Unified CI/CD Security Observability — AI‑Enhanced & Offline‑Ready**
26
+
27
+ Aggregate findings from **Trivy, Semgrep, Poutine, Zizmor** and more into a single, beautiful dashboard. Correlate risks with an **LLM‑powered analysis engine**, track security trends over time, and enforce guardrails – all in one CLI + web UI.
28
+
29
+ [![GitHub stars](https://img.shields.io/github/stars/Mehrdoost/devsecops-radar?style=social)](https://github.com/Mehrdoost/devsecops-radar/stargazers)
30
+ [![License](https://img.shields.io/github/license/Mehrdoost/devsecops-radar)](LICENSE)
31
+ [![Docker Pulls](https://img.shields.io/docker/pulls/Mehrdoost/devsecops-radar)](https://hub.docker.com/r/Mehrdoost/devsecops-radar)
32
+ [![PyPI version](https://img.shields.io/pypi/v/devsecops-radar.svg)](https://pypi.org/project/devsecops-radar/)
33
+ [![GitHub release](https://img.shields.io/github/v/release/Mehrdoost/devsecops-radar?include_prereleases)](https://github.com/Mehrdoost/devsecops-radar/releases)
34
+ [![CI](https://github.com/Mehrdoost/devsecops-radar/actions/workflows/test-action.yml/badge.svg)](https://github.com/Mehrdoost/devsecops-radar/actions/workflows/test-action.yml)
35
+
36
+ ---
37
+
38
+ ## 🚀 Quick Start
39
+
40
+ ```bash
41
+ # Install from PyPI
42
+ pip install devsecops-radar
43
+
44
+ # Or install directly from GitHub
45
+ pip install git+[https://github.com/Mehrdoost/devsecops-radar.git](https://github.com/Mehrdoost/devsecops-radar.git)
46
+
47
+ # Run the web dashboard
48
+ devsecops-radar-web
49
+ ```
50
+
51
+ 🐳 **Docker:** `docker pull ghcr.io/mehrdoost/devsecops-radar:latest` *(see instructions below)*
52
+
53
+ ---
54
+
55
+ ## ✨ Key Features
56
+
57
+ | Capability | Description |
58
+ | :--- | :--- |
59
+ | 🔌 **Multi‑Scanner Integration** | Natively parses Trivy, Semgrep, Poutine, Zizmor. More via pluggable architecture. |
60
+ | 🧠 **LLM‑Powered Analysis** | Optional AI correlation, false‑positive reduction, attack‑path identification (Ollama‑backed, offline capable). |
61
+ | 📈 **Scan History & Trends** | SQLite‑powered historical storage. Visual trend chart shows risk evolution over time. |
62
+ | 🤖 **GitHub Action** | One‑step integration into your CI/CD. Summarises findings and optionally comments on PRs. |
63
+ | 🎨 **Beautiful Dark Dashboard** | Severity doughnut, trend line chart, search & filters – works fully offline (all assets bundled). |
64
+ | 🐳 **Docker Native** | Official image on GitHub Container Registry. Just one `docker run` away. |
65
+
66
+ ---
67
+
68
+ ## 🔧 Supported Scanners
69
+
70
+ | Scanner | What it scans | Status |
71
+ | :--- | :--- | :--- |
72
+ | **Trivy** | Container images & dependencies | ✅ |
73
+ | **Semgrep** | SAST (Static Code Analysis) | ✅ |
74
+ | **Poutine** | GitLab CI/CD configuration security | ✅ |
75
+ | **Zizmor** | GitHub Actions workflow security | ✅ |
76
+ | **Snyk, ZAP, Dependency-Track** | Roadmap | 🔲 |
77
+
78
+ *Adding a new scanner is easy – extend `BaseScanner` and plug it in.*
79
+
80
+ ---
81
+
82
+
83
+ ## 📸 Dashboard Preview
84
+
85
+ ![DevSecOps Radar Dashboard](docs/Demo.gif)
86
+
87
+ ---
88
+
89
+ ## 🤖 GitHub Action
90
+
91
+ Add security analysis to your workflow with a single step:
92
+
93
+ ```yaml
94
+ - name: Pipeline Sentinel
95
+ uses: Mehrdoost/devsecops-radar/action@main
96
+ with:
97
+ trivy_report: trivy-results.json
98
+ semgrep_report: semgrep-results.json
99
+ poutine_report: poutine-results.json
100
+ zizmor_report: zizmor-results.json
101
+ ```
102
+
103
+ The action merges findings, creates a job summary, and outputs CRITICAL/HIGH counts.
104
+
105
+ ---
106
+
107
+ ## 📊 Scan History & Trends
108
+
109
+ Every run automatically stores findings in a local `scan_history.db`.
110
+ The dashboard renders a **Trend Over Time** chart so teams can monitor whether security posture is improving.
111
+
112
+ ```bash
113
+ # Multiple scans build history
114
+ devsecops-radar --trivy sample_trivy.json --semgrep sample_semgrep.json
115
+ devsecops-radar --trivy sample_trivy.json --semgrep sample_semgrep.json --poutine sample_poutine.json
116
+
117
+ # Now view the trend in the dashboard
118
+ devsecops-radar-web
119
+ ```
120
+
121
+ ---
122
+
123
+ ## 🧠 AI‑Powered Analysis (Optional)
124
+
125
+ Enable LLM analysis with `--analyze` (requires Ollama running locally):
126
+
127
+ ```bash
128
+ ollama pull llama3.2:latest # one-time setup
129
+ devsecops-radar --trivy sample_trivy.json --semgrep sample_semgrep.json --zizmor sample_zizmor.json --analyze
130
+ ```
131
+
132
+ *Generates `findings_ai_summary.md` with executive summary, attack paths, and remediation tips.*
133
+
134
+ ---
135
+
136
+ ## 🛠️ Usage
137
+
138
+ ### From Source (Python)
139
+ ```bash
140
+ pip install -e .
141
+ devsecops-radar --trivy trivy.json --semgrep semgrep.json
142
+ devsecops-radar-web
143
+ ```
144
+
145
+ ### Docker
146
+ ```bash
147
+ docker pull ghcr.io/mehrdoost/devsecops-radar:latest
148
+ docker run -p 8080:8080 -v $(pwd)/findings.json:/data/findings.json ghcr.io/mehrdoost/devsecops-radar:latest
149
+ ```
150
+
151
+ ### Using Sample Data
152
+ ```bash
153
+ devsecops-radar --trivy sample_trivy.json --semgrep sample_semgrep.json --poutine sample_poutine.json --zizmor sample_zizmor.json
154
+ ```
155
+
156
+ ---
157
+
158
+ ## 🗺️ Roadmap
159
+
160
+ - [x] Multi‑scanner engine (Trivy, Semgrep, Poutine, Zizmor)
161
+ - [x] AI correlation & analysis
162
+ - [x] Scan history & trend visualisation
163
+ - [x] GitHub Action (composite)
164
+ - [x] Docker image (GitHub Container Registry)
165
+ - [ ] Security guardrail policies (`policy.yml`)
166
+ - [ ] AI remediation advisor (detailed fix guidance)
167
+ - [ ] Findings diff/compare between branches
168
+ - [ ] Jira / Slack integration
169
+
170
+ ---
171
+
172
+ ## 🤝 Contributing
173
+
174
+ Pull requests and issues are warmly welcome!
175
+ If you’d like to integrate a new scanner, open an issue with a sample of its JSON output.
176
+
177
+ ---
178
+
179
+ ## 👨‍💻 Author
180
+
181
+ **Mehrdoost**
182
+
183
+ [![GitHub](https://img.shields.io/badge/GitHub-Mehrdoost-181717?logo=github)](https://github.com/Mehrdoost)
184
+
185
+ ---
186
+
187
+ ## 📜 License
188
+
189
+ MIT – see [LICENSE](LICENSE) file.
190
+
191
+ ⭐ **If this project helps your team ship more secure software, please drop a star!**
@@ -0,0 +1,169 @@
1
+ # 🛡️ Pipeline Sentinel
2
+
3
+ **Unified CI/CD Security Observability — AI‑Enhanced & Offline‑Ready**
4
+
5
+ Aggregate findings from **Trivy, Semgrep, Poutine, Zizmor** and more into a single, beautiful dashboard. Correlate risks with an **LLM‑powered analysis engine**, track security trends over time, and enforce guardrails – all in one CLI + web UI.
6
+
7
+ [![GitHub stars](https://img.shields.io/github/stars/Mehrdoost/devsecops-radar?style=social)](https://github.com/Mehrdoost/devsecops-radar/stargazers)
8
+ [![License](https://img.shields.io/github/license/Mehrdoost/devsecops-radar)](LICENSE)
9
+ [![Docker Pulls](https://img.shields.io/docker/pulls/Mehrdoost/devsecops-radar)](https://hub.docker.com/r/Mehrdoost/devsecops-radar)
10
+ [![PyPI version](https://img.shields.io/pypi/v/devsecops-radar.svg)](https://pypi.org/project/devsecops-radar/)
11
+ [![GitHub release](https://img.shields.io/github/v/release/Mehrdoost/devsecops-radar?include_prereleases)](https://github.com/Mehrdoost/devsecops-radar/releases)
12
+ [![CI](https://github.com/Mehrdoost/devsecops-radar/actions/workflows/test-action.yml/badge.svg)](https://github.com/Mehrdoost/devsecops-radar/actions/workflows/test-action.yml)
13
+
14
+ ---
15
+
16
+ ## 🚀 Quick Start
17
+
18
+ ```bash
19
+ # Install from PyPI
20
+ pip install devsecops-radar
21
+
22
+ # Or install directly from GitHub
23
+ pip install git+[https://github.com/Mehrdoost/devsecops-radar.git](https://github.com/Mehrdoost/devsecops-radar.git)
24
+
25
+ # Run the web dashboard
26
+ devsecops-radar-web
27
+ ```
28
+
29
+ 🐳 **Docker:** `docker pull ghcr.io/mehrdoost/devsecops-radar:latest` *(see instructions below)*
30
+
31
+ ---
32
+
33
+ ## ✨ Key Features
34
+
35
+ | Capability | Description |
36
+ | :--- | :--- |
37
+ | 🔌 **Multi‑Scanner Integration** | Natively parses Trivy, Semgrep, Poutine, Zizmor. More via pluggable architecture. |
38
+ | 🧠 **LLM‑Powered Analysis** | Optional AI correlation, false‑positive reduction, attack‑path identification (Ollama‑backed, offline capable). |
39
+ | 📈 **Scan History & Trends** | SQLite‑powered historical storage. Visual trend chart shows risk evolution over time. |
40
+ | 🤖 **GitHub Action** | One‑step integration into your CI/CD. Summarises findings and optionally comments on PRs. |
41
+ | 🎨 **Beautiful Dark Dashboard** | Severity doughnut, trend line chart, search & filters – works fully offline (all assets bundled). |
42
+ | 🐳 **Docker Native** | Official image on GitHub Container Registry. Just one `docker run` away. |
43
+
44
+ ---
45
+
46
+ ## 🔧 Supported Scanners
47
+
48
+ | Scanner | What it scans | Status |
49
+ | :--- | :--- | :--- |
50
+ | **Trivy** | Container images & dependencies | ✅ |
51
+ | **Semgrep** | SAST (Static Code Analysis) | ✅ |
52
+ | **Poutine** | GitLab CI/CD configuration security | ✅ |
53
+ | **Zizmor** | GitHub Actions workflow security | ✅ |
54
+ | **Snyk, ZAP, Dependency-Track** | Roadmap | 🔲 |
55
+
56
+ *Adding a new scanner is easy – extend `BaseScanner` and plug it in.*
57
+
58
+ ---
59
+
60
+
61
+ ## 📸 Dashboard Preview
62
+
63
+ ![DevSecOps Radar Dashboard](docs/Demo.gif)
64
+
65
+ ---
66
+
67
+ ## 🤖 GitHub Action
68
+
69
+ Add security analysis to your workflow with a single step:
70
+
71
+ ```yaml
72
+ - name: Pipeline Sentinel
73
+ uses: Mehrdoost/devsecops-radar/action@main
74
+ with:
75
+ trivy_report: trivy-results.json
76
+ semgrep_report: semgrep-results.json
77
+ poutine_report: poutine-results.json
78
+ zizmor_report: zizmor-results.json
79
+ ```
80
+
81
+ The action merges findings, creates a job summary, and outputs CRITICAL/HIGH counts.
82
+
83
+ ---
84
+
85
+ ## 📊 Scan History & Trends
86
+
87
+ Every run automatically stores findings in a local `scan_history.db`.
88
+ The dashboard renders a **Trend Over Time** chart so teams can monitor whether security posture is improving.
89
+
90
+ ```bash
91
+ # Multiple scans build history
92
+ devsecops-radar --trivy sample_trivy.json --semgrep sample_semgrep.json
93
+ devsecops-radar --trivy sample_trivy.json --semgrep sample_semgrep.json --poutine sample_poutine.json
94
+
95
+ # Now view the trend in the dashboard
96
+ devsecops-radar-web
97
+ ```
98
+
99
+ ---
100
+
101
+ ## 🧠 AI‑Powered Analysis (Optional)
102
+
103
+ Enable LLM analysis with `--analyze` (requires Ollama running locally):
104
+
105
+ ```bash
106
+ ollama pull llama3.2:latest # one-time setup
107
+ devsecops-radar --trivy sample_trivy.json --semgrep sample_semgrep.json --zizmor sample_zizmor.json --analyze
108
+ ```
109
+
110
+ *Generates `findings_ai_summary.md` with executive summary, attack paths, and remediation tips.*
111
+
112
+ ---
113
+
114
+ ## 🛠️ Usage
115
+
116
+ ### From Source (Python)
117
+ ```bash
118
+ pip install -e .
119
+ devsecops-radar --trivy trivy.json --semgrep semgrep.json
120
+ devsecops-radar-web
121
+ ```
122
+
123
+ ### Docker
124
+ ```bash
125
+ docker pull ghcr.io/mehrdoost/devsecops-radar:latest
126
+ docker run -p 8080:8080 -v $(pwd)/findings.json:/data/findings.json ghcr.io/mehrdoost/devsecops-radar:latest
127
+ ```
128
+
129
+ ### Using Sample Data
130
+ ```bash
131
+ devsecops-radar --trivy sample_trivy.json --semgrep sample_semgrep.json --poutine sample_poutine.json --zizmor sample_zizmor.json
132
+ ```
133
+
134
+ ---
135
+
136
+ ## 🗺️ Roadmap
137
+
138
+ - [x] Multi‑scanner engine (Trivy, Semgrep, Poutine, Zizmor)
139
+ - [x] AI correlation & analysis
140
+ - [x] Scan history & trend visualisation
141
+ - [x] GitHub Action (composite)
142
+ - [x] Docker image (GitHub Container Registry)
143
+ - [ ] Security guardrail policies (`policy.yml`)
144
+ - [ ] AI remediation advisor (detailed fix guidance)
145
+ - [ ] Findings diff/compare between branches
146
+ - [ ] Jira / Slack integration
147
+
148
+ ---
149
+
150
+ ## 🤝 Contributing
151
+
152
+ Pull requests and issues are warmly welcome!
153
+ If you’d like to integrate a new scanner, open an issue with a sample of its JSON output.
154
+
155
+ ---
156
+
157
+ ## 👨‍💻 Author
158
+
159
+ **Mehrdoost**
160
+
161
+ [![GitHub](https://img.shields.io/badge/GitHub-Mehrdoost-181717?logo=github)](https://github.com/Mehrdoost)
162
+
163
+ ---
164
+
165
+ ## 📜 License
166
+
167
+ MIT – see [LICENSE](LICENSE) file.
168
+
169
+ ⭐ **If this project helps your team ship more secure software, please drop a star!**
File without changes
File without changes
@@ -0,0 +1,72 @@
1
+ import argparse
2
+ import json
3
+ import os
4
+ from devsecops_radar.scanners.trivy import TrivyScanner
5
+ from devsecops_radar.scanners.semgrep import SemgrepScanner
6
+ from devsecops_radar.scanners.poutine import PoutineScanner
7
+ from devsecops_radar.scanners.zizmor import ZizmorScanner
8
+ from devsecops_radar.core.analyzer import OllamaAnalyzer
9
+ from devsecops_radar.core.database import save_scan
10
+
11
+ def main():
12
+ parser = argparse.ArgumentParser(description='DevSecOps Radar - Collect and view security findings.')
13
+ parser.add_argument('--trivy', type=str, help='Path to Trivy JSON output file or image name to scan directly (e.g., nginx:latest)')
14
+ parser.add_argument('--semgrep', type=str, help='Path to Semgrep JSON output file or target directory to scan directly')
15
+ parser.add_argument('--poutine', type=str, help='Path to Poutine JSON output file or repository path to scan directly')
16
+ parser.add_argument('--zizmor', type=str, help='Path to Zizmor JSON output file or repository path to scan directly')
17
+ parser.add_argument('--output', type=str, default='findings.json', help='Output file for merged findings')
18
+ parser.add_argument('--analyze', action='store_true', help='Enable LLM analysis (requires Ollama running locally)')
19
+ args = parser.parse_args()
20
+
21
+ all_findings = []
22
+
23
+ if args.trivy:
24
+ trivy = TrivyScanner()
25
+ if os.path.isfile(args.trivy):
26
+ all_findings.extend(trivy.parse(args.trivy))
27
+ else:
28
+ print(f"Scanning image: {args.trivy}")
29
+ all_findings.extend(trivy.run(args.trivy))
30
+
31
+ if args.semgrep:
32
+ semgrep = SemgrepScanner()
33
+ if os.path.isfile(args.semgrep):
34
+ all_findings.extend(semgrep.parse(args.semgrep))
35
+ else:
36
+ print(f"Scanning directory: {args.semgrep}")
37
+ all_findings.extend(semgrep.run(args.semgrep))
38
+
39
+ if args.poutine:
40
+ poutine = PoutineScanner()
41
+ if os.path.isfile(args.poutine):
42
+ all_findings.extend(poutine.parse(args.poutine))
43
+ else:
44
+ print(f"Scanning repository: {args.poutine}")
45
+ all_findings.extend(poutine.run(args.poutine))
46
+
47
+ if args.zizmor:
48
+ zizmor = ZizmorScanner()
49
+ if os.path.isfile(args.zizmor):
50
+ all_findings.extend(zizmor.parse(args.zizmor))
51
+ else:
52
+ print(f"Scanning repository: {args.zizmor}")
53
+ all_findings.extend(zizmor.run(args.zizmor))
54
+
55
+ with open(args.output, 'w') as f:
56
+ json.dump(all_findings, f, indent=2)
57
+ print(f"Merged {len(all_findings)} findings into {args.output}")
58
+
59
+ # Save to scan history database
60
+ save_scan(all_findings)
61
+
62
+ if args.analyze:
63
+ print("Running AI analysis...")
64
+ analyzer = OllamaAnalyzer()
65
+ summary = analyzer.analyze(all_findings)
66
+ summary_file = args.output.replace('.json', '_ai_summary.md')
67
+ with open(summary_file, 'w', encoding='utf-8') as s:
68
+ s.write(f"# 🛡️ Pipeline Sentinel AI Analysis\n\n{summary}")
69
+ print(f"AI summary saved to {summary_file}")
70
+
71
+ if __name__ == '__main__':
72
+ main()
File without changes
@@ -0,0 +1,45 @@
1
+ import json
2
+ import os
3
+ import requests
4
+ from typing import List, Dict, Any, Optional
5
+
6
+ class BaseAnalyzer:
7
+ def analyze(self, findings: List[Dict[str, Any]]) -> str:
8
+ raise NotImplementedError
9
+
10
+ class OllamaAnalyzer(BaseAnalyzer):
11
+ def __init__(self, model: str = "llama3.2:latest", endpoint: str = "http://localhost:11434/api/generate"):
12
+ self.model = model
13
+ self.endpoint = endpoint
14
+
15
+ def analyze(self, findings: List[Dict[str, Any]]) -> str:
16
+ if not findings:
17
+ return "No findings to analyze."
18
+
19
+ criticals = [f for f in findings if f.get("severity") == "CRITICAL"]
20
+ highs = [f for f in findings if f.get("severity") == "HIGH"]
21
+ mediums = [f for f in findings if f.get("severity") == "MEDIUM"]
22
+ lows = [f for f in findings if f.get("severity") == "LOW"]
23
+
24
+ prompt = f"""You are a DevSecOps security expert. Analyze the following aggregated security findings from CI/CD scanners (Trivy, Semgrep, Poutine, Zizmor).
25
+ Total findings: {len(findings)} (CRITICAL: {len(criticals)}, HIGH: {len(highs)}, MEDIUM: {len(mediums)}, LOW: {len(lows)})
26
+
27
+ List of findings:
28
+ {json.dumps(findings, indent=2)}
29
+
30
+ Provide:
31
+ 1. A short executive summary (2-3 sentences) of the overall risk.
32
+ 2. Identify possible attack paths (e.g., "a vulnerable package combined with an exposed secret could lead to RCE").
33
+ 3. Recommend the top 3 most critical actions to fix.
34
+ Keep the response concise and use bullet points."""
35
+ try:
36
+ resp = requests.post(
37
+ self.endpoint,
38
+ json={"model": self.model, "prompt": prompt, "stream": False},
39
+ timeout=120
40
+ )
41
+ resp.raise_for_status()
42
+ result = resp.json()
43
+ return result.get("response", "No AI response.")
44
+ except Exception as e:
45
+ return f"AI analysis failed: {str(e)}"
@@ -0,0 +1,54 @@
1
+ import sqlite3
2
+ import json
3
+ from datetime import datetime
4
+ from typing import List, Dict, Any
5
+
6
+ DB_PATH = "scan_history.db"
7
+
8
+ def init_db():
9
+ conn = sqlite3.connect(DB_PATH)
10
+ c = conn.cursor()
11
+ c.execute('''
12
+ CREATE TABLE IF NOT EXISTS scans (
13
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
14
+ timestamp TEXT NOT NULL,
15
+ findings_json TEXT NOT NULL
16
+ )
17
+ ''')
18
+ conn.commit()
19
+ conn.close()
20
+
21
+ def save_scan(findings: List[Dict[str, Any]]):
22
+ init_db()
23
+ conn = sqlite3.connect(DB_PATH)
24
+ c = conn.cursor()
25
+ c.execute('INSERT INTO scans (timestamp, findings_json) VALUES (?, ?)',
26
+ (datetime.utcnow().isoformat(), json.dumps(findings)))
27
+ conn.commit()
28
+ conn.close()
29
+
30
+ def get_all_scans() -> List[Dict[str, Any]]:
31
+ init_db()
32
+ conn = sqlite3.connect(DB_PATH)
33
+ conn.row_factory = sqlite3.Row
34
+ c = conn.cursor()
35
+ c.execute('SELECT id, timestamp, findings_json FROM scans ORDER BY timestamp ASC')
36
+ rows = c.fetchall()
37
+ scans = []
38
+ for row in rows:
39
+ findings = json.loads(row['findings_json'])
40
+ criticals = sum(1 for f in findings if f.get('severity') == 'CRITICAL')
41
+ highs = sum(1 for f in findings if f.get('severity') == 'HIGH')
42
+ mediums = sum(1 for f in findings if f.get('severity') == 'MEDIUM')
43
+ lows = sum(1 for f in findings if f.get('severity') == 'LOW')
44
+ scans.append({
45
+ 'id': row['id'],
46
+ 'timestamp': row['timestamp'],
47
+ 'total': len(findings),
48
+ 'critical': criticals,
49
+ 'high': highs,
50
+ 'medium': mediums,
51
+ 'low': lows
52
+ })
53
+ conn.close()
54
+ return scans
@@ -0,0 +1,44 @@
1
+ import json
2
+ from typing import List, Dict, Any
3
+
4
+ def parse_trivy_json(file_path: str) -> List[Dict[str, Any]]:
5
+ with open(file_path) as f:
6
+ data = json.load(f)
7
+ findings = []
8
+ for result in data.get("Results", []):
9
+ target = result.get("Target", "Unknown")
10
+ for vuln in result.get("Vulnerabilities", []):
11
+ findings.append({
12
+ "tool": "Trivy",
13
+ "target": target,
14
+ "id": vuln.get("VulnerabilityID"),
15
+ "severity": vuln.get("Severity", "UNKNOWN").upper(),
16
+ "title": vuln.get("Title", ""),
17
+ "description": vuln.get("Description", ""),
18
+ "package": vuln.get("PkgName", ""),
19
+ "installed_version": vuln.get("InstalledVersion", ""),
20
+ "fixed_version": vuln.get("FixedVersion", "")
21
+ })
22
+ return findings
23
+
24
+ def parse_semgrep_json(file_path: str) -> List[Dict[str, Any]]:
25
+ with open(file_path) as f:
26
+ data = json.load(f)
27
+ findings = []
28
+ for result in data.get("results", []):
29
+ findings.append({
30
+ "tool": "Semgrep",
31
+ "target": result.get("path", ""),
32
+ "id": result.get("check_id", ""),
33
+ "severity": result.get("extra", {}).get("severity", "WARNING").upper(),
34
+ "title": result.get("check_id", ""),
35
+ "description": result.get("extra", {}).get("message", ""),
36
+ "line": result.get("start", {}).get("line", 0),
37
+ })
38
+ return findings
39
+
40
+ def merge_findings(*finding_lists) -> List[Dict[str, Any]]:
41
+ merged = []
42
+ for lst in finding_lists:
43
+ merged.extend(lst)
44
+ return merged
@@ -0,0 +1,11 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import List, Dict, Any
3
+
4
+ class BaseScanner(ABC):
5
+ @abstractmethod
6
+ def run(self, target: str) -> List[Dict[str, Any]]:
7
+ pass
8
+
9
+ @abstractmethod
10
+ def parse(self, file_path: str) -> List[Dict[str, Any]]:
11
+ pass
@@ -0,0 +1,33 @@
1
+ import json
2
+ import subprocess
3
+ import tempfile
4
+ import os
5
+ from typing import List, Dict, Any
6
+ from .base import BaseScanner
7
+
8
+ class PoutineScanner(BaseScanner):
9
+ def run(self, target: str) -> List[Dict[str, Any]]:
10
+ with tempfile.NamedTemporaryFile(suffix='.json', delete=False) as tmp:
11
+ outfile = tmp.name
12
+ try:
13
+ subprocess.run(['poutine', 'scan', target, '--format', 'json', '--output', outfile], check=True)
14
+ return self.parse(outfile)
15
+ finally:
16
+ if os.path.exists(outfile):
17
+ os.unlink(outfile)
18
+
19
+ def parse(self, file_path: str) -> List[Dict[str, Any]]:
20
+ with open(file_path) as f:
21
+ data = json.load(f)
22
+ findings = []
23
+ for result in data.get("findings", []):
24
+ findings.append({
25
+ "tool": "Poutine",
26
+ "target": result.get("location", {}).get("file", ""),
27
+ "id": result.get("rule_id", ""),
28
+ "severity": result.get("severity", "UNKNOWN").upper(),
29
+ "title": result.get("message", ""),
30
+ "description": result.get("description", ""),
31
+ "line": result.get("location", {}).get("line", 0),
32
+ })
33
+ return findings
@@ -0,0 +1,33 @@
1
+ import json
2
+ import subprocess
3
+ import tempfile
4
+ import os
5
+ from typing import List, Dict, Any
6
+ from .base import BaseScanner
7
+
8
+ class SemgrepScanner(BaseScanner):
9
+ def run(self, target: str) -> List[Dict[str, Any]]:
10
+ with tempfile.NamedTemporaryFile(suffix='.json', delete=False) as tmp:
11
+ outfile = tmp.name
12
+ try:
13
+ subprocess.run(['semgrep', '--config=auto', '--json', '--output', outfile, target], check=True)
14
+ return self.parse(outfile)
15
+ finally:
16
+ if os.path.exists(outfile):
17
+ os.unlink(outfile)
18
+
19
+ def parse(self, file_path: str) -> List[Dict[str, Any]]:
20
+ with open(file_path) as f:
21
+ data = json.load(f)
22
+ findings = []
23
+ for result in data.get("results", []):
24
+ findings.append({
25
+ "tool": "Semgrep",
26
+ "target": result.get("path", ""),
27
+ "id": result.get("check_id", ""),
28
+ "severity": result.get("extra", {}).get("severity", "WARNING").upper(),
29
+ "title": result.get("check_id", ""),
30
+ "description": result.get("extra", {}).get("message", ""),
31
+ "line": result.get("start", {}).get("line", 0),
32
+ })
33
+ return findings
@@ -0,0 +1,37 @@
1
+ import json
2
+ import subprocess
3
+ import tempfile
4
+ import os
5
+ from typing import List, Dict, Any
6
+ from .base import BaseScanner
7
+
8
+ class TrivyScanner(BaseScanner):
9
+ def run(self, target: str) -> List[Dict[str, Any]]:
10
+ with tempfile.NamedTemporaryFile(suffix='.json', delete=False) as tmp:
11
+ outfile = tmp.name
12
+ try:
13
+ subprocess.run(['trivy', 'image', '--format', 'json', '--output', outfile, target], check=True)
14
+ return self.parse(outfile)
15
+ finally:
16
+ if os.path.exists(outfile):
17
+ os.unlink(outfile)
18
+
19
+ def parse(self, file_path: str) -> List[Dict[str, Any]]:
20
+ with open(file_path) as f:
21
+ data = json.load(f)
22
+ findings = []
23
+ for result in data.get("Results", []):
24
+ target_name = result.get("Target", "Unknown")
25
+ for vuln in result.get("Vulnerabilities", []):
26
+ findings.append({
27
+ "tool": "Trivy",
28
+ "target": target_name,
29
+ "id": vuln.get("VulnerabilityID"),
30
+ "severity": vuln.get("Severity", "UNKNOWN").upper(),
31
+ "title": vuln.get("Title", ""),
32
+ "description": vuln.get("Description", ""),
33
+ "package": vuln.get("PkgName", ""),
34
+ "installed_version": vuln.get("InstalledVersion", ""),
35
+ "fixed_version": vuln.get("FixedVersion", "")
36
+ })
37
+ return findings
@@ -0,0 +1,33 @@
1
+ import json
2
+ import subprocess
3
+ import tempfile
4
+ import os
5
+ from typing import List, Dict, Any
6
+ from .base import BaseScanner
7
+
8
+ class ZizmorScanner(BaseScanner):
9
+ def run(self, target: str) -> List[Dict[str, Any]]:
10
+ with tempfile.NamedTemporaryFile(suffix='.json', delete=False) as tmp:
11
+ outfile = tmp.name
12
+ try:
13
+ subprocess.run(['zizmor', 'scan', target, '--output', outfile, '--format', 'json'], check=True)
14
+ return self.parse(outfile)
15
+ finally:
16
+ if os.path.exists(outfile):
17
+ os.unlink(outfile)
18
+
19
+ def parse(self, file_path: str) -> List[Dict[str, Any]]:
20
+ with open(file_path) as f:
21
+ data = json.load(f)
22
+ findings = []
23
+ for result in data.get("findings", []):
24
+ findings.append({
25
+ "tool": "Zizmor",
26
+ "target": result.get("path", ""),
27
+ "id": result.get("rule_id", ""),
28
+ "severity": result.get("severity", "UNKNOWN").upper(),
29
+ "title": result.get("message", ""),
30
+ "description": result.get("description", ""),
31
+ "line": result.get("location", {}).get("line", 0),
32
+ })
33
+ return findings
File without changes
@@ -0,0 +1,33 @@
1
+ from flask import Flask, render_template, jsonify
2
+ import json
3
+ import os
4
+ from devsecops_radar.core.database import get_all_scans
5
+
6
+ app = Flask(__name__)
7
+
8
+ FINDINGS_FILE = os.environ.get('FINDINGS_FILE', 'findings.json')
9
+
10
+ def load_findings():
11
+ if not os.path.exists(FINDINGS_FILE):
12
+ return []
13
+ with open(FINDINGS_FILE) as f:
14
+ return json.load(f)
15
+
16
+ @app.route('/')
17
+ def index():
18
+ findings = load_findings()
19
+ return render_template('index.html', findings=findings)
20
+
21
+ @app.route('/api/findings')
22
+ def api_findings():
23
+ return jsonify(load_findings())
24
+
25
+ @app.route('/api/history')
26
+ def api_history():
27
+ return jsonify(get_all_scans())
28
+
29
+ def start_server(host='0.0.0.0', port=8080):
30
+ app.run(host=host, port=port, debug=True)
31
+
32
+ if __name__ == '__main__':
33
+ start_server()
@@ -0,0 +1,191 @@
1
+ Metadata-Version: 2.4
2
+ Name: devsecops-radar
3
+ Version: 0.1.0
4
+ Summary: Unified CI/CD Security Dashboard — Pipeline Sentinel
5
+ Author-email: Mehrdoost <mehrdoost@users.noreply.github.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Mehrdoost/devsecops-radar
8
+ Project-URL: Source, https://github.com/Mehrdoost/devsecops-radar
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Topic :: Security
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: flask>=3.0
18
+ Requires-Dist: semgrep>=1.0
19
+ Requires-Dist: pyyaml>=6.0
20
+ Requires-Dist: requests>=2.31
21
+ Dynamic: license-file
22
+
23
+ # 🛡️ Pipeline Sentinel
24
+
25
+ **Unified CI/CD Security Observability — AI‑Enhanced & Offline‑Ready**
26
+
27
+ Aggregate findings from **Trivy, Semgrep, Poutine, Zizmor** and more into a single, beautiful dashboard. Correlate risks with an **LLM‑powered analysis engine**, track security trends over time, and enforce guardrails – all in one CLI + web UI.
28
+
29
+ [![GitHub stars](https://img.shields.io/github/stars/Mehrdoost/devsecops-radar?style=social)](https://github.com/Mehrdoost/devsecops-radar/stargazers)
30
+ [![License](https://img.shields.io/github/license/Mehrdoost/devsecops-radar)](LICENSE)
31
+ [![Docker Pulls](https://img.shields.io/docker/pulls/Mehrdoost/devsecops-radar)](https://hub.docker.com/r/Mehrdoost/devsecops-radar)
32
+ [![PyPI version](https://img.shields.io/pypi/v/devsecops-radar.svg)](https://pypi.org/project/devsecops-radar/)
33
+ [![GitHub release](https://img.shields.io/github/v/release/Mehrdoost/devsecops-radar?include_prereleases)](https://github.com/Mehrdoost/devsecops-radar/releases)
34
+ [![CI](https://github.com/Mehrdoost/devsecops-radar/actions/workflows/test-action.yml/badge.svg)](https://github.com/Mehrdoost/devsecops-radar/actions/workflows/test-action.yml)
35
+
36
+ ---
37
+
38
+ ## 🚀 Quick Start
39
+
40
+ ```bash
41
+ # Install from PyPI
42
+ pip install devsecops-radar
43
+
44
+ # Or install directly from GitHub
45
+ pip install git+[https://github.com/Mehrdoost/devsecops-radar.git](https://github.com/Mehrdoost/devsecops-radar.git)
46
+
47
+ # Run the web dashboard
48
+ devsecops-radar-web
49
+ ```
50
+
51
+ 🐳 **Docker:** `docker pull ghcr.io/mehrdoost/devsecops-radar:latest` *(see instructions below)*
52
+
53
+ ---
54
+
55
+ ## ✨ Key Features
56
+
57
+ | Capability | Description |
58
+ | :--- | :--- |
59
+ | 🔌 **Multi‑Scanner Integration** | Natively parses Trivy, Semgrep, Poutine, Zizmor. More via pluggable architecture. |
60
+ | 🧠 **LLM‑Powered Analysis** | Optional AI correlation, false‑positive reduction, attack‑path identification (Ollama‑backed, offline capable). |
61
+ | 📈 **Scan History & Trends** | SQLite‑powered historical storage. Visual trend chart shows risk evolution over time. |
62
+ | 🤖 **GitHub Action** | One‑step integration into your CI/CD. Summarises findings and optionally comments on PRs. |
63
+ | 🎨 **Beautiful Dark Dashboard** | Severity doughnut, trend line chart, search & filters – works fully offline (all assets bundled). |
64
+ | 🐳 **Docker Native** | Official image on GitHub Container Registry. Just one `docker run` away. |
65
+
66
+ ---
67
+
68
+ ## 🔧 Supported Scanners
69
+
70
+ | Scanner | What it scans | Status |
71
+ | :--- | :--- | :--- |
72
+ | **Trivy** | Container images & dependencies | ✅ |
73
+ | **Semgrep** | SAST (Static Code Analysis) | ✅ |
74
+ | **Poutine** | GitLab CI/CD configuration security | ✅ |
75
+ | **Zizmor** | GitHub Actions workflow security | ✅ |
76
+ | **Snyk, ZAP, Dependency-Track** | Roadmap | 🔲 |
77
+
78
+ *Adding a new scanner is easy – extend `BaseScanner` and plug it in.*
79
+
80
+ ---
81
+
82
+
83
+ ## 📸 Dashboard Preview
84
+
85
+ ![DevSecOps Radar Dashboard](docs/Demo.gif)
86
+
87
+ ---
88
+
89
+ ## 🤖 GitHub Action
90
+
91
+ Add security analysis to your workflow with a single step:
92
+
93
+ ```yaml
94
+ - name: Pipeline Sentinel
95
+ uses: Mehrdoost/devsecops-radar/action@main
96
+ with:
97
+ trivy_report: trivy-results.json
98
+ semgrep_report: semgrep-results.json
99
+ poutine_report: poutine-results.json
100
+ zizmor_report: zizmor-results.json
101
+ ```
102
+
103
+ The action merges findings, creates a job summary, and outputs CRITICAL/HIGH counts.
104
+
105
+ ---
106
+
107
+ ## 📊 Scan History & Trends
108
+
109
+ Every run automatically stores findings in a local `scan_history.db`.
110
+ The dashboard renders a **Trend Over Time** chart so teams can monitor whether security posture is improving.
111
+
112
+ ```bash
113
+ # Multiple scans build history
114
+ devsecops-radar --trivy sample_trivy.json --semgrep sample_semgrep.json
115
+ devsecops-radar --trivy sample_trivy.json --semgrep sample_semgrep.json --poutine sample_poutine.json
116
+
117
+ # Now view the trend in the dashboard
118
+ devsecops-radar-web
119
+ ```
120
+
121
+ ---
122
+
123
+ ## 🧠 AI‑Powered Analysis (Optional)
124
+
125
+ Enable LLM analysis with `--analyze` (requires Ollama running locally):
126
+
127
+ ```bash
128
+ ollama pull llama3.2:latest # one-time setup
129
+ devsecops-radar --trivy sample_trivy.json --semgrep sample_semgrep.json --zizmor sample_zizmor.json --analyze
130
+ ```
131
+
132
+ *Generates `findings_ai_summary.md` with executive summary, attack paths, and remediation tips.*
133
+
134
+ ---
135
+
136
+ ## 🛠️ Usage
137
+
138
+ ### From Source (Python)
139
+ ```bash
140
+ pip install -e .
141
+ devsecops-radar --trivy trivy.json --semgrep semgrep.json
142
+ devsecops-radar-web
143
+ ```
144
+
145
+ ### Docker
146
+ ```bash
147
+ docker pull ghcr.io/mehrdoost/devsecops-radar:latest
148
+ docker run -p 8080:8080 -v $(pwd)/findings.json:/data/findings.json ghcr.io/mehrdoost/devsecops-radar:latest
149
+ ```
150
+
151
+ ### Using Sample Data
152
+ ```bash
153
+ devsecops-radar --trivy sample_trivy.json --semgrep sample_semgrep.json --poutine sample_poutine.json --zizmor sample_zizmor.json
154
+ ```
155
+
156
+ ---
157
+
158
+ ## 🗺️ Roadmap
159
+
160
+ - [x] Multi‑scanner engine (Trivy, Semgrep, Poutine, Zizmor)
161
+ - [x] AI correlation & analysis
162
+ - [x] Scan history & trend visualisation
163
+ - [x] GitHub Action (composite)
164
+ - [x] Docker image (GitHub Container Registry)
165
+ - [ ] Security guardrail policies (`policy.yml`)
166
+ - [ ] AI remediation advisor (detailed fix guidance)
167
+ - [ ] Findings diff/compare between branches
168
+ - [ ] Jira / Slack integration
169
+
170
+ ---
171
+
172
+ ## 🤝 Contributing
173
+
174
+ Pull requests and issues are warmly welcome!
175
+ If you’d like to integrate a new scanner, open an issue with a sample of its JSON output.
176
+
177
+ ---
178
+
179
+ ## 👨‍💻 Author
180
+
181
+ **Mehrdoost**
182
+
183
+ [![GitHub](https://img.shields.io/badge/GitHub-Mehrdoost-181717?logo=github)](https://github.com/Mehrdoost)
184
+
185
+ ---
186
+
187
+ ## 📜 License
188
+
189
+ MIT – see [LICENSE](LICENSE) file.
190
+
191
+ ⭐ **If this project helps your team ship more secure software, please drop a star!**
@@ -0,0 +1,25 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ devsecops_radar/__init__.py
5
+ devsecops_radar.egg-info/PKG-INFO
6
+ devsecops_radar.egg-info/SOURCES.txt
7
+ devsecops_radar.egg-info/dependency_links.txt
8
+ devsecops_radar.egg-info/entry_points.txt
9
+ devsecops_radar.egg-info/requires.txt
10
+ devsecops_radar.egg-info/top_level.txt
11
+ devsecops_radar/cli/__init__.py
12
+ devsecops_radar/cli/scanner.py
13
+ devsecops_radar/core/__init__.py
14
+ devsecops_radar/core/analyzer.py
15
+ devsecops_radar/core/database.py
16
+ devsecops_radar/core/parser.py
17
+ devsecops_radar/scanners/base.py
18
+ devsecops_radar/scanners/poutine.py
19
+ devsecops_radar/scanners/semgrep.py
20
+ devsecops_radar/scanners/trivy.py
21
+ devsecops_radar/scanners/zizmor.py
22
+ devsecops_radar/web/__init__.py
23
+ devsecops_radar/web/app.py
24
+ tests/test_cli.py
25
+ tests/test_scanners.py
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ devsecops-radar = devsecops_radar.cli.scanner:main
3
+ devsecops-radar-web = devsecops_radar.web.app:start_server
@@ -0,0 +1,4 @@
1
+ flask>=3.0
2
+ semgrep>=1.0
3
+ pyyaml>=6.0
4
+ requests>=2.31
@@ -0,0 +1 @@
1
+ devsecops_radar
@@ -0,0 +1,38 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "devsecops-radar"
7
+ version = "0.1.0"
8
+ description = "Unified CI/CD Security Dashboard — Pipeline Sentinel"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ authors = [
12
+ {name = "Mehrdoost", email = "mehrdoost@users.noreply.github.com"}
13
+ ]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "Topic :: Security",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.12",
21
+ ]
22
+ dependencies = [
23
+ "flask>=3.0",
24
+ "semgrep>=1.0",
25
+ "pyyaml>=6.0",
26
+ "requests>=2.31"
27
+ ]
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/Mehrdoost/devsecops-radar"
31
+ Source = "https://github.com/Mehrdoost/devsecops-radar"
32
+
33
+ [tool.setuptools]
34
+ packages = ["devsecops_radar", "devsecops_radar.core", "devsecops_radar.cli", "devsecops_radar.web", "devsecops_radar.scanners"]
35
+
36
+ [project.scripts]
37
+ devsecops-radar = "devsecops_radar.cli.scanner:main"
38
+ devsecops-radar-web = "devsecops_radar.web.app:start_server"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,70 @@
1
+ import json
2
+ import tempfile
3
+ import os
4
+ from devsecops_radar.scanners.trivy import TrivyScanner
5
+ from devsecops_radar.scanners.semgrep import SemgrepScanner
6
+ from devsecops_radar.scanners.poutine import PoutineScanner
7
+
8
+ sample_trivy = {
9
+ "Results": [{
10
+ "Target": "test-app",
11
+ "Vulnerabilities": [{
12
+ "VulnerabilityID": "CVE-TEST-1",
13
+ "Severity": "CRITICAL",
14
+ "Title": "Test vuln",
15
+ "Description": "Test description",
16
+ "PkgName": "test-pkg",
17
+ "InstalledVersion": "1.0",
18
+ "FixedVersion": "2.0"
19
+ }]
20
+ }]
21
+ }
22
+
23
+ sample_semgrep = {
24
+ "results": [{
25
+ "path": "test.py",
26
+ "check_id": "test.rule",
27
+ "extra": {"severity": "HIGH", "message": "Test finding"},
28
+ "start": {"line": 10}
29
+ }]
30
+ }
31
+
32
+ sample_poutine = {
33
+ "findings": [{
34
+ "rule_id": "test-rule",
35
+ "severity": "MEDIUM",
36
+ "message": "Test poutine",
37
+ "description": "Desc",
38
+ "location": {"file": ".gitlab-ci.yml", "line": 1}
39
+ }]
40
+ }
41
+
42
+ def write_temp(data):
43
+ tmp = tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False)
44
+ json.dump(data, tmp)
45
+ tmp.close()
46
+ return tmp.name
47
+
48
+ def test_trivy_parse():
49
+ path = write_temp(sample_trivy)
50
+ findings = TrivyScanner().parse(path)
51
+ os.unlink(path)
52
+ assert len(findings) == 1
53
+ assert findings[0]['tool'] == 'Trivy'
54
+ assert findings[0]['severity'] == 'CRITICAL'
55
+
56
+ def test_semgrep_parse():
57
+ path = write_temp(sample_semgrep)
58
+ findings = SemgrepScanner().parse(path)
59
+ os.unlink(path)
60
+ assert len(findings) == 1
61
+ assert findings[0]['tool'] == 'Semgrep'
62
+ assert findings[0]['severity'] == 'HIGH'
63
+
64
+ def test_poutine_parse():
65
+ path = write_temp(sample_poutine)
66
+ findings = PoutineScanner().parse(path)
67
+ os.unlink(path)
68
+ assert len(findings) == 1
69
+ assert findings[0]['tool'] == 'Poutine'
70
+ assert findings[0]['severity'] == 'MEDIUM'