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.
- agents/__init__.py +28 -0
- agents/agent_base.py +239 -0
- agents/agent_orchestrator.py +346 -0
- agents/analysis_agent.py +225 -0
- agents/cli.py +258 -0
- agents/exploit_agent.py +224 -0
- agents/integration.py +211 -0
- agents/post_scan_agent.py +937 -0
- agents/react_agent.py +384 -0
- agents/react_agent_enhanced.py +616 -0
- agents/react_agent_vm.py +298 -0
- agents/research_agent.py +176 -0
- api/__init__.py +11 -0
- api/auth.py +123 -0
- api/main.py +1027 -0
- api/schemas.py +357 -0
- api/websocket.py +97 -0
- autonomous/__init__.py +122 -0
- autonomous/agent.py +253 -0
- autonomous/agent_loop.py +1370 -0
- autonomous/exploit_validator.py +1537 -0
- autonomous/memory.py +448 -0
- autonomous/react.py +339 -0
- autonomous/tool_executor.py +488 -0
- backends/__init__.py +16 -0
- backends/chatgpt_direct.py +133 -0
- backends/claude_direct.py +130 -0
- backends/duckduckgo.py +138 -0
- backends/openrouter.py +120 -0
- benchmarks/__init__.py +149 -0
- benchmarks/benchmark_engine.py +904 -0
- benchmarks/ci_benchmark.py +785 -0
- benchmarks/comparison.py +729 -0
- benchmarks/metrics.py +553 -0
- benchmarks/run_benchmarks.py +809 -0
- ci_cd/__init__.py +2 -0
- core/__init__.py +17 -0
- core/async_pool.py +282 -0
- core/asyncio_fix.py +222 -0
- core/cache.py +472 -0
- core/container.py +277 -0
- core/database.py +114 -0
- core/input_validator.py +353 -0
- core/models.py +288 -0
- core/orchestrator.py +611 -0
- core/plugin_manager.py +571 -0
- core/rate_limiter.py +405 -0
- core/secure_config.py +328 -0
- core/shield_integration.py +296 -0
- modules/__init__.py +46 -0
- modules/cve_database.py +362 -0
- modules/exploit_assist.py +330 -0
- modules/nuclei_integration.py +480 -0
- modules/osint.py +604 -0
- modules/protonvpn.py +554 -0
- modules/recon.py +165 -0
- modules/sql_injection_db.py +826 -0
- modules/tool_orchestrator.py +498 -0
- modules/vuln_scanner.py +292 -0
- modules/wordlist_generator.py +566 -0
- risk_engine/__init__.py +99 -0
- risk_engine/business_impact.py +267 -0
- risk_engine/business_impact_calculator.py +563 -0
- risk_engine/cvss.py +156 -0
- risk_engine/epss.py +190 -0
- risk_engine/example_usage.py +294 -0
- risk_engine/false_positive_engine.py +1073 -0
- risk_engine/scorer.py +304 -0
- web_ui/backend/main.py +471 -0
- zen_ai_pentest-2.0.0.dist-info/METADATA +795 -0
- zen_ai_pentest-2.0.0.dist-info/RECORD +75 -0
- zen_ai_pentest-2.0.0.dist-info/WHEEL +5 -0
- zen_ai_pentest-2.0.0.dist-info/entry_points.txt +2 -0
- zen_ai_pentest-2.0.0.dist-info/licenses/LICENSE +21 -0
- zen_ai_pentest-2.0.0.dist-info/top_level.txt +10 -0
agents/react_agent_vm.py
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ReAct Agent mit VirtualBox VM-Integration
|
|
3
|
+
|
|
4
|
+
Erweiterung des ReAct Agents für isolierte VM-basierte Pentests.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List, TypedDict, Annotated, Literal
|
|
8
|
+
import logging
|
|
9
|
+
|
|
10
|
+
from langchain_core.tools import tool
|
|
11
|
+
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, ToolMessage, SystemMessage
|
|
12
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
13
|
+
from langgraph.graph import StateGraph, START, END, add_messages
|
|
14
|
+
from langgraph.checkpoint.memory import MemorySaver
|
|
15
|
+
|
|
16
|
+
from .react_agent import ReActAgent, ReActAgentConfig, AgentState
|
|
17
|
+
from ..virtualization.vm_manager import VirtualBoxManager, PentestSandbox
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class VMAgentConfig(ReActAgentConfig):
|
|
24
|
+
"""Erweiterte Konfiguration für VM-basierten Agent"""
|
|
25
|
+
use_vm: bool = True
|
|
26
|
+
vm_name: str = "kali-pentest"
|
|
27
|
+
vm_username: str = "kali"
|
|
28
|
+
vm_password: str = "kali"
|
|
29
|
+
auto_restore_snapshot: bool = True
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class VMReActAgent(ReActAgent):
|
|
33
|
+
"""
|
|
34
|
+
ReAct Agent mit VM-Backend.
|
|
35
|
+
|
|
36
|
+
Führt alle Tools in einer isolierten Kali Linux VM aus
|
|
37
|
+
für maximale Sicherheit und Reproduzierbarkeit.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, config: VMAgentConfig = None):
|
|
41
|
+
self.vm_config = config or VMAgentConfig()
|
|
42
|
+
|
|
43
|
+
# VM Manager initialisieren
|
|
44
|
+
if self.vm_config.use_vm:
|
|
45
|
+
self.vbox = VirtualBoxManager()
|
|
46
|
+
self.sandbox = PentestSandbox(self.vbox)
|
|
47
|
+
self.session_id = None
|
|
48
|
+
|
|
49
|
+
# Parent init ohne Tools (wir überschreiben sie)
|
|
50
|
+
self.config = self.vm_config
|
|
51
|
+
self.llm = None # Wird in _build_graph gesetzt
|
|
52
|
+
self.tools = []
|
|
53
|
+
self.graph = self._build_graph_with_vm()
|
|
54
|
+
|
|
55
|
+
logger.info(f"VMReActAgent initialisiert (VM: {self.vm_config.vm_name})")
|
|
56
|
+
|
|
57
|
+
def _build_graph_with_vm(self):
|
|
58
|
+
"""Baut Graph mit VM-Tools"""
|
|
59
|
+
from ..core.llm_backend import LLMBackend
|
|
60
|
+
|
|
61
|
+
self.llm = LLMBackend(model=self.config.llm_model)
|
|
62
|
+
|
|
63
|
+
# VM-basierte Tools erstellen
|
|
64
|
+
self.tools = self._create_vm_tools()
|
|
65
|
+
llm_with_tools = self.llm.bind_tools(self.tools)
|
|
66
|
+
|
|
67
|
+
system_prompt = """Du bist ein autonomer Pentest-Agent mit VM-Integration.
|
|
68
|
+
|
|
69
|
+
Deine Aufgabe:
|
|
70
|
+
1. Führe alle Tests in einer isolierten VM durch
|
|
71
|
+
2. Nutze Snapshots für Clean-State-Workflows
|
|
72
|
+
3. Dokumentiere alle Findings strukturiert
|
|
73
|
+
4. Fasse Ergebnisse am Ende zusammen
|
|
74
|
+
|
|
75
|
+
SICHERHEIT:
|
|
76
|
+
- Alle Scans laufen in der VM 'kali-pentest'
|
|
77
|
+
- Snapshots werden automatisch verwaltet
|
|
78
|
+
- Keine Auswirkungen auf das Host-System
|
|
79
|
+
|
|
80
|
+
Wenn fertig, gib eine finale Zusammenfassung."""
|
|
81
|
+
|
|
82
|
+
def agent_node(state: AgentState) -> AgentState:
|
|
83
|
+
if state["iteration"] >= state["max_iterations"]:
|
|
84
|
+
return {
|
|
85
|
+
**state,
|
|
86
|
+
"messages": state["messages"] + [
|
|
87
|
+
AIMessage(content="Maximale Iterationen erreicht.")
|
|
88
|
+
],
|
|
89
|
+
"status": "completed"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
messages = [SystemMessage(content=system_prompt)] + state["messages"]
|
|
93
|
+
response = llm_with_tools.invoke(messages)
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
**state,
|
|
97
|
+
"messages": [response],
|
|
98
|
+
"iteration": state["iteration"] + 1
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
def tools_node(state: AgentState) -> AgentState:
|
|
102
|
+
last_message = state["messages"][-1]
|
|
103
|
+
|
|
104
|
+
if not hasattr(last_message, 'tool_calls') or not last_message.tool_calls:
|
|
105
|
+
return state
|
|
106
|
+
|
|
107
|
+
tool_messages = []
|
|
108
|
+
new_findings = []
|
|
109
|
+
|
|
110
|
+
for tool_call in last_message.tool_calls:
|
|
111
|
+
tool_name = tool_call["name"]
|
|
112
|
+
args = tool_call["args"]
|
|
113
|
+
tool_id = tool_call["id"]
|
|
114
|
+
|
|
115
|
+
logger.info(f"VM-Tool-Aufruf: {tool_name}")
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
tool_func = {t.name: t for t in self.tools}.get(tool_name)
|
|
119
|
+
if tool_func:
|
|
120
|
+
result = tool_func.invoke(args)
|
|
121
|
+
tool_messages.append(ToolMessage(content=result, tool_call_id=tool_id))
|
|
122
|
+
new_findings.append({
|
|
123
|
+
"tool": tool_name,
|
|
124
|
+
"args": args,
|
|
125
|
+
"result": result
|
|
126
|
+
})
|
|
127
|
+
else:
|
|
128
|
+
tool_messages.append(ToolMessage(
|
|
129
|
+
content=f"Tool {tool_name} nicht gefunden",
|
|
130
|
+
tool_call_id=tool_id
|
|
131
|
+
))
|
|
132
|
+
except Exception as e:
|
|
133
|
+
error_msg = f"VM-Tool-Fehler: {str(e)}"
|
|
134
|
+
logger.error(error_msg)
|
|
135
|
+
tool_messages.append(ToolMessage(content=error_msg, tool_call_id=tool_id))
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
**state,
|
|
139
|
+
"messages": tool_messages,
|
|
140
|
+
"findings": state["findings"] + new_findings
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
def should_continue(state: AgentState):
|
|
144
|
+
last_message = state["messages"][-1]
|
|
145
|
+
|
|
146
|
+
if state.get("status") == "completed":
|
|
147
|
+
return "end"
|
|
148
|
+
|
|
149
|
+
if hasattr(last_message, 'tool_calls') and last_message.tool_calls:
|
|
150
|
+
return "tools"
|
|
151
|
+
|
|
152
|
+
if isinstance(last_message, ToolMessage):
|
|
153
|
+
return "agent"
|
|
154
|
+
|
|
155
|
+
return "end"
|
|
156
|
+
|
|
157
|
+
workflow = StateGraph(AgentState)
|
|
158
|
+
workflow.add_node("agent", agent_node)
|
|
159
|
+
workflow.add_node("tools", tools_node)
|
|
160
|
+
workflow.add_edge(START, "agent")
|
|
161
|
+
workflow.add_conditional_edges("agent", should_continue,
|
|
162
|
+
{"tools": "tools", "agent": "agent", "end": END})
|
|
163
|
+
workflow.add_conditional_edges("tools", should_continue,
|
|
164
|
+
{"tools": "tools", "agent": "agent", "end": END})
|
|
165
|
+
|
|
166
|
+
checkpointer = MemorySaver()
|
|
167
|
+
return workflow.compile(checkpointer=checkpointer)
|
|
168
|
+
|
|
169
|
+
def _create_vm_tools(self) -> List:
|
|
170
|
+
"""Erstellt VM-basierte Tools"""
|
|
171
|
+
from langchain_core.tools import tool
|
|
172
|
+
|
|
173
|
+
config = self.vm_config
|
|
174
|
+
|
|
175
|
+
@tool
|
|
176
|
+
def vm_nmap_scan(target: str, ports: str = "top-100") -> str:
|
|
177
|
+
"""Scannt Ports in VM mit Nmap"""
|
|
178
|
+
if not self.session_id:
|
|
179
|
+
return "Keine aktive Session"
|
|
180
|
+
|
|
181
|
+
port_arg = "--top-ports 100" if ports == "top-100" else f"-p {ports}"
|
|
182
|
+
exit_code, stdout, stderr = self.sandbox.execute_tool(
|
|
183
|
+
self.session_id, "nmap", f"{port_arg} {target}",
|
|
184
|
+
config.vm_username, config.vm_password
|
|
185
|
+
)
|
|
186
|
+
return stdout if exit_code == 0 else stderr
|
|
187
|
+
|
|
188
|
+
@tool
|
|
189
|
+
def vm_nuclei_scan(target: str, severity: str = "critical,high") -> str:
|
|
190
|
+
"""Scannt nach CVEs in VM mit Nuclei"""
|
|
191
|
+
if not self.session_id:
|
|
192
|
+
return "Keine aktive Session"
|
|
193
|
+
|
|
194
|
+
exit_code, stdout, stderr = self.sandbox.execute_tool(
|
|
195
|
+
self.session_id, "nuclei",
|
|
196
|
+
f"-u {target} -s {severity}",
|
|
197
|
+
config.vm_username, config.vm_password
|
|
198
|
+
)
|
|
199
|
+
return stdout if exit_code == 0 else stderr
|
|
200
|
+
|
|
201
|
+
@tool
|
|
202
|
+
def vm_gobuster_scan(target: str, wordlist: str = "common.txt") -> str:
|
|
203
|
+
"""Directory Enumeration in VM mit Gobuster"""
|
|
204
|
+
if not self.session_id:
|
|
205
|
+
return "Keine aktive Session"
|
|
206
|
+
|
|
207
|
+
exit_code, stdout, stderr = self.sandbox.execute_tool(
|
|
208
|
+
self.session_id, "gobuster",
|
|
209
|
+
f"dir -u {target} -w /usr/share/wordlists/dirb/{wordlist}",
|
|
210
|
+
config.vm_username, config.vm_password
|
|
211
|
+
)
|
|
212
|
+
return stdout if exit_code == 0 else stderr
|
|
213
|
+
|
|
214
|
+
@tool
|
|
215
|
+
def restore_vm_snapshot(snapshot_name: str = "clean_state") -> str:
|
|
216
|
+
"""Stellt VM-Snapshot wieder her"""
|
|
217
|
+
success = self.vbox.restore_snapshot(config.vm_name, snapshot_name)
|
|
218
|
+
return f"Snapshot {snapshot_name} wiederhergestellt" if success else "Fehler"
|
|
219
|
+
|
|
220
|
+
return [vm_nmap_scan, vm_nuclei_scan, vm_gobuster_scan, restore_vm_snapshot]
|
|
221
|
+
|
|
222
|
+
def run(self, target: str, objective: str = "comprehensive scan") -> dict:
|
|
223
|
+
"""Führt VM-basierten Pentest durch"""
|
|
224
|
+
|
|
225
|
+
# VM Session starten
|
|
226
|
+
if self.vm_config.use_vm:
|
|
227
|
+
import uuid
|
|
228
|
+
self.session_id = f"vm_pentest_{uuid.uuid4().hex[:8]}"
|
|
229
|
+
|
|
230
|
+
logger.info(f"Starte VM-Session: {self.session_id}")
|
|
231
|
+
|
|
232
|
+
if self.vm_config.auto_restore_snapshot:
|
|
233
|
+
logger.info("Stelle clean_state Snapshot wieder her...")
|
|
234
|
+
self.vbox.restore_snapshot(self.vm_config.vm_name, "clean_state")
|
|
235
|
+
|
|
236
|
+
if not self.sandbox.create_session(self.session_id, self.vm_config.vm_name):
|
|
237
|
+
raise RuntimeError("Konnte VM-Session nicht starten")
|
|
238
|
+
|
|
239
|
+
try:
|
|
240
|
+
# Parent run aufrufen
|
|
241
|
+
initial_state = {
|
|
242
|
+
"messages": [HumanMessage(content=f"{objective} on {target}")],
|
|
243
|
+
"findings": [],
|
|
244
|
+
"target": target,
|
|
245
|
+
"iteration": 0,
|
|
246
|
+
"max_iterations": self.config.max_iterations,
|
|
247
|
+
"status": "running"
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
result = self.graph.invoke(
|
|
251
|
+
initial_state,
|
|
252
|
+
config={"configurable": {"thread_id": self.session_id}}
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
"findings": result["findings"],
|
|
257
|
+
"final_message": result["messages"][-1].content if result["messages"] else "",
|
|
258
|
+
"iterations": result["iteration"],
|
|
259
|
+
"status": result["status"],
|
|
260
|
+
"target": target,
|
|
261
|
+
"session_id": self.session_id
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
finally:
|
|
265
|
+
# Aufräumen
|
|
266
|
+
if self.session_id and self.vm_config.use_vm:
|
|
267
|
+
logger.info("Beende VM-Session...")
|
|
268
|
+
self.sandbox.end_session(self.session_id)
|
|
269
|
+
self.session_id = None
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
# Factory
|
|
273
|
+
def get_vm_agent(config: VMAgentConfig = None) -> VMReActAgent:
|
|
274
|
+
"""Gibt VM-basierten Agent zurück"""
|
|
275
|
+
return VMReActAgent(config)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
if __name__ == "__main__":
|
|
279
|
+
logging.basicConfig(level=logging.INFO)
|
|
280
|
+
|
|
281
|
+
config = VMAgentConfig(
|
|
282
|
+
use_vm=True,
|
|
283
|
+
vm_name="kali-pentest",
|
|
284
|
+
max_iterations=5,
|
|
285
|
+
auto_restore_snapshot=True
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
agent = VMReActAgent(config)
|
|
289
|
+
result = agent.run("scanme.nmap.org", "Port scan and vulnerability assessment")
|
|
290
|
+
|
|
291
|
+
print("\n" + "=" * 70)
|
|
292
|
+
print("VM-BASIERTER PENTEST ABGESCHLOSSEN")
|
|
293
|
+
print("=" * 70)
|
|
294
|
+
print(f"Session: {result.get('session_id')}")
|
|
295
|
+
print(f"Iterations: {result['iterations']}")
|
|
296
|
+
print(f"Findings: {len(result['findings'])}")
|
|
297
|
+
print("\nFinal Analysis:")
|
|
298
|
+
print(result['final_message'][:500])
|
agents/research_agent.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Research Agent - Specializes in reconnaissance and information gathering
|
|
4
|
+
Part of the Multi-Agent Collaboration System
|
|
5
|
+
Author: SHAdd0WTAka
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Any, Dict, List
|
|
11
|
+
|
|
12
|
+
from modules.cve_database import CVEDatabase
|
|
13
|
+
from modules.sql_injection_db import SQLInjectionDatabase
|
|
14
|
+
|
|
15
|
+
from .agent_base import AgentMessage, AgentRole, BaseAgent
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger("ZenAI.Agents")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ResearchAgent(BaseAgent):
|
|
21
|
+
"""
|
|
22
|
+
Research Agent - Gathers information, performs reconnaissance
|
|
23
|
+
Can research CVEs, exploits, techniques, and targets
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, name: str, orchestrator=None, zen_orchestrator=None):
|
|
27
|
+
super().__init__(name, AgentRole.RESEARCHER, orchestrator)
|
|
28
|
+
self.zen_orchestrator = zen_orchestrator # LLM orchestrator
|
|
29
|
+
self.cve_db = CVEDatabase()
|
|
30
|
+
self.sqli_db = SQLInjectionDatabase()
|
|
31
|
+
self.current_research = {}
|
|
32
|
+
|
|
33
|
+
# Register message handlers
|
|
34
|
+
self.register_handler("research_task", self._handle_research_task)
|
|
35
|
+
self.register_handler("request_info", self._handle_info_request)
|
|
36
|
+
self.register_handler("findings", self._handle_findings)
|
|
37
|
+
|
|
38
|
+
async def _handle_research_task(self, msg: AgentMessage):
|
|
39
|
+
"""Handle incoming research tasks"""
|
|
40
|
+
logger.info(f"[ResearchAgent:{self.name}] Received research task")
|
|
41
|
+
|
|
42
|
+
task_context = msg.context.get("pentest_context", {})
|
|
43
|
+
thread_id = msg.context.get("thread_id", "unknown")
|
|
44
|
+
|
|
45
|
+
# Acknowledge receipt
|
|
46
|
+
await self.send_message(
|
|
47
|
+
content=f"Starting research on {task_context.get('target', 'unknown target')}",
|
|
48
|
+
recipient=msg.sender,
|
|
49
|
+
msg_type="research_status",
|
|
50
|
+
context={"thread_id": thread_id, "status": "started"},
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Perform research
|
|
54
|
+
findings = await self._perform_research(task_context)
|
|
55
|
+
|
|
56
|
+
# Share findings with all agents
|
|
57
|
+
await self.send_message(
|
|
58
|
+
content=f"Research complete. Found {len(findings)} relevant items.",
|
|
59
|
+
recipient="all",
|
|
60
|
+
msg_type="findings",
|
|
61
|
+
priority=2,
|
|
62
|
+
context={
|
|
63
|
+
"thread_id": thread_id,
|
|
64
|
+
"findings": findings,
|
|
65
|
+
"researcher": self.name,
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Update shared context
|
|
70
|
+
self.update_context(f"research_{thread_id}", findings, share=True)
|
|
71
|
+
|
|
72
|
+
async def _handle_info_request(self, msg: AgentMessage):
|
|
73
|
+
"""Handle requests for specific information"""
|
|
74
|
+
request_type = msg.context.get("info_type")
|
|
75
|
+
query = msg.content
|
|
76
|
+
|
|
77
|
+
logger.info(f"[ResearchAgent:{self.name}] Info request: {request_type}")
|
|
78
|
+
|
|
79
|
+
result = None
|
|
80
|
+
|
|
81
|
+
if request_type == "cve":
|
|
82
|
+
result = self.cve_db.search_cve(query)
|
|
83
|
+
elif request_type == "ransomware":
|
|
84
|
+
result = self.cve_db.search_ransomware(query)
|
|
85
|
+
elif request_type == "sqli_payload":
|
|
86
|
+
db_type = msg.context.get("db_type")
|
|
87
|
+
result = self.sqli_db.get_payloads(db_type=db_type)
|
|
88
|
+
|
|
89
|
+
if result:
|
|
90
|
+
await self.send_message(
|
|
91
|
+
content=f"Found information about {query}",
|
|
92
|
+
recipient=msg.sender,
|
|
93
|
+
msg_type="response",
|
|
94
|
+
context={"result": result, "query": query},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
async def _handle_findings(self, msg: AgentMessage):
|
|
98
|
+
"""Handle findings from other agents - cross-reference with research"""
|
|
99
|
+
findings = msg.context.get("findings", {})
|
|
100
|
+
|
|
101
|
+
# If findings contain CVEs, enrich them with database info
|
|
102
|
+
if "cves" in findings:
|
|
103
|
+
enriched = []
|
|
104
|
+
for cve_id in findings["cves"]:
|
|
105
|
+
cve_info = self.cve_db.search_cve(cve_id)
|
|
106
|
+
if cve_info:
|
|
107
|
+
enriched.append(cve_info)
|
|
108
|
+
|
|
109
|
+
if enriched:
|
|
110
|
+
await self.send_message(
|
|
111
|
+
content=f"Enriched {len(enriched)} CVEs with database information",
|
|
112
|
+
recipient=msg.sender,
|
|
113
|
+
msg_type="enrichment",
|
|
114
|
+
context={"enriched_cves": enriched},
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
async def _perform_research(self, context: Dict) -> List[Dict]:
|
|
118
|
+
"""Perform actual research based on context"""
|
|
119
|
+
findings = []
|
|
120
|
+
target = context.get("target", "")
|
|
121
|
+
|
|
122
|
+
# Research 1: Check for known CVEs related to target tech
|
|
123
|
+
technologies = context.get("technologies", [])
|
|
124
|
+
for tech in technologies:
|
|
125
|
+
# Use LLM to find relevant CVEs
|
|
126
|
+
if self.zen_orchestrator:
|
|
127
|
+
prompt = f"""
|
|
128
|
+
List known CVEs for {tech} that are commonly exploited by ransomware.
|
|
129
|
+
Return CVE IDs only, one per line.
|
|
130
|
+
"""
|
|
131
|
+
response = await self.zen_orchestrator.process(prompt)
|
|
132
|
+
|
|
133
|
+
# Parse CVEs from response
|
|
134
|
+
import re
|
|
135
|
+
|
|
136
|
+
cves = re.findall(r"CVE-\d{4}-\d{4,}", response.content)
|
|
137
|
+
|
|
138
|
+
for cve_id in cves[:5]: # Limit to top 5
|
|
139
|
+
cve_data = self.cve_db.search_cve(cve_id)
|
|
140
|
+
if cve_data:
|
|
141
|
+
findings.append(
|
|
142
|
+
{"type": "cve", "data": cve_data, "source": "llm_research"}
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Research 2: Check for ransomware using these technologies
|
|
146
|
+
ransomware = self.cve_db.list_all_ransomware()
|
|
147
|
+
for rw in ransomware[:3]: # Check top 3
|
|
148
|
+
findings.append({"type": "ransomware", "data": rw, "source": "database"})
|
|
149
|
+
|
|
150
|
+
return findings
|
|
151
|
+
|
|
152
|
+
async def execute_task(self, task: Dict) -> Dict:
|
|
153
|
+
"""Execute a research task"""
|
|
154
|
+
task_type = task.get("type", "")
|
|
155
|
+
context = task.get("context", {})
|
|
156
|
+
|
|
157
|
+
logger.info(f"[ResearchAgent:{self.name}] Executing task: {task_type}")
|
|
158
|
+
|
|
159
|
+
if task_type == "reconnaissance":
|
|
160
|
+
findings = await self._perform_research(context)
|
|
161
|
+
|
|
162
|
+
# Share with other agents
|
|
163
|
+
await self.send_message(
|
|
164
|
+
content="Reconnaissance findings ready",
|
|
165
|
+
recipient="role:analyst",
|
|
166
|
+
msg_type="findings",
|
|
167
|
+
context={"findings": findings},
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
"status": "complete",
|
|
172
|
+
"findings_count": len(findings),
|
|
173
|
+
"agent": self.name,
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {"status": "unknown_task"}
|
api/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Zen AI Pentest - REST API Module
|
|
3
|
+
|
|
4
|
+
A production-ready API for penetration testing operations.
|
|
5
|
+
Built with FastAPI for high performance and automatic documentation.
|
|
6
|
+
|
|
7
|
+
Author: SHAdd0WTAka + Kimi AI
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
__version__ = "1.0.0"
|
|
11
|
+
__all__ = ["app", "create_app"]
|
api/auth.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
JWT Authentication für Zen-AI-Pentest API
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from datetime import datetime, timedelta
|
|
6
|
+
from typing import Optional, Dict
|
|
7
|
+
from jose import JWTError, jwt
|
|
8
|
+
from passlib.context import CryptContext
|
|
9
|
+
from fastapi import Depends, HTTPException, status
|
|
10
|
+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
11
|
+
|
|
12
|
+
# Configuration
|
|
13
|
+
SECRET_KEY = "your-secret-key-here-change-in-production" # In production: env var
|
|
14
|
+
ALGORITHM = "HS256"
|
|
15
|
+
ACCESS_TOKEN_EXPIRE_MINUTES = 30
|
|
16
|
+
|
|
17
|
+
# Password hashing
|
|
18
|
+
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
|
19
|
+
security = HTTPBearer()
|
|
20
|
+
|
|
21
|
+
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
|
22
|
+
"""Verifiziert Passwort"""
|
|
23
|
+
return pwd_context.verify(plain_password, hashed_password)
|
|
24
|
+
|
|
25
|
+
def get_password_hash(password: str) -> str:
|
|
26
|
+
"""Hashed Passwort"""
|
|
27
|
+
return pwd_context.hash(password)
|
|
28
|
+
|
|
29
|
+
def create_access_token(data: Dict, expires_delta: Optional[timedelta] = None) -> str:
|
|
30
|
+
"""Erstellt JWT Token"""
|
|
31
|
+
to_encode = data.copy()
|
|
32
|
+
expire = datetime.utcnow() + (expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
|
|
33
|
+
to_encode.update({"exp": expire})
|
|
34
|
+
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
|
35
|
+
return encoded_jwt
|
|
36
|
+
|
|
37
|
+
def decode_token(token: str) -> Optional[Dict]:
|
|
38
|
+
"""Decodiert JWT Token"""
|
|
39
|
+
try:
|
|
40
|
+
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
|
41
|
+
return payload
|
|
42
|
+
except JWTError:
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
async def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)) -> Dict:
|
|
46
|
+
"""FastAPI Dependency für Token-Verifizierung"""
|
|
47
|
+
token = credentials.credentials
|
|
48
|
+
payload = decode_token(token)
|
|
49
|
+
|
|
50
|
+
if payload is None:
|
|
51
|
+
raise HTTPException(
|
|
52
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
53
|
+
detail="Invalid authentication credentials",
|
|
54
|
+
headers={"WWW-Authenticate": "Bearer"},
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Check expiration
|
|
58
|
+
exp = payload.get("exp")
|
|
59
|
+
if exp and datetime.utcnow().timestamp() > exp:
|
|
60
|
+
raise HTTPException(
|
|
61
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
62
|
+
detail="Token has expired",
|
|
63
|
+
headers={"WWW-Authenticate": "Bearer"},
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return payload
|
|
67
|
+
|
|
68
|
+
def check_permissions(user: Dict, required_role: str) -> bool:
|
|
69
|
+
"""Prüft ob User die benötigte Rolle hat"""
|
|
70
|
+
user_role = user.get("role", "viewer")
|
|
71
|
+
|
|
72
|
+
roles_hierarchy = {
|
|
73
|
+
"viewer": 1,
|
|
74
|
+
"operator": 2,
|
|
75
|
+
"admin": 3
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
user_level = roles_hierarchy.get(user_role, 0)
|
|
79
|
+
required_level = roles_hierarchy.get(required_role, 0)
|
|
80
|
+
|
|
81
|
+
return user_level >= required_level
|
|
82
|
+
|
|
83
|
+
async def require_admin(credentials: HTTPAuthorizationCredentials = Depends(security)) -> Dict:
|
|
84
|
+
"""Erfordert Admin-Rolle"""
|
|
85
|
+
user = await verify_token(credentials)
|
|
86
|
+
|
|
87
|
+
if not check_permissions(user, "admin"):
|
|
88
|
+
raise HTTPException(
|
|
89
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
90
|
+
detail="Admin privileges required"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return user
|
|
94
|
+
|
|
95
|
+
async def require_operator(credentials: HTTPAuthorizationCredentials = Depends(security)) -> Dict:
|
|
96
|
+
"""Erfordert Operator oder höhere Rolle"""
|
|
97
|
+
user = await verify_token(credentials)
|
|
98
|
+
|
|
99
|
+
if not check_permissions(user, "operator"):
|
|
100
|
+
raise HTTPException(
|
|
101
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
102
|
+
detail="Operator privileges required"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return user
|
|
106
|
+
|
|
107
|
+
# API Key Authentication (für CI/CD Integrationen)
|
|
108
|
+
API_KEYS = {} # In production: in DB speichern
|
|
109
|
+
|
|
110
|
+
def verify_api_key(api_key: str) -> Optional[Dict]:
|
|
111
|
+
"""Verifiziert API Key"""
|
|
112
|
+
return API_KEYS.get(api_key)
|
|
113
|
+
|
|
114
|
+
def create_api_key(user_id: int, name: str) -> str:
|
|
115
|
+
"""Erstellt neuen API Key"""
|
|
116
|
+
import secrets
|
|
117
|
+
key = secrets.token_urlsafe(32)
|
|
118
|
+
API_KEYS[key] = {
|
|
119
|
+
"user_id": user_id,
|
|
120
|
+
"name": name,
|
|
121
|
+
"created_at": datetime.utcnow().isoformat()
|
|
122
|
+
}
|
|
123
|
+
return key
|