zen-ai-pentest 2.0.0__py3-none-any.whl

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 (75) hide show
  1. agents/__init__.py +28 -0
  2. agents/agent_base.py +239 -0
  3. agents/agent_orchestrator.py +346 -0
  4. agents/analysis_agent.py +225 -0
  5. agents/cli.py +258 -0
  6. agents/exploit_agent.py +224 -0
  7. agents/integration.py +211 -0
  8. agents/post_scan_agent.py +937 -0
  9. agents/react_agent.py +384 -0
  10. agents/react_agent_enhanced.py +616 -0
  11. agents/react_agent_vm.py +298 -0
  12. agents/research_agent.py +176 -0
  13. api/__init__.py +11 -0
  14. api/auth.py +123 -0
  15. api/main.py +1027 -0
  16. api/schemas.py +357 -0
  17. api/websocket.py +97 -0
  18. autonomous/__init__.py +122 -0
  19. autonomous/agent.py +253 -0
  20. autonomous/agent_loop.py +1370 -0
  21. autonomous/exploit_validator.py +1537 -0
  22. autonomous/memory.py +448 -0
  23. autonomous/react.py +339 -0
  24. autonomous/tool_executor.py +488 -0
  25. backends/__init__.py +16 -0
  26. backends/chatgpt_direct.py +133 -0
  27. backends/claude_direct.py +130 -0
  28. backends/duckduckgo.py +138 -0
  29. backends/openrouter.py +120 -0
  30. benchmarks/__init__.py +149 -0
  31. benchmarks/benchmark_engine.py +904 -0
  32. benchmarks/ci_benchmark.py +785 -0
  33. benchmarks/comparison.py +729 -0
  34. benchmarks/metrics.py +553 -0
  35. benchmarks/run_benchmarks.py +809 -0
  36. ci_cd/__init__.py +2 -0
  37. core/__init__.py +17 -0
  38. core/async_pool.py +282 -0
  39. core/asyncio_fix.py +222 -0
  40. core/cache.py +472 -0
  41. core/container.py +277 -0
  42. core/database.py +114 -0
  43. core/input_validator.py +353 -0
  44. core/models.py +288 -0
  45. core/orchestrator.py +611 -0
  46. core/plugin_manager.py +571 -0
  47. core/rate_limiter.py +405 -0
  48. core/secure_config.py +328 -0
  49. core/shield_integration.py +296 -0
  50. modules/__init__.py +46 -0
  51. modules/cve_database.py +362 -0
  52. modules/exploit_assist.py +330 -0
  53. modules/nuclei_integration.py +480 -0
  54. modules/osint.py +604 -0
  55. modules/protonvpn.py +554 -0
  56. modules/recon.py +165 -0
  57. modules/sql_injection_db.py +826 -0
  58. modules/tool_orchestrator.py +498 -0
  59. modules/vuln_scanner.py +292 -0
  60. modules/wordlist_generator.py +566 -0
  61. risk_engine/__init__.py +99 -0
  62. risk_engine/business_impact.py +267 -0
  63. risk_engine/business_impact_calculator.py +563 -0
  64. risk_engine/cvss.py +156 -0
  65. risk_engine/epss.py +190 -0
  66. risk_engine/example_usage.py +294 -0
  67. risk_engine/false_positive_engine.py +1073 -0
  68. risk_engine/scorer.py +304 -0
  69. web_ui/backend/main.py +471 -0
  70. zen_ai_pentest-2.0.0.dist-info/METADATA +795 -0
  71. zen_ai_pentest-2.0.0.dist-info/RECORD +75 -0
  72. zen_ai_pentest-2.0.0.dist-info/WHEEL +5 -0
  73. zen_ai_pentest-2.0.0.dist-info/entry_points.txt +2 -0
  74. zen_ai_pentest-2.0.0.dist-info/licenses/LICENSE +21 -0
  75. zen_ai_pentest-2.0.0.dist-info/top_level.txt +10 -0
