zen-ai-pentest 2.0.4__py3-none-any.whl → 2.1.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.
- api/main.py +14 -2
- modules/__init__.py +2 -2
- modules/siem_integration.py +501 -0
- risk_engine/__init__.py +3 -1
- {zen_ai_pentest-2.0.4.dist-info → zen_ai_pentest-2.1.0.dist-info}/METADATA +46 -3
- {zen_ai_pentest-2.0.4.dist-info → zen_ai_pentest-2.1.0.dist-info}/RECORD +10 -9
- {zen_ai_pentest-2.0.4.dist-info → zen_ai_pentest-2.1.0.dist-info}/WHEEL +0 -0
- {zen_ai_pentest-2.0.4.dist-info → zen_ai_pentest-2.1.0.dist-info}/entry_points.txt +0 -0
- {zen_ai_pentest-2.0.4.dist-info → zen_ai_pentest-2.1.0.dist-info}/licenses/LICENSE +0 -0
- {zen_ai_pentest-2.0.4.dist-info → zen_ai_pentest-2.1.0.dist-info}/top_level.txt +0 -0
api/main.py
CHANGED
|
@@ -60,7 +60,7 @@ async def lifespan(app: FastAPI):
|
|
|
60
60
|
app = FastAPI(
|
|
61
61
|
title="Zen-AI-Pentest API",
|
|
62
62
|
description="Professional Pentesting Framework API",
|
|
63
|
-
version="2.
|
|
63
|
+
version="2.1.0",
|
|
64
64
|
lifespan=lifespan
|
|
65
65
|
)
|
|
66
66
|
|
|
@@ -500,7 +500,7 @@ async def health_check():
|
|
|
500
500
|
"""Health check endpoint"""
|
|
501
501
|
return {
|
|
502
502
|
"status": "healthy",
|
|
503
|
-
"version": "2.
|
|
503
|
+
"version": "2.1.0",
|
|
504
504
|
"timestamp": datetime.utcnow().isoformat()
|
|
505
505
|
}
|
|
506
506
|
|
|
@@ -1022,6 +1022,18 @@ async def create_jira_ticket(
|
|
|
1022
1022
|
# Import models for reports
|
|
1023
1023
|
from database.models import Report
|
|
1024
1024
|
|
|
1025
|
+
# ============================================================================
|
|
1026
|
+
# API v1.0 (Q1 2026)
|
|
1027
|
+
# ============================================================================
|
|
1028
|
+
|
|
1029
|
+
# Import v1 router
|
|
1030
|
+
try:
|
|
1031
|
+
from api.v1.siem import router as siem_v1_router
|
|
1032
|
+
app.include_router(siem_v1_router, prefix="/api/v1/siem", tags=["SIEM v1.0"])
|
|
1033
|
+
logger.info("API v1.0 SIEM endpoints loaded")
|
|
1034
|
+
except ImportError as e:
|
|
1035
|
+
logger.warning(f"Could not load API v1.0 endpoints: {e}")
|
|
1036
|
+
|
|
1025
1037
|
if __name__ == "__main__":
|
|
1026
1038
|
import uvicorn
|
|
1027
1039
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
modules/__init__.py
CHANGED
|
@@ -10,7 +10,7 @@ from .osint import (DomainInfo, EmailProfile, OSINTModule, OSINTResult,
|
|
|
10
10
|
from .protonvpn import (ProtonVPNManager, VPNProtocol, VPNSecurityLevel,
|
|
11
11
|
VPNServer, VPNStatus, quick_connect, secure_connect)
|
|
12
12
|
from .recon import ReconModule
|
|
13
|
-
from .report_gen import ReportGenerator
|
|
13
|
+
# from .report_gen import ReportGenerator # Module not available
|
|
14
14
|
from .sql_injection_db import (DBType, SQLInjectionDatabase, SQLITechnique,
|
|
15
15
|
SQLPayload)
|
|
16
16
|
from .vuln_scanner import VulnScannerModule
|
|
@@ -19,7 +19,7 @@ __all__ = [
|
|
|
19
19
|
"ReconModule",
|
|
20
20
|
"VulnScannerModule",
|
|
21
21
|
"ExploitAssistModule",
|
|
22
|
-
"ReportGenerator",
|
|
22
|
+
# "ReportGenerator", # Module not available
|
|
23
23
|
"NucleiIntegration",
|
|
24
24
|
"NucleiTemplateManager",
|
|
25
25
|
"SQLInjectionDatabase",
|
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SIEM Integration Module for Zen AI Pentest
|
|
3
|
+
Supports: Splunk, Elastic, Azure Sentinel, IBM QRadar
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from typing import Dict, List, Optional, Any
|
|
12
|
+
import requests
|
|
13
|
+
from urllib.parse import urljoin
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class SIEMConfig:
|
|
20
|
+
"""SIEM connection configuration"""
|
|
21
|
+
name: str
|
|
22
|
+
type: str # splunk, elastic, sentinel, qradar
|
|
23
|
+
url: str
|
|
24
|
+
api_key: Optional[str] = None
|
|
25
|
+
username: Optional[str] = None
|
|
26
|
+
password: Optional[str] = None
|
|
27
|
+
index: Optional[str] = None
|
|
28
|
+
verify_ssl: bool = True
|
|
29
|
+
timeout: int = 30
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class SecurityEvent:
|
|
34
|
+
"""Normalized security event for SIEM ingestion"""
|
|
35
|
+
timestamp: datetime
|
|
36
|
+
severity: str # critical, high, medium, low, info
|
|
37
|
+
event_type: str
|
|
38
|
+
source: str
|
|
39
|
+
target: str
|
|
40
|
+
description: str
|
|
41
|
+
cve_id: Optional[str] = None
|
|
42
|
+
cvss_score: Optional[float] = None
|
|
43
|
+
raw_data: Optional[Dict] = None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class BaseSIEMConnector(ABC):
|
|
47
|
+
"""Base class for SIEM connectors"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, config: SIEMConfig):
|
|
50
|
+
self.config = config
|
|
51
|
+
self.session = requests.Session()
|
|
52
|
+
self.session.verify = config.verify_ssl
|
|
53
|
+
self.session.timeout = config.timeout
|
|
54
|
+
|
|
55
|
+
@abstractmethod
|
|
56
|
+
def connect(self) -> bool:
|
|
57
|
+
"""Test connection to SIEM"""
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
@abstractmethod
|
|
61
|
+
def send_event(self, event: SecurityEvent) -> bool:
|
|
62
|
+
"""Send security event to SIEM"""
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def send_events_batch(self, events: List[SecurityEvent]) -> bool:
|
|
67
|
+
"""Send multiple events to SIEM"""
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
@abstractmethod
|
|
71
|
+
def query_threat_intel(self, indicator: str) -> Optional[Dict]:
|
|
72
|
+
"""Query SIEM for threat intelligence"""
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class SplunkConnector(BaseSIEMConnector):
|
|
77
|
+
"""Splunk HTTP Event Collector (HEC) connector"""
|
|
78
|
+
|
|
79
|
+
def connect(self) -> bool:
|
|
80
|
+
try:
|
|
81
|
+
url = urljoin(self.config.url, "/services/collector/health")
|
|
82
|
+
headers = {"Authorization": f"Splunk {self.config.api_key}"}
|
|
83
|
+
response = self.session.get(url, headers=headers)
|
|
84
|
+
return response.status_code == 200
|
|
85
|
+
except Exception as e:
|
|
86
|
+
logger.error(f"Splunk connection failed: {e}")
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
def send_event(self, event: SecurityEvent) -> bool:
|
|
90
|
+
try:
|
|
91
|
+
url = urljoin(self.config.url, "/services/collector/event")
|
|
92
|
+
headers = {
|
|
93
|
+
"Authorization": f"Splunk {self.config.api_key}",
|
|
94
|
+
"Content-Type": "application/json"
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
payload = {
|
|
98
|
+
"time": event.timestamp.timestamp(),
|
|
99
|
+
"sourcetype": "zen_ai_pentest",
|
|
100
|
+
"index": self.config.index or "security",
|
|
101
|
+
"event": {
|
|
102
|
+
"severity": event.severity,
|
|
103
|
+
"event_type": event.event_type,
|
|
104
|
+
"source": event.source,
|
|
105
|
+
"target": event.target,
|
|
106
|
+
"description": event.description,
|
|
107
|
+
"cve_id": event.cve_id,
|
|
108
|
+
"cvss_score": event.cvss_score,
|
|
109
|
+
**(event.raw_data or {})
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
response = self.session.post(url, headers=headers, json=payload)
|
|
114
|
+
return response.status_code == 200
|
|
115
|
+
except Exception as e:
|
|
116
|
+
logger.error(f"Failed to send event to Splunk: {e}")
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
def send_events_batch(self, events: List[SecurityEvent]) -> bool:
|
|
120
|
+
"""Send multiple events to Splunk"""
|
|
121
|
+
try:
|
|
122
|
+
url = urljoin(self.config.url, "/services/collector/event")
|
|
123
|
+
headers = {
|
|
124
|
+
"Authorization": f"Splunk {self.config.api_key}",
|
|
125
|
+
"Content-Type": "application/json"
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
batch_data = ""
|
|
129
|
+
for event in events:
|
|
130
|
+
payload = {
|
|
131
|
+
"time": event.timestamp.timestamp(),
|
|
132
|
+
"sourcetype": "zen_ai_pentest",
|
|
133
|
+
"index": self.config.index or "security",
|
|
134
|
+
"event": {
|
|
135
|
+
"severity": event.severity,
|
|
136
|
+
"event_type": event.event_type,
|
|
137
|
+
"source": event.source,
|
|
138
|
+
"target": event.target,
|
|
139
|
+
"description": event.description,
|
|
140
|
+
"cve_id": event.cve_id,
|
|
141
|
+
"cvss_score": event.cvss_score,
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
batch_data += json.dumps(payload) + "\n"
|
|
145
|
+
|
|
146
|
+
response = self.session.post(url, headers=headers, data=batch_data)
|
|
147
|
+
return response.status_code == 200
|
|
148
|
+
except Exception as e:
|
|
149
|
+
logger.error(f"Failed to send batch to Splunk: {e}")
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
def query_threat_intel(self, indicator: str) -> Optional[Dict]:
|
|
153
|
+
"""Query Splunk for threat intelligence"""
|
|
154
|
+
try:
|
|
155
|
+
search_query = f'search index=threat_intel indicator="{indicator}" | head 1'
|
|
156
|
+
url = urljoin(self.config.url, "/services/search/jobs")
|
|
157
|
+
headers = {"Authorization": f"Splunk {self.config.api_key}"}
|
|
158
|
+
|
|
159
|
+
data = {"search": search_query, "output_mode": "json"}
|
|
160
|
+
response = self.session.post(url, headers=headers, data=data)
|
|
161
|
+
|
|
162
|
+
if response.status_code == 200:
|
|
163
|
+
return response.json()
|
|
164
|
+
return None
|
|
165
|
+
except Exception as e:
|
|
166
|
+
logger.error(f"Failed to query Splunk: {e}")
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class ElasticConnector(BaseSIEMConnector):
|
|
171
|
+
"""Elastic/Elasticsearch connector"""
|
|
172
|
+
|
|
173
|
+
def connect(self) -> bool:
|
|
174
|
+
try:
|
|
175
|
+
url = urljoin(self.config.url, "_cluster/health")
|
|
176
|
+
headers = {"Authorization": f"ApiKey {self.config.api_key}"}
|
|
177
|
+
response = self.session.get(url, headers=headers)
|
|
178
|
+
return response.status_code == 200
|
|
179
|
+
except Exception as e:
|
|
180
|
+
logger.error(f"Elastic connection failed: {e}")
|
|
181
|
+
return False
|
|
182
|
+
|
|
183
|
+
def send_event(self, event: SecurityEvent) -> bool:
|
|
184
|
+
try:
|
|
185
|
+
index = self.config.index or "zen-ai-security"
|
|
186
|
+
url = urljoin(self.config.url, f"{index}/_doc")
|
|
187
|
+
headers = {"Authorization": f"ApiKey {self.config.api_key}"}
|
|
188
|
+
|
|
189
|
+
document = {
|
|
190
|
+
"@timestamp": event.timestamp.isoformat(),
|
|
191
|
+
"severity": event.severity,
|
|
192
|
+
"event.type": event.event_type,
|
|
193
|
+
"source.ip": event.source,
|
|
194
|
+
"destination.ip": event.target,
|
|
195
|
+
"message": event.description,
|
|
196
|
+
"vulnerability.cve": event.cve_id,
|
|
197
|
+
"vulnerability.cvss": event.cvss_score,
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
response = self.session.post(url, headers=headers, json=document)
|
|
201
|
+
return response.status_code in [200, 201]
|
|
202
|
+
except Exception as e:
|
|
203
|
+
logger.error(f"Failed to send event to Elastic: {e}")
|
|
204
|
+
return False
|
|
205
|
+
|
|
206
|
+
def send_events_batch(self, events: List[SecurityEvent]) -> bool:
|
|
207
|
+
"""Send bulk events to Elastic"""
|
|
208
|
+
try:
|
|
209
|
+
index = self.config.index or "zen-ai-security"
|
|
210
|
+
url = urljoin(self.config.url, "_bulk")
|
|
211
|
+
headers = {"Authorization": f"ApiKey {self.config.api_key}"}
|
|
212
|
+
|
|
213
|
+
bulk_data = ""
|
|
214
|
+
for event in events:
|
|
215
|
+
action = json.dumps({"index": {"_index": index}})
|
|
216
|
+
document = json.dumps({
|
|
217
|
+
"@timestamp": event.timestamp.isoformat(),
|
|
218
|
+
"severity": event.severity,
|
|
219
|
+
"event.type": event.event_type,
|
|
220
|
+
"source.ip": event.source,
|
|
221
|
+
"destination.ip": event.target,
|
|
222
|
+
"message": event.description,
|
|
223
|
+
"vulnerability.cve": event.cve_id,
|
|
224
|
+
"vulnerability.cvss": event.cvss_score,
|
|
225
|
+
})
|
|
226
|
+
bulk_data += action + "\n" + document + "\n"
|
|
227
|
+
|
|
228
|
+
response = self.session.post(
|
|
229
|
+
url,
|
|
230
|
+
headers={**headers, "Content-Type": "application/x-ndjson"},
|
|
231
|
+
data=bulk_data
|
|
232
|
+
)
|
|
233
|
+
return response.status_code == 200
|
|
234
|
+
except Exception as e:
|
|
235
|
+
logger.error(f"Failed to send bulk to Elastic: {e}")
|
|
236
|
+
return False
|
|
237
|
+
|
|
238
|
+
def query_threat_intel(self, indicator: str) -> Optional[Dict]:
|
|
239
|
+
"""Query Elastic for threat intelligence"""
|
|
240
|
+
try:
|
|
241
|
+
index = "threat-intel"
|
|
242
|
+
url = urljoin(self.config.url, f"{index}/_search")
|
|
243
|
+
headers = {"Authorization": f"ApiKey {self.config.api_key}"}
|
|
244
|
+
|
|
245
|
+
query = {
|
|
246
|
+
"query": {
|
|
247
|
+
"match": {"indicator": indicator}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
response = self.session.post(url, headers=headers, json=query)
|
|
252
|
+
if response.status_code == 200:
|
|
253
|
+
return response.json()
|
|
254
|
+
return None
|
|
255
|
+
except Exception as e:
|
|
256
|
+
logger.error(f"Failed to query Elastic: {e}")
|
|
257
|
+
return None
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class AzureSentinelConnector(BaseSIEMConnector):
|
|
261
|
+
"""Azure Sentinel/Log Analytics connector"""
|
|
262
|
+
|
|
263
|
+
def __init__(self, config: SIEMConfig):
|
|
264
|
+
super().__init__(config)
|
|
265
|
+
self.workspace_id = config.username # Workspace ID
|
|
266
|
+
self.shared_key = config.api_key # Shared Key
|
|
267
|
+
|
|
268
|
+
def connect(self) -> bool:
|
|
269
|
+
try:
|
|
270
|
+
# Test connection by querying Log Analytics
|
|
271
|
+
url = f"https://api.loganalytics.io/v1/workspaces/{self.workspace_id}/query"
|
|
272
|
+
headers = self._build_auth_header()
|
|
273
|
+
|
|
274
|
+
response = self.session.get(url, headers=headers)
|
|
275
|
+
return response.status_code in [200, 401] # 401 means auth works but query invalid
|
|
276
|
+
except Exception as e:
|
|
277
|
+
logger.error(f"Sentinel connection failed: {e}")
|
|
278
|
+
return False
|
|
279
|
+
|
|
280
|
+
def _build_auth_header(self) -> Dict[str, str]:
|
|
281
|
+
"""Build Azure API authentication header"""
|
|
282
|
+
import hmac
|
|
283
|
+
import hashlib
|
|
284
|
+
import base64
|
|
285
|
+
|
|
286
|
+
date = datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
|
|
287
|
+
string_to_hash = f"POST\n{len('')}\napplication/json\nx-ms-date:{date}\n/api/logs"
|
|
288
|
+
decoded_key = base64.b64decode(self.shared_key)
|
|
289
|
+
encoded_hash = base64.b64encode(
|
|
290
|
+
hmac.new(decoded_key, string_to_hash.encode(), hashlib.sha256).digest()
|
|
291
|
+
).decode()
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
"Content-Type": "application/json",
|
|
295
|
+
"Authorization": f"SharedKey {self.workspace_id}:{encoded_hash}",
|
|
296
|
+
"x-ms-date": date,
|
|
297
|
+
"Log-Type": "ZenAIPentest"
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
def send_event(self, event: SecurityEvent) -> bool:
|
|
301
|
+
try:
|
|
302
|
+
url = f"https://{self.workspace_id}.ods.opinsights.azure.com/api/logs?api-version=2016-04-01"
|
|
303
|
+
headers = self._build_auth_header()
|
|
304
|
+
|
|
305
|
+
log_data = [{
|
|
306
|
+
"TimeGenerated": event.timestamp.isoformat(),
|
|
307
|
+
"Severity": event.severity,
|
|
308
|
+
"EventType": event.event_type,
|
|
309
|
+
"Source": event.source,
|
|
310
|
+
"Target": event.target,
|
|
311
|
+
"Description": event.description,
|
|
312
|
+
"CVE": event.cve_id,
|
|
313
|
+
"CVSS": event.cvss_score
|
|
314
|
+
}]
|
|
315
|
+
|
|
316
|
+
response = self.session.post(url, headers=headers, json=log_data)
|
|
317
|
+
return response.status_code == 200
|
|
318
|
+
except Exception as e:
|
|
319
|
+
logger.error(f"Failed to send event to Sentinel: {e}")
|
|
320
|
+
return False
|
|
321
|
+
|
|
322
|
+
def send_events_batch(self, events: List[SecurityEvent]) -> bool:
|
|
323
|
+
"""Send batch to Azure Log Analytics"""
|
|
324
|
+
try:
|
|
325
|
+
url = f"https://{self.workspace_id}.ods.opinsights.azure.com/api/logs?api-version=2016-04-01"
|
|
326
|
+
headers = self._build_auth_header()
|
|
327
|
+
|
|
328
|
+
log_data = []
|
|
329
|
+
for event in events:
|
|
330
|
+
log_data.append({
|
|
331
|
+
"TimeGenerated": event.timestamp.isoformat(),
|
|
332
|
+
"Severity": event.severity,
|
|
333
|
+
"EventType": event.event_type,
|
|
334
|
+
"Source": event.source,
|
|
335
|
+
"Target": event.target,
|
|
336
|
+
"Description": event.description,
|
|
337
|
+
"CVE": event.cve_id,
|
|
338
|
+
"CVSS": event.cvss_score
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
response = self.session.post(url, headers=headers, json=log_data)
|
|
342
|
+
return response.status_code == 200
|
|
343
|
+
except Exception as e:
|
|
344
|
+
logger.error(f"Failed to send batch to Sentinel: {e}")
|
|
345
|
+
return False
|
|
346
|
+
|
|
347
|
+
def query_threat_intel(self, indicator: str) -> Optional[Dict]:
|
|
348
|
+
"""Query Sentinel Threat Intelligence"""
|
|
349
|
+
try:
|
|
350
|
+
url = f"https://api.loganalytics.io/v1/workspaces/{self.workspace_id}/query"
|
|
351
|
+
headers = self._build_auth_header()
|
|
352
|
+
|
|
353
|
+
query = f"ThreatIntelligenceIndicator | where Indicator == '{indicator}' | limit 1"
|
|
354
|
+
|
|
355
|
+
response = self.session.post(
|
|
356
|
+
url,
|
|
357
|
+
headers=headers,
|
|
358
|
+
json={"query": query}
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
if response.status_code == 200:
|
|
362
|
+
return response.json()
|
|
363
|
+
return None
|
|
364
|
+
except Exception as e:
|
|
365
|
+
logger.error(f"Failed to query Sentinel: {e}")
|
|
366
|
+
return None
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
class QRadarConnector(BaseSIEMConnector):
|
|
370
|
+
"""IBM QRadar connector"""
|
|
371
|
+
|
|
372
|
+
def connect(self) -> bool:
|
|
373
|
+
try:
|
|
374
|
+
url = urljoin(self.config.url, "/api/system/about")
|
|
375
|
+
headers = self._build_auth_header()
|
|
376
|
+
response = self.session.get(url, headers=headers)
|
|
377
|
+
return response.status_code == 200
|
|
378
|
+
except Exception as e:
|
|
379
|
+
logger.error(f"QRadar connection failed: {e}")
|
|
380
|
+
return False
|
|
381
|
+
|
|
382
|
+
def _build_auth_header(self) -> Dict[str, str]:
|
|
383
|
+
"""Build QRadar authentication header"""
|
|
384
|
+
import base64
|
|
385
|
+
|
|
386
|
+
if self.config.api_key:
|
|
387
|
+
return {"SEC": self.config.api_key}
|
|
388
|
+
elif self.config.username and self.config.password:
|
|
389
|
+
credentials = base64.b64encode(
|
|
390
|
+
f"{self.config.username}:{self.config.password}".encode()
|
|
391
|
+
).decode()
|
|
392
|
+
return {"Authorization": f"Basic {credentials}"}
|
|
393
|
+
return {}
|
|
394
|
+
|
|
395
|
+
def send_event(self, event: SecurityEvent) -> bool:
|
|
396
|
+
try:
|
|
397
|
+
url = urljoin(self.config.url, "/api/asset_model/assets")
|
|
398
|
+
headers = self._build_auth_header()
|
|
399
|
+
|
|
400
|
+
payload = {
|
|
401
|
+
"event": {
|
|
402
|
+
"severity": event.severity,
|
|
403
|
+
"event_type": event.event_type,
|
|
404
|
+
"source": event.source,
|
|
405
|
+
"target": event.target,
|
|
406
|
+
"description": event.description,
|
|
407
|
+
"cve": event.cve_id
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
response = self.session.post(url, headers=headers, json=payload)
|
|
412
|
+
return response.status_code in [200, 201]
|
|
413
|
+
except Exception as e:
|
|
414
|
+
logger.error(f"Failed to send event to QRadar: {e}")
|
|
415
|
+
return False
|
|
416
|
+
|
|
417
|
+
def send_events_batch(self, events: List[SecurityEvent]) -> bool:
|
|
418
|
+
"""QRadar batch event submission"""
|
|
419
|
+
for event in events:
|
|
420
|
+
if not self.send_event(event):
|
|
421
|
+
return False
|
|
422
|
+
return True
|
|
423
|
+
|
|
424
|
+
def query_threat_intel(self, indicator: str) -> Optional[Dict]:
|
|
425
|
+
"""Query QRadar reference sets"""
|
|
426
|
+
try:
|
|
427
|
+
url = urljoin(self.config.url, f"/api/reference_data/sets/{indicator}")
|
|
428
|
+
headers = self._build_auth_header()
|
|
429
|
+
|
|
430
|
+
response = self.session.get(url, headers=headers)
|
|
431
|
+
if response.status_code == 200:
|
|
432
|
+
return response.json()
|
|
433
|
+
return None
|
|
434
|
+
except Exception as e:
|
|
435
|
+
logger.error(f"Failed to query QRadar: {e}")
|
|
436
|
+
return None
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
class SIEMIntegrationManager:
|
|
440
|
+
"""Manager for multiple SIEM integrations"""
|
|
441
|
+
|
|
442
|
+
def __init__(self):
|
|
443
|
+
self.connectors: Dict[str, BaseSIEMConnector] = {}
|
|
444
|
+
self._connector_classes = {
|
|
445
|
+
"splunk": SplunkConnector,
|
|
446
|
+
"elastic": ElasticConnector,
|
|
447
|
+
"sentinel": AzureSentinelConnector,
|
|
448
|
+
"qradar": QRadarConnector
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
def add_siem(self, config: SIEMConfig) -> bool:
|
|
452
|
+
"""Add SIEM integration"""
|
|
453
|
+
connector_class = self._connector_classes.get(config.type)
|
|
454
|
+
if not connector_class:
|
|
455
|
+
logger.error(f"Unknown SIEM type: {config.type}")
|
|
456
|
+
return False
|
|
457
|
+
|
|
458
|
+
connector = connector_class(config)
|
|
459
|
+
if connector.connect():
|
|
460
|
+
self.connectors[config.name] = connector
|
|
461
|
+
logger.info(f"Connected to {config.name} ({config.type})")
|
|
462
|
+
return True
|
|
463
|
+
else:
|
|
464
|
+
logger.error(f"Failed to connect to {config.name}")
|
|
465
|
+
return False
|
|
466
|
+
|
|
467
|
+
def send_to_all(self, event: SecurityEvent) -> Dict[str, bool]:
|
|
468
|
+
"""Send event to all configured SIEMs"""
|
|
469
|
+
results = {}
|
|
470
|
+
for name, connector in self.connectors.items():
|
|
471
|
+
results[name] = connector.send_event(event)
|
|
472
|
+
return results
|
|
473
|
+
|
|
474
|
+
def send_batch_to_all(self, events: List[SecurityEvent]) -> Dict[str, bool]:
|
|
475
|
+
"""Send batch of events to all SIEMs"""
|
|
476
|
+
results = {}
|
|
477
|
+
for name, connector in self.connectors.items():
|
|
478
|
+
results[name] = connector.send_events_batch(events)
|
|
479
|
+
return results
|
|
480
|
+
|
|
481
|
+
def get_status(self) -> Dict[str, bool]:
|
|
482
|
+
"""Get connection status of all SIEMs"""
|
|
483
|
+
return {name: connector.connect() for name, connector in self.connectors.items()}
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
# Factory function
|
|
487
|
+
def create_siem_connector(config: SIEMConfig) -> Optional[BaseSIEMConnector]:
|
|
488
|
+
"""Factory function to create appropriate SIEM connector"""
|
|
489
|
+
connectors = {
|
|
490
|
+
"splunk": SplunkConnector,
|
|
491
|
+
"elastic": ElasticConnector,
|
|
492
|
+
"sentinel": AzureSentinelConnector,
|
|
493
|
+
"qradar": QRadarConnector
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
connector_class = connectors.get(config.type.lower())
|
|
497
|
+
if connector_class:
|
|
498
|
+
return connector_class(config)
|
|
499
|
+
|
|
500
|
+
logger.error(f"Unknown SIEM type: {config.type}")
|
|
501
|
+
return None
|
risk_engine/__init__.py
CHANGED
|
@@ -58,7 +58,7 @@ from .business_impact_calculator import (
|
|
|
58
58
|
)
|
|
59
59
|
|
|
60
60
|
# Additional exports for backwards compatibility
|
|
61
|
-
from .scorer import RiskScorer
|
|
61
|
+
from .scorer import RiskScorer, RiskScore, SeverityLevel
|
|
62
62
|
from .cvss import CVSSCalculator
|
|
63
63
|
from .epss import EPSSClient
|
|
64
64
|
|
|
@@ -92,6 +92,8 @@ __all__ = [
|
|
|
92
92
|
|
|
93
93
|
# Additional scoring modules
|
|
94
94
|
"RiskScorer",
|
|
95
|
+
"RiskScore",
|
|
96
|
+
"SeverityLevel",
|
|
95
97
|
"CVSSCalculator",
|
|
96
98
|
"EPSSClient",
|
|
97
99
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zen-ai-pentest
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Advanced AI-Powered Penetration Testing Framework with Multi-Agent Orchestration
|
|
5
5
|
Home-page: https://github.com/SHAdd0WTAka/zen-ai-pentest
|
|
6
6
|
Author: SHAdd0WTAka
|
|
@@ -63,10 +63,53 @@ Dynamic: requires-python
|
|
|
63
63
|
[](docker/)
|
|
64
64
|
[](tests/)
|
|
65
65
|
[](https://pypi.org/project/zen-ai-pentest/)
|
|
66
|
-
[](https://github.com/SHAdd0WTAka/zen-ai-pentest/releases)
|
|
67
67
|
[](#-authors--team)
|
|
68
68
|
[](ROADMAP_2026.md)
|
|
69
|
-
|
|
69
|
+
[](docs/architecture.md)
|
|
70
|
+
```mermaid
|
|
71
|
+
graph TB
|
|
72
|
+
subgraph "User Interface"
|
|
73
|
+
CLI[CLI]
|
|
74
|
+
API[REST API]
|
|
75
|
+
WebUI[Web UI]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
subgraph "Core Engine"
|
|
79
|
+
Orchestrator[Agent Orchestrator]
|
|
80
|
+
StateMachine[State Machine]
|
|
81
|
+
RiskEngine[Risk Engine]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
subgraph "AI Agents"
|
|
85
|
+
Recon[Reconnaissance]
|
|
86
|
+
Vuln[Vulnerability]
|
|
87
|
+
Exploit[Exploit]
|
|
88
|
+
Report[Report]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
subgraph "Tools"
|
|
92
|
+
Nmap[Nmap]
|
|
93
|
+
SQLMap[SQLMap]
|
|
94
|
+
Metasploit[Metasploit]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
subgraph "External APIs"
|
|
98
|
+
OpenAI[OpenAI]
|
|
99
|
+
Anthropic[Anthropic]
|
|
100
|
+
ThreatIntel[Threat Intelligence]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
CLI --> API
|
|
104
|
+
WebUI --> API
|
|
105
|
+
API --> Orchestrator
|
|
106
|
+
Orchestrator --> StateMachine
|
|
107
|
+
StateMachine --> Recon
|
|
108
|
+
StateMachine --> Vuln
|
|
109
|
+
StateMachine --> Exploit
|
|
110
|
+
Exploit --> OpenAI
|
|
111
|
+
RiskEngine --> ThreatIntel
|
|
112
|
+
```
|
|
70
113
|
**Zen-AI-Pentest** is an autonomous, AI-powered penetration testing framework that combines cutting-edge language models with professional security tools. Built for security professionals, bug bounty hunters, and enterprise security teams.
|
|
71
114
|
|
|
72
115
|
---
|
|
@@ -12,7 +12,7 @@ agents/react_agent_vm.py,sha256=DF_TMWG_AfWbtvw9s4TB1MAwkFLMRaWpKw2UcM7Pa5U,1112
|
|
|
12
12
|
agents/research_agent.py,sha256=f_OjmEvgQX1mUdLRlsj3-QMyB1m5J0oHIsTvFkcRijc,6394
|
|
13
13
|
api/__init__.py,sha256=HQ2cFR8t_PlbtgLzir7P1Umzh-syS5iEfCNZky5uL_Y,256
|
|
14
14
|
api/auth.py,sha256=ZqIvj0L0BTAFsytjCznGF53i1kz6DOunhLxd0mPUykA,3934
|
|
15
|
-
api/main.py,sha256=
|
|
15
|
+
api/main.py,sha256=0sy31LpFZh6SvvGV1qwWV91QLbXMTuZz4o2CnjpWjf0,32801
|
|
16
16
|
api/schemas.py,sha256=nnE-97OOOWpAAenD_sCsCpcLy-SpWW1g2WW902mbihY,10306
|
|
17
17
|
api/websocket.py,sha256=DTDKr48g6RBNCBX6LM1WljR1FLTkoYB-BTm1TtMNaOY,3602
|
|
18
18
|
autonomous/__init__.py,sha256=Gv83jnjasidFfyD6ggCXjJlqjn6YwANLi_b0-kpBSok,2858
|
|
@@ -47,18 +47,19 @@ core/plugin_manager.py,sha256=3sG2DMiWbyjAaoydtyz2pXmdJ-G_-SWgCLGs-w_QoSA,19167
|
|
|
47
47
|
core/rate_limiter.py,sha256=poEYln6DGFs6C6n2rSnqRykax-YDi5NySk4LJnkgCpk,12423
|
|
48
48
|
core/secure_config.py,sha256=tTDVXKoLiGKdfCQkLorgEKw2qdVyPjwhaxwyuFlGD_8,10361
|
|
49
49
|
core/shield_integration.py,sha256=ktL_25WY0JBH5dBNvqcJLw42mQFkKr6ku5lkgH119Mw,9997
|
|
50
|
-
modules/__init__.py,sha256=
|
|
50
|
+
modules/__init__.py,sha256=eIfsYjpXtuo4cH4TQOIOrXLa6OXrP168ESPM3OuQYT0,1418
|
|
51
51
|
modules/cve_database.py,sha256=46ucXLdWjEUed4qh4wznn3oWEyoxWay2r3EVgVMmBrg,13137
|
|
52
52
|
modules/exploit_assist.py,sha256=xMcIYgwP6XSNuJDw8hI3FM1GsvvL9B3uMiYE8w-OBbY,10254
|
|
53
53
|
modules/nuclei_integration.py,sha256=2pvFCcTpEpC6M6YV2QWoHBxpKYswoYnD7dX4GJTQnX4,14814
|
|
54
54
|
modules/osint.py,sha256=SZG1rfMQ-zPIasYtKRzFlsjXR3wDSrVLRUtg6extUok,20600
|
|
55
55
|
modules/protonvpn.py,sha256=4G-tLL3JyjJm-ryQIt4-hQTX4eKX_OTZnKFkZaK_F8Q,18133
|
|
56
56
|
modules/recon.py,sha256=utS-wfdRBS7XaGtr1mcpMuetyJcCi2B8LZrAmVJjhcM,5002
|
|
57
|
+
modules/siem_integration.py,sha256=GbG8sbyziItdQhRArNku7pV6yZW8jR9AQgUxU2dvJ98,18594
|
|
57
58
|
modules/sql_injection_db.py,sha256=x9VVIaXkykXo0zFmcPqIMk9gh_CF-Lrs9Ht2hrb9DLk,28945
|
|
58
59
|
modules/tool_orchestrator.py,sha256=6FwR-EMR-HIrLFFimWf5MUMf_AYIYPUuZPaL8oTUNVU,15868
|
|
59
60
|
modules/vuln_scanner.py,sha256=L68uCM1YezXrnbqjhJlslVyQOlM9jejnZHWR8j0B1m8,8617
|
|
60
61
|
modules/wordlist_generator.py,sha256=3qAZseHDKI55x3gAnxKtwN1iMjXSJi1N0uMBnCnogV8,15429
|
|
61
|
-
risk_engine/__init__.py,sha256=
|
|
62
|
+
risk_engine/__init__.py,sha256=PPGuSptgnMfNM2-DtdgE-5h7CL_0W4FdgUFYqnTgLIw,2424
|
|
62
63
|
risk_engine/business_impact.py,sha256=gAjhNdMLdjx5iQkpPqxb6ugfRL6_1VCc6AIli95kS0E,8657
|
|
63
64
|
risk_engine/business_impact_calculator.py,sha256=9SgIXfhF-EFVwULyWepv1_Wcr_UOSg2E0oO2_VwwTWk,21222
|
|
64
65
|
risk_engine/cvss.py,sha256=iI8clKFSwi9w1xIW2Z9tCScUOmGSGWJrowA9jGWW86I,5397
|
|
@@ -67,9 +68,9 @@ risk_engine/example_usage.py,sha256=eM54wLDy3eZtiDGjOSZ4YrsCcEUzfVKjV_A_Pkn07is,
|
|
|
67
68
|
risk_engine/false_positive_engine.py,sha256=8u0wI3W25fsEq9eXfskUtcHfV3D2V6ElzVSgY_TAA-c,37961
|
|
68
69
|
risk_engine/scorer.py,sha256=BklUfMo26IaOYOJse-mjvY8NwjpbI4UEIlqZ6MhCmwU,10282
|
|
69
70
|
web_ui/backend/main.py,sha256=DTVmsvbDH7EvMmeohbcl8vYyKPyaLU8oHMvwWAqAF8k,15402
|
|
70
|
-
zen_ai_pentest-2.0.
|
|
71
|
-
zen_ai_pentest-2.0.
|
|
72
|
-
zen_ai_pentest-2.0.
|
|
73
|
-
zen_ai_pentest-2.0.
|
|
74
|
-
zen_ai_pentest-2.0.
|
|
75
|
-
zen_ai_pentest-2.0.
|
|
71
|
+
zen_ai_pentest-2.1.0.dist-info/licenses/LICENSE,sha256=C1sNTmgBbFuCm9vPCctuz6pQr4khkgqoQEMUE6cP-FY,1068
|
|
72
|
+
zen_ai_pentest-2.1.0.dist-info/METADATA,sha256=oq5UetxEhLY1rzImAO6AQvHxrEtu3T2PJDlS6lR6Qkw,30802
|
|
73
|
+
zen_ai_pentest-2.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
74
|
+
zen_ai_pentest-2.1.0.dist-info/entry_points.txt,sha256=qZRuz7yjZdEMiUrfflMiGzMUHUZWasT49-HNE-EKmTE,55
|
|
75
|
+
zen_ai_pentest-2.1.0.dist-info/top_level.txt,sha256=ExWeAuK-0xRpcWS7QsL8LEdPVJy1-Z-83qwOJ1-CERA,80
|
|
76
|
+
zen_ai_pentest-2.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|