AdityaDevx commited on
Commit
200e3d7
Β·
1 Parent(s): dda1f70

Gradio vulnerability scanner

Browse files
Files changed (2) hide show
  1. app.py +314 -0
  2. requirements.txt +2 -5
app.py ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import re
3
+ import os
4
+ import requests
5
+ import base64
6
+ from groq import Groq
7
+
8
+ def parse_github_url(url):
9
+ """Parse GitHub URL to extract owner, repo, and file path"""
10
+ url = url.strip().rstrip('/')
11
+ # File URL
12
+ m = re.match(r"https://github\.com/([^/]+)/([^/]+)/blob/[^/]+/(.+)", url)
13
+ if m:
14
+ return m.group(1), m.group(2), m.group(3), False
15
+ # Repo URL
16
+ m = re.match(r"https://github\.com/([^/]+)/([^/]+)/?$", url)
17
+ if m:
18
+ return m.group(1), m.group(2), None, True
19
+ return None, None, None, False
20
+
21
+ def get_file_content(owner, repo, path):
22
+ """Fetch file content from GitHub"""
23
+ token = os.getenv("GITHUB_TOKEN", "")
24
+ headers = {"Authorization": f"token {token}"} if token else {}
25
+ url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}"
26
+
27
+ try:
28
+ r = requests.get(url, headers=headers, timeout=15)
29
+ if r.status_code != 200:
30
+ return None, f"GitHub API error: {r.status_code}"
31
+
32
+ data = r.json()
33
+ content = base64.b64decode(data["content"]).decode("utf-8", errors="replace")
34
+ return content, None
35
+ except Exception as e:
36
+ return None, f"Error fetching file: {str(e)}"
37
+
38
+ def get_repo_files(owner, repo, path="", max_files=15):
39
+ """Get list of code files from repository"""
40
+ token = os.getenv("GITHUB_TOKEN", "")
41
+ headers = {"Authorization": f"token {token}"} if token else {}
42
+ url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}"
43
+
44
+ try:
45
+ r = requests.get(url, headers=headers, timeout=15)
46
+ if r.status_code != 200:
47
+ return []
48
+
49
+ items = r.json()
50
+ files = []
51
+
52
+ for item in items:
53
+ if len(files) >= max_files:
54
+ break
55
+
56
+ if item['type'] == 'file':
57
+ ext = item['name'].split('.')[-1].lower()
58
+ if ext in ['py', 'js', 'jsx', 'ts', 'tsx', 'java', 'cpp', 'c', 'php', 'rb', 'go', 'rs', 'sql', 'sh']:
59
+ files.append(item['path'])
60
+ elif item['type'] == 'dir' and item['name'] not in ['.git', 'node_modules', '__pycache__', 'dist', 'build']:
61
+ files.extend(get_repo_files(owner, repo, item['path'], max_files - len(files)))
62
+
63
+ return files
64
+ except:
65
+ return []
66
+
67
+ def scan_file(owner, repo, file_path, progress=gr.Progress()):
68
+ """Scan a single file for vulnerabilities"""
69
+ progress(0.2, desc="Fetching file content...")
70
+ code, err = get_file_content(owner, repo, file_path)
71
+ if err:
72
+ return f"❌ Error: {err}"
73
+
74
+ if len(code) > 6000:
75
+ code = code[:6000] + "\n... [truncated]"
76
+
77
+ progress(0.5, desc="Analyzing with AI...")
78
+
79
+ groq_key = os.getenv("GROQ_API_KEY", "")
80
+ if not groq_key:
81
+ return "❌ Error: GROQ_API_KEY not configured"
82
+
83
+ client = Groq(api_key=groq_key)
84
+
85
+ prompt = f"""You are a cybersecurity expert. Analyze this code for security vulnerabilities.
86
+
87
+ File: {file_path}
88
+ Repository: {owner}/{repo}
89
+
90
+ ```
91
+ {code}
92
+ ```
93
+
94
+ Provide a detailed security analysis in this exact markdown format:
95
+
96
+ # πŸ›‘οΈ Security Analysis Report
97
+
98
+ ## πŸ“ File Overview
99
+ - Repository: {owner}/{repo}
100
+ - File: {file_path}
101
+ - Language: [detected language]
102
+ - Lines analyzed: [count]
103
+
104
+ ## 🚨 Vulnerabilities Found
105
+
106
+ [For each vulnerability found:]
107
+ ### [Vulnerability Name] β€” [CRITICAL/HIGH/MEDIUM/LOW]
108
+ - **Line**: [line number or range]
109
+ - **Code**: `[vulnerable snippet]`
110
+ - **Issue**: [clear explanation]
111
+ - **CVE Reference**: [relevant CVE ID if applicable]
112
+
113
+ ## πŸ› οΈ Remediation
114
+ [Specific fix for each vulnerability with corrected code]
115
+
116
+ ## πŸ“Š Risk Summary
117
+ - Critical: [n] | High: [n] | Medium: [n] | Low: [n]
118
+ - **Overall Risk**: [CRITICAL/HIGH/MEDIUM/LOW]
119
+ """
120
+
121
+ try:
122
+ response = client.chat.completions.create(
123
+ model="llama-3.3-70b-versatile",
124
+ messages=[{"role": "user", "content": prompt}],
125
+ temperature=0.1,
126
+ max_tokens=4096,
127
+ )
128
+ progress(1.0, desc="Complete!")
129
+ return response.choices[0].message.content
130
+ except Exception as e:
131
+ return f"❌ AI analysis failed: {str(e)}"
132
+
133
+ def scan_repository(owner, repo, progress=gr.Progress()):
134
+ """Scan entire repository"""
135
+ progress(0.1, desc="Fetching repository files...")
136
+ files = get_repo_files(owner, repo)
137
+
138
+ if not files:
139
+ return "❌ No code files found or repository is private"
140
+
141
+ progress(0.2, desc=f"Found {len(files)} files to scan...")
142
+
143
+ all_vulnerabilities = []
144
+ scanned_count = 0
145
+
146
+ groq_key = os.getenv("GROQ_API_KEY", "")
147
+ if not groq_key:
148
+ return "❌ Error: GROQ_API_KEY not configured"
149
+
150
+ client = Groq(api_key=groq_key)
151
+
152
+ for i, file_path in enumerate(files[:15]):
153
+ progress((0.2 + (i / 15) * 0.7), desc=f"Scanning {i+1}/{min(len(files), 15)}: {file_path}")
154
+
155
+ code, err = get_file_content(owner, repo, file_path)
156
+ if err:
157
+ continue
158
+
159
+ if len(code) > 4000:
160
+ code = code[:4000] + "\n... [truncated]"
161
+
162
+ prompt = f"""Analyze this code file for security vulnerabilities. Be concise.
163
+
164
+ File: {file_path}
165
+
166
+ ```
167
+ {code}
168
+ ```
169
+
170
+ List only CRITICAL and HIGH severity vulnerabilities found. Format:
171
+ - **[Vulnerability]** in `{file_path}` line X: [brief issue]
172
+
173
+ If no critical/high issues, respond: "No critical issues found."
174
+ """
175
+
176
+ try:
177
+ response = client.chat.completions.create(
178
+ model="llama-3.3-70b-versatile",
179
+ messages=[{"role": "user", "content": prompt}],
180
+ temperature=0.1,
181
+ max_tokens=800,
182
+ )
183
+ result = response.choices[0].message.content.strip()
184
+ if "no critical issues" not in result.lower():
185
+ all_vulnerabilities.append(f"### {file_path}\n{result}\n")
186
+ scanned_count += 1
187
+ except:
188
+ continue
189
+
190
+ progress(1.0, desc="Generating report...")
191
+
192
+ if not all_vulnerabilities:
193
+ report = f"""# πŸ›‘οΈ Repository Security Scan Report
194
+
195
+ ## πŸ“¦ Repository Overview
196
+ - Repository: {owner}/{repo}
197
+ - Files Scanned: {scanned_count}
198
+ - Status: βœ… No critical vulnerabilities detected
199
+
200
+ ## πŸ“Š Summary
201
+ All scanned files passed security checks. No CRITICAL or HIGH severity issues found.
202
+
203
+ ## πŸ“ˆ Risk Summary
204
+ - Critical: 0 | High: 0 | Medium: 0 | Low: 0
205
+ - **Overall Risk**: LOW
206
+ """
207
+ else:
208
+ report = f"""# πŸ›‘οΈ Repository Security Scan Report
209
+
210
+ ## πŸ“¦ Repository Overview
211
+ - Repository: {owner}/{repo}
212
+ - Files Scanned: {scanned_count}
213
+ - Vulnerabilities Found: {len(all_vulnerabilities)} files with issues
214
+
215
+ ## 🚨 Vulnerabilities by File
216
+
217
+ {''.join(all_vulnerabilities)}
218
+
219
+ ## πŸ› οΈ Recommendations
220
+ 1. Review and fix all CRITICAL and HIGH severity issues immediately
221
+ 2. Implement input validation and sanitization
222
+ 3. Use parameterized queries for database operations
223
+ 4. Keep dependencies updated
224
+
225
+ ## πŸ“Š Risk Summary
226
+ - Critical: {sum(1 for v in all_vulnerabilities if 'CRITICAL' in v)} | High: {sum(1 for v in all_vulnerabilities if 'HIGH' in v)} | Medium: 0 | Low: 0
227
+ - **Overall Risk**: {'CRITICAL' if any('CRITICAL' in v for v in all_vulnerabilities) else 'HIGH'}
228
+ """
229
+
230
+ return report
231
+
232
+ def analyze_github_url(url, progress=gr.Progress()):
233
+ """Main analysis function"""
234
+ if not url or not url.strip():
235
+ return "❌ Please provide a GitHub URL"
236
+
237
+ owner, repo, file_path, is_repo = parse_github_url(url)
238
+
239
+ if not owner or not repo:
240
+ return "❌ Invalid GitHub URL. Use:\n- File: github.com/owner/repo/blob/branch/file.py\n- Repo: github.com/owner/repo"
241
+
242
+ if is_repo:
243
+ return scan_repository(owner, repo, progress)
244
+ else:
245
+ return scan_file(owner, repo, file_path, progress)
246
+
247
+ # Gradio Interface
248
+ CSS = """
249
+ .gradio-container {
250
+ font-family: 'Inter', sans-serif;
251
+ }
252
+ .cyber-title {
253
+ text-align: center;
254
+ color: #00ff41;
255
+ font-size: 2em;
256
+ font-weight: bold;
257
+ margin-bottom: 10px;
258
+ }
259
+ """
260
+
261
+ with gr.Blocks(css=CSS, title="πŸ›‘οΈ Vulnerability Scanner") as demo:
262
+ gr.HTML("""
263
+ <div class="cyber-title">
264
+ πŸ›‘οΈ VULNERABILITY SCANNER
265
+ </div>
266
+ <p style="text-align:center;color:#888;">
267
+ AI-powered security analysis for GitHub repositories and files
268
+ </p>
269
+ """)
270
+
271
+ with gr.Row():
272
+ with gr.Column():
273
+ url_input = gr.Textbox(
274
+ label="GitHub URL",
275
+ placeholder="https://github.com/owner/repo/blob/main/file.py or https://github.com/owner/repo",
276
+ lines=2
277
+ )
278
+
279
+ with gr.Row():
280
+ scan_btn = gr.Button("πŸ” Scan for Vulnerabilities", variant="primary")
281
+ clear_btn = gr.Button("πŸ—‘οΈ Clear")
282
+
283
+ gr.Examples(
284
+ examples=[
285
+ ["https://github.com/ayushmittal62/vunreability_scanner_testing/blob/master/python/database.py"],
286
+ ["https://github.com/ayushmittal62/vunreability_scanner_testing"],
287
+ ],
288
+ inputs=url_input,
289
+ label="πŸ“ Example URLs"
290
+ )
291
+
292
+ with gr.Row():
293
+ output = gr.Markdown(label="Analysis Results")
294
+
295
+ scan_btn.click(
296
+ fn=analyze_github_url,
297
+ inputs=[url_input],
298
+ outputs=[output]
299
+ )
300
+
301
+ clear_btn.click(
302
+ fn=lambda: ("", ""),
303
+ outputs=[url_input, output]
304
+ )
305
+
306
+ gr.HTML("""
307
+ <div style="text-align:center;margin-top:20px;color:#666;font-size:0.9em;">
308
+ Built by <a href="https://github.com/AdityaDev-X" target="_blank">AdityaDev-X</a> |
309
+ Powered by Groq LLaMA 3.3 70B
310
+ </div>
311
+ """)
312
+
313
+ if __name__ == "__main__":
314
+ demo.launch(server_name="0.0.0.0", server_port=7860)
requirements.txt CHANGED
@@ -1,7 +1,4 @@
1
- fastapi
2
- uvicorn
3
- pydantic
4
  requests
5
  python-dotenv
6
- markdownify
7
- groq
 
1
+ gradio
2
+ groq
 
3
  requests
4
  python-dotenv