@@ -0,0 +1,498 @@
1
+ """
2
+ Tool Orchestrator - Integration with Classic Pentesting Tools
3
+ Connects Zen AI Pentest with containerized classic tools via Integration Bridge
4
+ """
5
+
6
+ import asyncio
7
+ import json
8
+ import logging
9
+ from dataclasses import dataclass
10
+ from datetime import datetime
11
+ from pathlib import Path
12
+ from typing import Any, Dict, List, Optional
13
+
14
+ import aiohttp
15
+ from rich.console import Console
16
+ from rich.table import Table
17
+
18
+ logger = logging.getLogger(__name__)
19
+ console = Console()
20
+
21
+
22
+ @dataclass
23
+ class ToolConfig:
24
+ """Configuration for a pentesting tool"""
25
+
26
+ name: str
27
+ bridge_endpoint: str
28
+ description: str
29
+ typical_duration: int # seconds
30
+
31
+
32
+ # Tool registry
33
+ PENTEST_TOOLS = {
34
+ "nmap": ToolConfig(
35
+ name="nmap",
36
+ bridge_endpoint="/api/v1/scan/nmap",
37
+ description="Network port scanner with NSE scripting",
38
+ typical_duration=300,
39
+ ),
40
+ "sqlmap": ToolConfig(
41
+ name="sqlmap",
42
+ bridge_endpoint="/api/v1/scan/sqlmap",
43
+ description="Automated SQL injection scanner",
44
+ typical_duration=600,
45
+ ),
46
+ "metasploit": ToolConfig(
47
+ name="metasploit",
48
+ bridge_endpoint="/api/v1/scan/metasploit",
49
+ description="Exploitation framework",
50
+ typical_duration=300,
51
+ ),
52
+ "nuclei": ToolConfig(
53
+ name="nuclei",
54
+ bridge_endpoint="/api/v1/scan/nuclei",
55
+ description="Fast vulnerability scanner",
56
+ typical_duration=600,
57
+ ),
58
+ "gobuster": ToolConfig(
59
+ name="gobuster",
60
+ bridge_endpoint="/api/v1/scan/gobuster",
61
+ description="Directory/file bruteforcer",
62
+ typical_duration=180,
63
+ ),
64
+ "amass": ToolConfig(
65
+ name="amass",
66
+ bridge_endpoint="/api/v1/scan/amass",
67
+ description="Subdomain enumeration",
68
+ typical_duration=300,
69
+ ),
70
+ "wpscan": ToolConfig(
71
+ name="wpscan",
72
+ bridge_endpoint="/api/v1/scan/wpscan",
73
+ description="WordPress vulnerability scanner",
74
+ typical_duration=300,
75
+ ),
76
+ "nikto": ToolConfig(
77
+ name="nikto",
78
+ bridge_endpoint="/api/v1/scan/nikto",
79
+ description="Web server scanner",
80
+ typical_duration=600,
81
+ ),
82
+ }
83
+
84
+
85
+ class ToolOrchestrator:
86
+ """
87
+ Orchestrates classic pentesting tools through the Integration Bridge
88
+ """
89
+
90
+ def __init__(self, bridge_url: str = "http://integration-bridge:8080"):
91
+ self.bridge_url = bridge_url
92
+ self.session: Optional[aiohttp.ClientSession] = None
93
+ self.active_scans: Dict[str, Dict[str, Any]] = {}
94
+
95
+ async def __aenter__(self):
96
+ self.session = aiohttp.ClientSession()
97
+ return self
98
+
99
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
100
+ if self.session:
101
+ await self.session.close()
102
+
103
+ async def health_check(self) -> bool:
104
+ """Check if integration bridge is available"""
105
+ try:
106
+ async with self.session.get(f"{self.bridge_url}/health") as resp:
107
+ return resp.status == 200
108
+ except Exception as e:
109
+ logger.error(f"Bridge health check failed: {e}")
110
+ return False
111
+
112
+ async def scan_with_nmap(
113
+ self,
114
+ target: str,
115
+ scan_type: str = "tcp_syn",
116
+ ports: str = "top-1000",
117
+ scripts: Optional[List[str]] = None,
118
+ ) -> Dict[str, Any]:
119
+ """
120
+ Run Nmap scan against target
121
+
122
+ Args:
123
+ target: Target IP/hostname
124
+ scan_type: tcp_syn, tcp_connect, udp, comprehensive
125
+ ports: Port specification (top-1000, all, or specific range)
126
+ scripts: List of NSE scripts to run
127
+ """
128
+ payload = {
129
+ "target": target,
130
+ "scan_type": scan_type,
131
+ "ports": ports,
132
+ "scripts": scripts or [],
133
+ }
134
+
135
+ return await self._trigger_scan("nmap", payload)
136
+
137
+ async def scan_with_sqlmap(
138
+ self,
139
+ url: str,
140
+ method: str = "GET",
141
+ data: Optional[str] = None,
142
+ level: int = 1,
143
+ risk: int = 1,
144
+ ) -> Dict[str, Any]:
145
+ """
146
+ Run SQLMap scan against URL
147
+
148
+ Args:
149
+ url: Target URL
150
+ method: HTTP method
151
+ data: POST data
152
+ level: Test level (1-5)
153
+ risk: Risk level (1-3)
154
+ """
155
+ payload = {
156
+ "target": url,
157
+ "url": url,
158
+ "method": method,
159
+ "data": data,
160
+ "level": level,
161
+ "risk": risk,
162
+ }
163
+
164
+ return await self._trigger_scan("sqlmap", payload)
165
+
166
+ async def scan_with_nuclei(
167
+ self,
168
+ target: str,
169
+ severity: Optional[str] = None,
170
+ templates: Optional[List[str]] = None,
171
+ concurrency: int = 50,
172
+ ) -> Dict[str, Any]:
173
+ """
174
+ Run Nuclei vulnerability scan
175
+
176
+ Args:
177
+ target: Target URL
178
+ severity: Filter by severity (info, low, medium, high, critical)
179
+ templates: Specific templates to run
180
+ concurrency: Number of concurrent requests
181
+ """
182
+ payload = {
183
+ "target": target,
184
+ "options": {
185
+ "severity": severity,
186
+ "templates": templates or [],
187
+ "concurrency": concurrency,
188
+ },
189
+ }
190
+
191
+ return await self._trigger_scan("nuclei", payload)
192
+
193
+ async def scan_with_gobuster(
194
+ self,
195
+ target: str,
196
+ mode: str = "dir",
197
+ wordlist: str = "/wordlists/dirb/common.txt",
198
+ extensions: Optional[str] = None,
199
+ ) -> Dict[str, Any]:
200
+ """
201
+ Run Gobuster directory scan
202
+
203
+ Args:
204
+ target: Target URL
205
+ mode: dir, dns, fuzz
206
+ wordlist: Wordlist path
207
+ extensions: File extensions to search
208
+ """
209
+ payload = {
210
+ "target": target,
211
+ "options": {"mode": mode, "wordlist": wordlist, "extensions": extensions},
212
+ }
213
+
214
+ return await self._trigger_scan("gobuster", payload)
215
+
216
+ async def enumerate_subdomains(
217
+ self, domain: str, active: bool = False
218
+ ) -> Dict[str, Any]:
219
+ """
220
+ Enumerate subdomains with Amass
221
+
222
+ Args:
223
+ domain: Target domain
224
+ active: Enable active reconnaissance
225
+ """
226
+ payload = {"target": domain, "options": {"active": active}}
227
+
228
+ return await self._trigger_scan("amass", payload)
229
+
230
+ async def scan_wordpress(
231
+ self, url: str, enumerate_plugins: bool = True
232
+ ) -> Dict[str, Any]:
233
+ """
234
+ Scan WordPress site with WPScan
235
+
236
+ Args:
237
+ url: WordPress site URL
238
+ enumerate_plugins: Enumerate plugins/themes
239
+ """
240
+ payload = {"target": url, "options": {"enumerate": enumerate_plugins}}
241
+
242
+ return await self._trigger_scan("wpscan", payload)
243
+
244
+ async def run_msf_module(
245
+ self,
246
+ module: str,
247
+ rhosts: str,
248
+ rport: int = 443,
249
+ options: Optional[Dict[str, Any]] = None,
250
+ ) -> Dict[str, Any]:
251
+ """
252
+ Run Metasploit module
253
+
254
+ Args:
255
+ module: Metasploit module path
256
+ rhosts: Target hosts
257
+ rport: Target port
258
+ options: Additional module options
259
+ """
260
+ payload = {
261
+ "module": module,
262
+ "rhosts": rhosts,
263
+ "rport": rport,
264
+ "options": options or {},
265
+ }
266
+
267
+ return await self._trigger_scan("metasploit", payload)
268
+
269
+ async def _trigger_scan(self, tool: str, payload: Dict[str, Any]) -> Dict[str, Any]:
270
+ """Trigger scan via integration bridge"""
271
+ tool_config = PENTEST_TOOLS.get(tool)
272
+ if not tool_config:
273
+ raise ValueError(f"Unknown tool: {tool}")
274
+
275
+ url = f"{self.bridge_url}{tool_config.bridge_endpoint}"
276
+
277
+ logger.info(f"Triggering {tool} scan: {payload.get('target', 'N/A')}")
278
+
279
+ try:
280
+ async with self.session.post(url, json=payload) as resp:
281
+ if resp.status != 200:
282
+ error_text = await resp.text()
283
+ raise RuntimeError(f"Bridge returned {resp.status}: {error_text}")
284
+
285
+ result = await resp.json()
286
+ self.active_scans[result["scan_id"]] = {
287
+ "tool": tool,
288
+ "target": payload.get("target", "N/A"),
289
+ "status": "triggered",
290
+ "start_time": datetime.utcnow(),
291
+ }
292
+ return result
293
+
294
+ except Exception as e:
295
+ logger.error(f"Failed to trigger {tool} scan: {e}")
296
+ raise
297
+
298
+ async def wait_for_scan(
299
+ self, scan_id: str, poll_interval: int = 10, timeout: int = 3600
300
+ ) -> Dict[str, Any]:
301
+ """
302
+ Wait for scan to complete
303
+
304
+ Args:
305
+ scan_id: Scan ID to wait for
306
+ poll_interval: Seconds between status checks
307
+ timeout: Maximum wait time in seconds
308
+ """
309
+ start_time = datetime.utcnow()
310
+
311
+ while True:
312
+ # Check timeout
313
+ elapsed = (datetime.utcnow() - start_time).total_seconds()
314
+ if elapsed > timeout:
315
+ raise TimeoutError(f"Scan {scan_id} timed out after {timeout}s")
316
+
317
+ # Get status
318
+ status = await self.get_scan_status(scan_id)
319
+ current_status = status.get("status", "unknown")
320
+
321
+ if current_status in ("completed", "failed", "cancelled"):
322
+ return status
323
+
324
+ logger.debug(f"Scan {scan_id} status: {current_status}, waiting...")
325
+ await asyncio.sleep(poll_interval)
326
+
327
+ async def get_scan_status(self, scan_id: str) -> Dict[str, Any]:
328
+ """Get scan status from bridge"""
329
+ url = f"{self.bridge_url}/api/v1/scan/{scan_id}"
330
+
331
+ async with self.session.get(url) as resp:
332
+ if resp.status == 404:
333
+ return {"status": "not_found"}
334
+ resp.raise_for_status()
335
+ return await resp.json()
336
+
337
+ async def get_scan_results(self, scan_id: str) -> Dict[str, Any]:
338
+ """Get parsed scan results"""
339
+ url = f"{self.bridge_url}/api/v1/scan/{scan_id}/results"
340
+
341
+ async with self.session.get(url) as resp:
342
+ resp.raise_for_status()
343
+ return await resp.json()
344
+
345
+ async def run_comprehensive_scan(
346
+ self, target: str, scan_type: str = "web", tools: Optional[List[str]] = None
347
+ ) -> Dict[str, Any]:
348
+ """
349
+ Run comprehensive multi-tool scan
350
+
351
+ Args:
352
+ target: Target host/URL
353
+ scan_type: web, network, full
354
+ tools: Specific tools to use (None = auto-select)
355
+ """
356
+ results = {
357
+ "target": target,
358
+ "scan_type": scan_type,
359
+ "start_time": datetime.utcnow().isoformat(),
360
+ "scans": {},
361
+ }
362
+
363
+ # Auto-select tools based on scan type
364
+ if not tools:
365
+ if scan_type == "web":
366
+ tools = ["nmap", "nuclei", "gobuster", "nikto"]
367
+ elif scan_type == "network":
368
+ tools = ["nmap", "amass"]
369
+ elif scan_type == "full":
370
+ tools = ["nmap", "nuclei", "gobuster", "amass", "nikto"]
371
+
372
+ console.print(
373
+ f"[bold green]Starting comprehensive {scan_type} scan against {target}[/]"
374
+ )
375
+ console.print(f"[dim]Tools: {', '.join(tools)}[/]\n")
376
+
377
+ # Run scans concurrently
378
+ scan_tasks = []
379
+ for tool in tools:
380
+ if tool in PENTEST_TOOLS:
381
+ task = self._run_tool_with_progress(tool, target)
382
+ scan_tasks.append((tool, task))
383
+
384
+ # Execute all scans
385
+ for tool, task in scan_tasks:
386
+ try:
387
+ scan_result = await task
388
+ results["scans"][tool] = scan_result
389
+ console.print(f"[green]✓[/] {tool} completed")
390
+ except Exception as e:
391
+ console.print(f"[red]✗[/] {tool} failed: {e}")
392
+ results["scans"][tool] = {"error": str(e)}
393
+
394
+ results["end_time"] = datetime.utcnow().isoformat()
395
+
396
+ return results
397
+
398
+ async def _run_tool_with_progress(self, tool: str, target: str) -> Dict[str, Any]:
399
+ """Run tool with progress indication"""
400
+ config = PENTEST_TOOLS[tool]
401
+
402
+ with console.status(f"[bold cyan]Running {tool}...[/]"):
403
+ # Trigger scan
404
+ if tool == "nmap":
405
+ result = await self.scan_with_nmap(target)
406
+ elif tool == "nuclei":
407
+ result = await self.scan_with_nuclei(target)
408
+ elif tool == "gobuster":
409
+ result = await self.scan_with_gobuster(target)
410
+ elif tool == "amass":
411
+ result = await self.enumerate_subdomains(target)
412
+ elif tool == "wpscan":
413
+ result = await self.scan_wordpress(target)
414
+ else:
415
+ raise ValueError(f"Tool {tool} not implemented")
416
+
417
+ scan_id = result["scan_id"]
418
+
419
+ # Wait for completion
420
+ final_status = await self.wait_for_scan(
421
+ scan_id, poll_interval=5, timeout=config.typical_duration * 2
422
+ )
423
+
424
+ # Get results
425
+ try:
426
+ scan_results = await self.get_scan_results(scan_id)
427
+ except Exception:
428
+ scan_results = {
429
+ "status": "completed",
430
+ "raw": final_status.get("raw_output", ""),
431
+ }
432
+
433
+ return {
434
+ "scan_id": scan_id,
435
+ "status": final_status.get("status"),
436
+ "results": scan_results,
437
+ "duration": config.typical_duration,
438
+ }
439
+
440
+ def display_scan_summary(self, results: Dict[str, Any]):
441
+ """Display scan results in formatted table"""
442
+ table = Table(title=f"Scan Results: {results['target']}")
443
+ table.add_column("Tool", style="cyan")
444
+ table.add_column("Status", style="green")
445
+ table.add_column("Findings", style="yellow")
446
+ table.add_column("Duration", style="dim")
447
+
448
+ for tool, result in results.get("scans", {}).items():
449
+ status = result.get("status", "unknown")
450
+ status_emoji = (
451
+ "✓" if status == "completed" else "✗" if "error" in result else "⏳"
452
+ )
453
+
454
+ # Count findings if available
455
+ findings = "N/A"
456
+ if "results" in result:
457
+ if isinstance(result["results"], list):
458
+ findings = str(len(result["results"]))
459
+ elif isinstance(result["results"], dict):
460
+ findings = str(len(result["results"].get("hosts", [])))
461
+
462
+ duration = str(result.get("duration", "N/A"))
463
+
464
+ table.add_row(tool, f"{status_emoji} {status}", findings, f"{duration}s")
465
+
466
+ console.print(table)
467
+
468
+
469
+ # Convenience functions for direct usage
470
+ async def quick_nmap_scan(
471
+ target: str, bridge_url: str = "http://localhost:8080"
472
+ ) -> Dict[str, Any]:
473
+ """Quick Nmap scan wrapper"""
474
+ async with ToolOrchestrator(bridge_url) as orch:
475
+ result = await orch.scan_with_nmap(target, scan_type="quick")
476
+ return await orch.wait_for_scan(result["scan_id"])
477
+
478
+
479
+ async def quick_web_scan(
480
+ url: str, bridge_url: str = "http://localhost:8080"
481
+ ) -> Dict[str, Any]:
482
+ """Quick web vulnerability scan with Nuclei"""
483
+ async with ToolOrchestrator(bridge_url) as orch:
484
+ result = await orch.scan_with_nuclei(url)
485
+ return await orch.wait_for_scan(result["scan_id"])
486
+
487
+
488
+ async def find_subdomains(
489
+ domain: str, bridge_url: str = "http://localhost:8080"
490
+ ) -> List[str]:
491
+ """Enumerate subdomains with Amass"""
492
+ async with ToolOrchestrator(bridge_url) as orch:
493
+ result = await orch.enumerate_subdomains(domain, active=False)
494
+ status = await orch.wait_for_scan(result["scan_id"])
495
+
496
+ # Parse results
497
+ results = await orch.get_scan_results(result["scan_id"])
498
+ return results.get("results", [])