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
core/rate_limiter.py ADDED
@@ -0,0 +1,405 @@
1
+ """
2
+ Rate Limiting & Circuit Breaker
3
+ Prevents API abuse and handles backend failures gracefully
4
+ """
5
+
6
+ import asyncio
7
+ import hashlib
8
+ import logging
9
+ import random
10
+ import time
11
+ from dataclasses import dataclass, field
12
+ from enum import Enum
13
+ from functools import wraps
14
+ from typing import Any, Callable, Dict, Optional
15
+
16
+ try:
17
+ import aioredis
18
+
19
+ REDIS_AVAILABLE = True
20
+ except ImportError:
21
+ REDIS_AVAILABLE = False
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ class CircuitState(Enum):
27
+ CLOSED = "closed" # Normal operation
28
+ OPEN = "open" # Failing, reject requests
29
+ HALF_OPEN = "half_open" # Testing if service recovered
30
+
31
+
32
+ @dataclass
33
+ class RateLimitConfig:
34
+ """Rate limiting configuration"""
35
+
36
+ requests_per_second: float = 1.0
37
+ burst_size: int = 5
38
+ max_retries: int = 3
39
+ base_delay: float = 1.0
40
+ max_delay: float = 60.0
41
+ exponential_base: float = 2.0
42
+
43
+
44
+ @dataclass
45
+ class CircuitBreakerConfig:
46
+ """Circuit breaker configuration"""
47
+
48
+ failure_threshold: int = 5
49
+ recovery_timeout: float = 30.0
50
+ half_open_max_calls: int = 3
51
+
52
+
53
+ class TokenBucket:
54
+ """
55
+ Token bucket rate limiter.
56
+ Allows burst traffic while maintaining average rate.
57
+ """
58
+
59
+ def __init__(self, rate: float, capacity: int):
60
+ self.rate = rate
61
+ self.capacity = capacity
62
+ self.tokens = capacity
63
+ self.last_update = time.monotonic()
64
+ self._lock = asyncio.Lock()
65
+
66
+ async def acquire(self, tokens: int = 1) -> float:
67
+ """
68
+ Acquire tokens. Returns wait time if not enough tokens.
69
+ """
70
+ async with self._lock:
71
+ now = time.monotonic()
72
+ elapsed = now - self.last_update
73
+
74
+ # Add tokens based on elapsed time
75
+ self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
76
+ self.last_update = now
77
+
78
+ if self.tokens >= tokens:
79
+ self.tokens -= tokens
80
+ return 0.0
81
+
82
+ # Calculate wait time
83
+ needed = tokens - self.tokens
84
+ wait_time = needed / self.rate
85
+
86
+ return wait_time
87
+
88
+ async def wait(self, tokens: int = 1):
89
+ """Wait until tokens are available"""
90
+ wait_time = await self.acquire(tokens)
91
+ if wait_time > 0:
92
+ await asyncio.sleep(wait_time)
93
+ async with self._lock:
94
+ self.tokens -= tokens
95
+
96
+
97
+ class ExponentialBackoff:
98
+ """
99
+ Exponential backoff with jitter for retries.
100
+ """
101
+
102
+ def __init__(
103
+ self,
104
+ base_delay: float = 1.0,
105
+ max_delay: float = 60.0,
106
+ exponential_base: float = 2.0,
107
+ jitter: bool = True,
108
+ ):
109
+ self.base_delay = base_delay
110
+ self.max_delay = max_delay
111
+ self.exponential_base = exponential_base
112
+ self.jitter = jitter
113
+ self.attempt = 0
114
+
115
+ def next_delay(self) -> float:
116
+ """Calculate next delay with exponential backoff"""
117
+ delay = min(
118
+ self.base_delay * (self.exponential_base**self.attempt), self.max_delay
119
+ )
120
+
121
+ if self.jitter:
122
+ # Add random jitter (±25%)
123
+ delay *= random.uniform(0.75, 1.25)
124
+
125
+ self.attempt += 1
126
+ return delay
127
+
128
+ def reset(self):
129
+ """Reset attempt counter"""
130
+ self.attempt = 0
131
+
132
+
133
+ class CircuitBreaker:
134
+ """
135
+ Circuit breaker pattern implementation.
136
+ Prevents cascading failures when backends are down.
137
+ """
138
+
139
+ def __init__(self, name: str, config: CircuitBreakerConfig = None):
140
+ self.name = name
141
+ self.config = config or CircuitBreakerConfig()
142
+ self.state = CircuitState.CLOSED
143
+ self.failures = 0
144
+ self.last_failure_time: Optional[float] = None
145
+ self.half_open_calls = 0
146
+ self._lock = asyncio.Lock()
147
+
148
+ async def call(self, func: Callable, *args, **kwargs) -> Any:
149
+ """
150
+ Execute function with circuit breaker protection.
151
+ """
152
+ async with self._lock:
153
+ if self.state == CircuitState.OPEN:
154
+ if self._should_attempt_reset():
155
+ self.state = CircuitState.HALF_OPEN
156
+ self.half_open_calls = 0
157
+ logger.info(f"Circuit {self.name}: Entering HALF_OPEN state")
158
+ else:
159
+ raise CircuitBreakerOpen(f"Circuit {self.name} is OPEN")
160
+
161
+ if self.state == CircuitState.HALF_OPEN:
162
+ if self.half_open_calls >= self.config.half_open_max_calls:
163
+ raise CircuitBreakerOpen(
164
+ f"Circuit {self.name} HALF_OPEN limit reached"
165
+ )
166
+ self.half_open_calls += 1
167
+
168
+ # Execute outside lock
169
+ try:
170
+ result = await func(*args, **kwargs)
171
+ await self._on_success()
172
+ return result
173
+ except Exception as e:
174
+ await self._on_failure()
175
+ raise
176
+
177
+ def _should_attempt_reset(self) -> bool:
178
+ """Check if enough time passed to try recovery"""
179
+ if self.last_failure_time is None:
180
+ return True
181
+ return (
182
+ time.monotonic() - self.last_failure_time
183
+ ) >= self.config.recovery_timeout
184
+
185
+ async def _on_success(self):
186
+ """Handle successful call"""
187
+ async with self._lock:
188
+ if self.state == CircuitState.HALF_OPEN:
189
+ self.state = CircuitState.CLOSED
190
+ self.failures = 0
191
+ self.half_open_calls = 0
192
+ logger.info(f"Circuit {self.name}: CLOSED (recovered)")
193
+
194
+ async def _on_failure(self):
195
+ """Handle failed call"""
196
+ async with self._lock:
197
+ self.failures += 1
198
+ self.last_failure_time = time.monotonic()
199
+
200
+ if self.state == CircuitState.HALF_OPEN:
201
+ self.state = CircuitState.OPEN
202
+ logger.warning(f"Circuit {self.name}: OPEN (recovery failed)")
203
+ elif self.failures >= self.config.failure_threshold:
204
+ self.state = CircuitState.OPEN
205
+ logger.warning(f"Circuit {self.name}: OPEN ({self.failures} failures)")
206
+
207
+ def get_state(self) -> CircuitState:
208
+ """Get current circuit state"""
209
+ return self.state
210
+
211
+ async def force_reset(self):
212
+ """Force circuit to CLOSED state"""
213
+ async with self._lock:
214
+ self.state = CircuitState.CLOSED
215
+ self.failures = 0
216
+ self.half_open_calls = 0
217
+ logger.info(f"Circuit {self.name}: Force reset to CLOSED")
218
+
219
+
220
+ class CircuitBreakerOpen(Exception):
221
+ """Exception raised when circuit breaker is open"""
222
+
223
+ pass
224
+
225
+
226
+ class RateLimitedClient:
227
+ """
228
+ HTTP client with rate limiting, retries, and circuit breaker.
229
+ """
230
+
231
+ def __init__(
232
+ self,
233
+ name: str,
234
+ rate_config: RateLimitConfig = None,
235
+ circuit_config: CircuitBreakerConfig = None,
236
+ ):
237
+ self.name = name
238
+ self.rate_config = rate_config or RateLimitConfig()
239
+ self.circuit = CircuitBreaker(name, circuit_config)
240
+
241
+ # Token bucket for rate limiting
242
+ self.bucket = TokenBucket(
243
+ rate=self.rate_config.requests_per_second,
244
+ capacity=self.rate_config.burst_size,
245
+ )
246
+
247
+ self._session: Optional[Any] = None
248
+
249
+ async def request(
250
+ self, method: str, url: str, retries: int = None, **kwargs
251
+ ) -> Any:
252
+ """
253
+ Make rate-limited request with circuit breaker.
254
+ """
255
+ retries = retries or self.rate_config.max_retries
256
+ backoff = ExponentialBackoff(
257
+ base_delay=self.rate_config.base_delay, max_delay=self.rate_config.max_delay
258
+ )
259
+
260
+ last_error = None
261
+
262
+ for attempt in range(retries + 1):
263
+ # Wait for rate limit
264
+ await self.bucket.wait()
265
+
266
+ try:
267
+ return await self.circuit.call(self._do_request, method, url, **kwargs)
268
+ except CircuitBreakerOpen:
269
+ raise
270
+ except Exception as e:
271
+ last_error = e
272
+ logger.warning(
273
+ f"{self.name} request failed (attempt {attempt + 1}): {e}"
274
+ )
275
+
276
+ if attempt < retries:
277
+ delay = backoff.next_delay()
278
+ logger.info(f"Retrying in {delay:.2f}s...")
279
+ await asyncio.sleep(delay)
280
+
281
+ raise last_error or Exception("Max retries exceeded")
282
+
283
+ async def _do_request(self, method: str, url: str, **kwargs) -> Any:
284
+ """Actual request implementation - override in subclass"""
285
+ raise NotImplementedError("Subclasses must implement _do_request")
286
+
287
+ async def close(self):
288
+ """Close client resources"""
289
+ pass
290
+
291
+
292
+ class SmartRouter:
293
+ """
294
+ Intelligent backend routing with health checks and circuit breakers.
295
+ """
296
+
297
+ def __init__(self):
298
+ self.backends: Dict[str, Any] = {}
299
+ self.circuit_breakers: Dict[str, CircuitBreaker] = {}
300
+ self.health_status: Dict[str, bool] = {}
301
+ self._lock = asyncio.Lock()
302
+
303
+ def register_backend(
304
+ self,
305
+ name: str,
306
+ backend: Any,
307
+ priority: int = 0,
308
+ circuit_config: CircuitBreakerConfig = None,
309
+ ):
310
+ """Register a backend with circuit breaker"""
311
+ self.backends[name] = {"instance": backend, "priority": priority, "name": name}
312
+ self.circuit_breakers[name] = CircuitBreaker(name, circuit_config)
313
+ self.health_status[name] = True
314
+
315
+ async def get_healthy_backend(self, min_priority: int = 0) -> Optional[Any]:
316
+ """
317
+ Get a healthy backend based on priority and circuit state.
318
+ """
319
+ # Sort by priority (highest first)
320
+ candidates = sorted(
321
+ self.backends.values(), key=lambda b: b["priority"], reverse=True
322
+ )
323
+
324
+ for backend_info in candidates:
325
+ name = backend_info["name"]
326
+
327
+ # Check circuit breaker state
328
+ circuit = self.circuit_breakers[name]
329
+ if circuit.state == CircuitState.OPEN:
330
+ continue
331
+
332
+ # Check health status
333
+ if await self._is_healthy(name):
334
+ return backend_info["instance"]
335
+
336
+ # Fallback: try any backend even if circuit is half-open
337
+ for backend_info in candidates:
338
+ name = backend_info["name"]
339
+ circuit = self.circuit_breakers[name]
340
+ if circuit.state == CircuitState.HALF_OPEN:
341
+ return backend_info["instance"]
342
+
343
+ return None
344
+
345
+ async def _is_healthy(self, name: str) -> bool:
346
+ """Check if backend is healthy"""
347
+ # Could implement actual health check here
348
+ return self.health_status.get(name, False)
349
+
350
+ async def execute(self, name: str, func: Callable, *args, **kwargs) -> Any:
351
+ """Execute function with circuit breaker protection"""
352
+ if name not in self.circuit_breakers:
353
+ raise ValueError(f"Unknown backend: {name}")
354
+
355
+ circuit = self.circuit_breakers[name]
356
+ return await circuit.call(func, *args, **kwargs)
357
+
358
+ def get_status(self) -> Dict[str, Any]:
359
+ """Get status of all backends"""
360
+ return {
361
+ name: {
362
+ "circuit_state": cb.state.value,
363
+ "failures": cb.failures,
364
+ "healthy": self.health_status.get(name, False),
365
+ }
366
+ for name, cb in self.circuit_breakers.items()
367
+ }
368
+
369
+
370
+ def rate_limited(
371
+ requests_per_second: float = 1.0, burst_size: int = 5, max_retries: int = 3
372
+ ):
373
+ """
374
+ Decorator for rate limiting async functions.
375
+ """
376
+ bucket = TokenBucket(rate=requests_per_second, capacity=burst_size)
377
+
378
+ def decorator(func: Callable) -> Callable:
379
+ @wraps(func)
380
+ async def wrapper(*args, **kwargs):
381
+ await bucket.wait()
382
+
383
+ backoff = ExponentialBackoff()
384
+ last_error = None
385
+
386
+ for attempt in range(max_retries + 1):
387
+ try:
388
+ return await func(*args, **kwargs)
389
+ except Exception as e:
390
+ last_error = e
391
+ if attempt < max_retries:
392
+ delay = backoff.next_delay()
393
+ await asyncio.sleep(delay)
394
+
395
+ raise last_error
396
+
397
+ return wrapper
398
+
399
+ return decorator
400
+
401
+
402
+ def cached_key(*args, **kwargs) -> str:
403
+ """Generate cache key from function arguments"""
404
+ key_data = str(args) + str(sorted(kwargs.items()))
405
+ return hashlib.md5(key_data.encode()).hexdigest()