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/__init__.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Multi-Agent Collaboration System for Zen AI Pentest
|
|
3
|
+
Inspired by Clawed/Moltbot architecture
|
|
4
|
+
Agents can communicate, share context, and coordinate research
|
|
5
|
+
Author: SHAdd0WTAka
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .agent_base import AgentMessage, AgentRole, BaseAgent
|
|
9
|
+
from .agent_orchestrator import AgentOrchestrator
|
|
10
|
+
from .analysis_agent import AnalysisAgent
|
|
11
|
+
from .exploit_agent import ExploitAgent
|
|
12
|
+
from .post_scan_agent import (PentestLoot, PostScanAgent, VerifiedFinding,
|
|
13
|
+
run_post_scan_workflow)
|
|
14
|
+
from .research_agent import ResearchAgent
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"BaseAgent",
|
|
18
|
+
"AgentRole",
|
|
19
|
+
"AgentMessage",
|
|
20
|
+
"AgentOrchestrator",
|
|
21
|
+
"ResearchAgent",
|
|
22
|
+
"ExploitAgent",
|
|
23
|
+
"AnalysisAgent",
|
|
24
|
+
"PostScanAgent",
|
|
25
|
+
"run_post_scan_workflow",
|
|
26
|
+
"VerifiedFinding",
|
|
27
|
+
"PentestLoot",
|
|
28
|
+
]
|
agents/agent_base.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Base Agent Class for Multi-Agent System
|
|
4
|
+
Provides messaging, context sharing, and role-based capabilities
|
|
5
|
+
Author: SHAdd0WTAka
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import logging
|
|
10
|
+
import uuid
|
|
11
|
+
from abc import ABC, abstractmethod
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from enum import Enum
|
|
15
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger("ZenAI.Agents")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AgentRole(Enum):
|
|
21
|
+
"""Agent roles for specialization"""
|
|
22
|
+
|
|
23
|
+
RESEARCHER = "researcher" # Gathers information, reconnaissance
|
|
24
|
+
ANALYST = "analyst" # Analyzes data, finds patterns
|
|
25
|
+
EXPLOIT = "exploit" # Develops exploits, payloads
|
|
26
|
+
COORDINATOR = "coordinator" # Manages workflow between agents
|
|
27
|
+
REPORTER = "reporter" # Generates reports, summaries
|
|
28
|
+
POST_EXPLOITATION = (
|
|
29
|
+
"post_exploit" # Post-scan workflow: verification, evidence, cleanup
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class AgentState(Enum):
|
|
34
|
+
"""Operational states for agents"""
|
|
35
|
+
|
|
36
|
+
IDLE = "idle"
|
|
37
|
+
BUSY = "busy"
|
|
38
|
+
WAITING = "waiting"
|
|
39
|
+
COMPLETED = "completed"
|
|
40
|
+
FAILED = "failed"
|
|
41
|
+
STOPPED = "stopped"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class AgentMessage:
|
|
47
|
+
"""Message format for inter-agent communication"""
|
|
48
|
+
|
|
49
|
+
id: str = field(default_factory=lambda: str(uuid.uuid4())[:8])
|
|
50
|
+
sender: str = ""
|
|
51
|
+
recipient: str = "" # "all" for broadcast, specific ID for direct
|
|
52
|
+
msg_type: str = "chat" # chat, task, result, request, response
|
|
53
|
+
content: str = ""
|
|
54
|
+
context: Dict[str, Any] = field(default_factory=dict)
|
|
55
|
+
timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
|
|
56
|
+
priority: int = 1 # 1=low, 2=medium, 3=high, 4=critical
|
|
57
|
+
requires_response: bool = False
|
|
58
|
+
|
|
59
|
+
def to_dict(self) -> Dict:
|
|
60
|
+
return {
|
|
61
|
+
"id": self.id,
|
|
62
|
+
"sender": self.sender,
|
|
63
|
+
"recipient": self.recipient,
|
|
64
|
+
"type": self.msg_type,
|
|
65
|
+
"content": self.content,
|
|
66
|
+
"context": self.context,
|
|
67
|
+
"timestamp": self.timestamp,
|
|
68
|
+
"priority": self.priority,
|
|
69
|
+
"requires_response": self.requires_response,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class BaseAgent(ABC):
|
|
74
|
+
"""
|
|
75
|
+
Base class for all agents in the system
|
|
76
|
+
Provides messaging, context management, and coordination capabilities
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
def __init__(self, name: str, role: AgentRole, orchestrator=None):
|
|
80
|
+
self.id = str(uuid.uuid4())[:8]
|
|
81
|
+
self.name = name
|
|
82
|
+
self.role = role
|
|
83
|
+
self.orchestrator = orchestrator
|
|
84
|
+
self.message_queue: asyncio.Queue = asyncio.Queue()
|
|
85
|
+
self.inbox: List[AgentMessage] = []
|
|
86
|
+
self.context: Dict[str, Any] = {} # Shared workspace
|
|
87
|
+
self.memory: List[Dict] = [] # Agent's memory of interactions
|
|
88
|
+
self.handlers: Dict[str, Callable] = {}
|
|
89
|
+
self.running = False
|
|
90
|
+
self.task: Optional[asyncio.Task] = None
|
|
91
|
+
|
|
92
|
+
logger.info(f"[Agent] Initialized {self.name} ({self.role.value}) [{self.id}]")
|
|
93
|
+
|
|
94
|
+
def register_handler(self, msg_type: str, handler: Callable):
|
|
95
|
+
"""Register a handler for specific message types"""
|
|
96
|
+
self.handlers[msg_type] = handler
|
|
97
|
+
|
|
98
|
+
async def send_message(
|
|
99
|
+
self,
|
|
100
|
+
content: str,
|
|
101
|
+
recipient: str = "all",
|
|
102
|
+
msg_type: str = "chat",
|
|
103
|
+
priority: int = 1,
|
|
104
|
+
requires_response: bool = False,
|
|
105
|
+
context: Dict = None,
|
|
106
|
+
) -> str:
|
|
107
|
+
"""
|
|
108
|
+
Send a message to another agent or broadcast
|
|
109
|
+
Returns message ID for tracking
|
|
110
|
+
"""
|
|
111
|
+
msg = AgentMessage(
|
|
112
|
+
sender=f"{self.name}[{self.id}]",
|
|
113
|
+
recipient=recipient,
|
|
114
|
+
msg_type=msg_type,
|
|
115
|
+
content=content,
|
|
116
|
+
priority=priority,
|
|
117
|
+
requires_response=requires_response,
|
|
118
|
+
context=context or {},
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if self.orchestrator:
|
|
122
|
+
await self.orchestrator.route_message(msg)
|
|
123
|
+
logger.debug(f"[Agent:{self.name}] Sent {msg_type} to {recipient}")
|
|
124
|
+
else:
|
|
125
|
+
# Direct send if no orchestrator
|
|
126
|
+
await self.message_queue.put(msg)
|
|
127
|
+
|
|
128
|
+
return msg.id
|
|
129
|
+
|
|
130
|
+
async def receive_message(self, msg: AgentMessage):
|
|
131
|
+
"""Receive a message into the queue"""
|
|
132
|
+
await self.message_queue.put(msg)
|
|
133
|
+
self.inbox.append(msg)
|
|
134
|
+
|
|
135
|
+
# Store in memory
|
|
136
|
+
self.memory.append(
|
|
137
|
+
{"type": "received", "message": msg.to_dict(), "processed": False}
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
logger.debug(f"[Agent:{self.name}] Received message from {msg.sender}")
|
|
141
|
+
|
|
142
|
+
async def process_messages(self):
|
|
143
|
+
"""Main message processing loop"""
|
|
144
|
+
self.running = True
|
|
145
|
+
|
|
146
|
+
while self.running:
|
|
147
|
+
try:
|
|
148
|
+
# Get message with timeout to allow checking running flag
|
|
149
|
+
msg = await asyncio.wait_for(self.message_queue.get(), timeout=1.0)
|
|
150
|
+
|
|
151
|
+
# Process based on type
|
|
152
|
+
if msg.msg_type in self.handlers:
|
|
153
|
+
await self.handlers[msg.msg_type](msg)
|
|
154
|
+
else:
|
|
155
|
+
await self.handle_message(msg)
|
|
156
|
+
|
|
157
|
+
# Mark as processed in memory
|
|
158
|
+
for mem in self.memory:
|
|
159
|
+
if mem["type"] == "received" and mem["message"]["id"] == msg.id:
|
|
160
|
+
mem["processed"] = True
|
|
161
|
+
|
|
162
|
+
except asyncio.TimeoutError:
|
|
163
|
+
continue
|
|
164
|
+
except Exception as e:
|
|
165
|
+
logger.error(f"[Agent:{self.name}] Error processing message: {e}")
|
|
166
|
+
|
|
167
|
+
async def handle_message(self, msg: AgentMessage):
|
|
168
|
+
"""Default message handler - override in subclasses"""
|
|
169
|
+
logger.info(f"[Agent:{self.name}] Handling {msg.msg_type} from {msg.sender}")
|
|
170
|
+
|
|
171
|
+
# Default chat response
|
|
172
|
+
if msg.msg_type == "chat":
|
|
173
|
+
response = f"Acknowledged: {msg.content[:50]}..."
|
|
174
|
+
if msg.requires_response:
|
|
175
|
+
await self.send_message(
|
|
176
|
+
content=response, recipient=msg.sender, msg_type="response"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
def update_context(self, key: str, value: Any, share: bool = False):
|
|
180
|
+
"""
|
|
181
|
+
Update agent's local context
|
|
182
|
+
If share=True, broadcast to all agents via orchestrator
|
|
183
|
+
"""
|
|
184
|
+
self.context[key] = value
|
|
185
|
+
|
|
186
|
+
if share and self.orchestrator:
|
|
187
|
+
asyncio.create_task(
|
|
188
|
+
self.orchestrator.update_shared_context(key, value, self.id)
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
def get_context(self, key: str) -> Any:
|
|
192
|
+
"""Get value from context"""
|
|
193
|
+
return self.context.get(key)
|
|
194
|
+
|
|
195
|
+
def share_findings(self, findings: Dict):
|
|
196
|
+
"""Share findings with all agents"""
|
|
197
|
+
if self.orchestrator:
|
|
198
|
+
asyncio.create_task(
|
|
199
|
+
self.send_message(
|
|
200
|
+
content=f"Sharing {len(findings)} findings",
|
|
201
|
+
msg_type="findings",
|
|
202
|
+
context={"findings": findings},
|
|
203
|
+
priority=2,
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
@abstractmethod
|
|
208
|
+
async def execute_task(self, task: Dict) -> Dict:
|
|
209
|
+
"""Execute a specific task - must be implemented by subclasses"""
|
|
210
|
+
pass
|
|
211
|
+
|
|
212
|
+
async def start(self):
|
|
213
|
+
"""Start the agent's message processing loop"""
|
|
214
|
+
self.task = asyncio.create_task(self.process_messages())
|
|
215
|
+
logger.info(f"[Agent:{self.name}] Started")
|
|
216
|
+
|
|
217
|
+
async def stop(self):
|
|
218
|
+
"""Stop the agent gracefully"""
|
|
219
|
+
self.running = False
|
|
220
|
+
if self.task:
|
|
221
|
+
self.task.cancel()
|
|
222
|
+
try:
|
|
223
|
+
await self.task
|
|
224
|
+
except asyncio.CancelledError:
|
|
225
|
+
pass
|
|
226
|
+
logger.info(f"[Agent:{self.name}] Stopped")
|
|
227
|
+
|
|
228
|
+
def get_status(self) -> Dict:
|
|
229
|
+
"""Get agent status"""
|
|
230
|
+
return {
|
|
231
|
+
"id": self.id,
|
|
232
|
+
"name": self.name,
|
|
233
|
+
"role": self.role.value,
|
|
234
|
+
"running": self.running,
|
|
235
|
+
"queue_size": self.message_queue.qsize(),
|
|
236
|
+
"inbox_count": len(self.inbox),
|
|
237
|
+
"memory_entries": len(self.memory),
|
|
238
|
+
"context_keys": list(self.context.keys()),
|
|
239
|
+
}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Agent Orchestrator
|
|
4
|
+
Manages multiple agents, routes messages, coordinates research
|
|
5
|
+
Like Clawed/Moltbot but for penetration testing
|
|
6
|
+
Author: SHAdd0WTAka
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import logging
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from typing import Any, Dict, List, Optional
|
|
13
|
+
|
|
14
|
+
from .agent_base import AgentMessage, AgentRole, BaseAgent
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger("ZenAI.Agents")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AgentOrchestrator:
|
|
20
|
+
"""
|
|
21
|
+
Central coordinator for multi-agent system
|
|
22
|
+
Manages agent lifecycle, message routing, and context sharing
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, zen_orchestrator=None):
|
|
26
|
+
self.agents: Dict[str, BaseAgent] = {}
|
|
27
|
+
self.agent_by_role: Dict[AgentRole, List[BaseAgent]] = {}
|
|
28
|
+
self.shared_context: Dict[str, Any] = {}
|
|
29
|
+
self.message_history: List[AgentMessage] = []
|
|
30
|
+
self.conversation_threads: Dict[str, List[str]] = {} # thread_id -> message_ids
|
|
31
|
+
self.zen_orchestrator = zen_orchestrator # Link to LLM orchestrator
|
|
32
|
+
self.running = False
|
|
33
|
+
self.research_coordination: Dict[str, Any] = {}
|
|
34
|
+
|
|
35
|
+
def register_agent(self, agent: BaseAgent):
|
|
36
|
+
"""Register an agent with the orchestrator"""
|
|
37
|
+
self.agents[agent.id] = agent
|
|
38
|
+
agent.orchestrator = self
|
|
39
|
+
|
|
40
|
+
# Index by role
|
|
41
|
+
if agent.role not in self.agent_by_role:
|
|
42
|
+
self.agent_by_role[agent.role] = []
|
|
43
|
+
self.agent_by_role[agent.role].append(agent)
|
|
44
|
+
|
|
45
|
+
logger.info(
|
|
46
|
+
f"[Orchestrator] Registered agent {agent.name} ({agent.role.value})"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def unregister_agent(self, agent_id: str):
|
|
50
|
+
"""Remove an agent from the system"""
|
|
51
|
+
if agent_id in self.agents:
|
|
52
|
+
agent = self.agents[agent_id]
|
|
53
|
+
|
|
54
|
+
# Remove from role index
|
|
55
|
+
if agent.role in self.agent_by_role:
|
|
56
|
+
self.agent_by_role[agent.role] = [
|
|
57
|
+
a for a in self.agent_by_role[agent.role] if a.id != agent_id
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
del self.agents[agent_id]
|
|
61
|
+
logger.info(f"[Orchestrator] Unregistered agent {agent_id}")
|
|
62
|
+
|
|
63
|
+
async def route_message(self, msg: AgentMessage):
|
|
64
|
+
"""Route a message to the appropriate recipient(s)"""
|
|
65
|
+
self.message_history.append(msg)
|
|
66
|
+
|
|
67
|
+
if msg.recipient == "all":
|
|
68
|
+
# Broadcast to all agents except sender
|
|
69
|
+
for agent_id, agent in self.agents.items():
|
|
70
|
+
if f"[{agent_id}]" not in msg.sender:
|
|
71
|
+
await agent.receive_message(msg)
|
|
72
|
+
|
|
73
|
+
elif msg.recipient.startswith("role:"):
|
|
74
|
+
# Send to all agents of a specific role
|
|
75
|
+
role_str = msg.recipient.split(":")[1]
|
|
76
|
+
try:
|
|
77
|
+
role = AgentRole(role_str)
|
|
78
|
+
for agent in self.agent_by_role.get(role, []):
|
|
79
|
+
if f"[{agent.id}]" not in msg.sender:
|
|
80
|
+
await agent.receive_message(msg)
|
|
81
|
+
except ValueError:
|
|
82
|
+
logger.error(f"[Orchestrator] Invalid role: {role_str}")
|
|
83
|
+
|
|
84
|
+
else:
|
|
85
|
+
# Direct message to specific agent
|
|
86
|
+
# Parse agent ID from recipient string like "AgentName[ID]"
|
|
87
|
+
if "[" in msg.recipient and "]" in msg.recipient:
|
|
88
|
+
agent_id = msg.recipient.split("[")[1].split("]")[0]
|
|
89
|
+
if agent_id in self.agents:
|
|
90
|
+
await self.agents[agent_id].receive_message(msg)
|
|
91
|
+
|
|
92
|
+
async def update_shared_context(self, key: str, value: Any, source_agent_id: str):
|
|
93
|
+
"""Update shared context and notify all agents"""
|
|
94
|
+
self.shared_context[key] = {
|
|
95
|
+
"value": value,
|
|
96
|
+
"updated_by": source_agent_id,
|
|
97
|
+
"timestamp": datetime.now().isoformat(),
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# Notify all agents of context update
|
|
101
|
+
for agent_id, agent in self.agents.items():
|
|
102
|
+
if agent_id != source_agent_id:
|
|
103
|
+
await agent.receive_message(
|
|
104
|
+
AgentMessage(
|
|
105
|
+
sender="orchestrator",
|
|
106
|
+
recipient=f"{agent.name}[{agent.id}]",
|
|
107
|
+
msg_type="context_update",
|
|
108
|
+
content=f"Context updated: {key}",
|
|
109
|
+
context={"key": key, "value": value},
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
def get_shared_context(self, key: str = None) -> Any:
|
|
114
|
+
"""Get value from shared context"""
|
|
115
|
+
if key:
|
|
116
|
+
return self.shared_context.get(key, {}).get("value")
|
|
117
|
+
return self.shared_context
|
|
118
|
+
|
|
119
|
+
async def start_research_coordination(
|
|
120
|
+
self, topic: str, pentest_context: Dict
|
|
121
|
+
) -> str:
|
|
122
|
+
"""
|
|
123
|
+
Coordinate multi-agent research on a topic
|
|
124
|
+
Returns thread ID for tracking
|
|
125
|
+
"""
|
|
126
|
+
thread_id = f"research_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
127
|
+
|
|
128
|
+
self.research_coordination[thread_id] = {
|
|
129
|
+
"topic": topic,
|
|
130
|
+
"context": pentest_context,
|
|
131
|
+
"started": datetime.now().isoformat(),
|
|
132
|
+
"agents_involved": [],
|
|
133
|
+
"findings": [],
|
|
134
|
+
"status": "active",
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# Notify all research agents
|
|
138
|
+
for agent in self.agent_by_role.get(AgentRole.RESEARCHER, []):
|
|
139
|
+
await agent.receive_message(
|
|
140
|
+
AgentMessage(
|
|
141
|
+
sender="orchestrator",
|
|
142
|
+
recipient=f"{agent.name}[{agent.id}]",
|
|
143
|
+
msg_type="research_task",
|
|
144
|
+
content=f"Research coordination started: {topic}",
|
|
145
|
+
context={
|
|
146
|
+
"thread_id": thread_id,
|
|
147
|
+
"pentest_context": pentest_context,
|
|
148
|
+
"shared_context": self.shared_context,
|
|
149
|
+
},
|
|
150
|
+
priority=3,
|
|
151
|
+
)
|
|
152
|
+
)
|
|
153
|
+
self.research_coordination[thread_id]["agents_involved"].append(agent.id)
|
|
154
|
+
|
|
155
|
+
logger.info(f"[Orchestrator] Started research coordination: {thread_id}")
|
|
156
|
+
return thread_id
|
|
157
|
+
|
|
158
|
+
async def coordinate_agents(self, task_type: str, context: Dict) -> Dict:
|
|
159
|
+
"""
|
|
160
|
+
Coordinate multiple agents for a complex task
|
|
161
|
+
Agents will communicate and share findings
|
|
162
|
+
"""
|
|
163
|
+
results = {
|
|
164
|
+
"task_type": task_type,
|
|
165
|
+
"started": datetime.now().isoformat(),
|
|
166
|
+
"agent_responses": {},
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# Determine which agents to involve based on task
|
|
170
|
+
involved_roles = []
|
|
171
|
+
|
|
172
|
+
if task_type == "reconnaissance":
|
|
173
|
+
involved_roles = [AgentRole.RESEARCHER, AgentRole.ANALYST]
|
|
174
|
+
elif task_type == "vulnerability_analysis":
|
|
175
|
+
involved_roles = [AgentRole.ANALYST, AgentRole.EXPLOIT]
|
|
176
|
+
elif task_type == "exploit_development":
|
|
177
|
+
involved_roles = [AgentRole.EXPLOIT, AgentRole.ANALYST]
|
|
178
|
+
elif task_type == "full_assessment":
|
|
179
|
+
involved_roles = [
|
|
180
|
+
AgentRole.RESEARCHER,
|
|
181
|
+
AgentRole.ANALYST,
|
|
182
|
+
AgentRole.EXPLOIT,
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
# Start all involved agents
|
|
186
|
+
tasks = []
|
|
187
|
+
for role in involved_roles:
|
|
188
|
+
for agent in self.agent_by_role.get(role, []):
|
|
189
|
+
agent_task = asyncio.create_task(
|
|
190
|
+
agent.execute_task(
|
|
191
|
+
{
|
|
192
|
+
"type": task_type,
|
|
193
|
+
"context": context,
|
|
194
|
+
"shared_context": self.shared_context,
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
tasks.append((agent.id, agent_task))
|
|
199
|
+
|
|
200
|
+
# Wait for all agents to complete
|
|
201
|
+
for agent_id, task in tasks:
|
|
202
|
+
try:
|
|
203
|
+
result = await asyncio.wait_for(task, timeout=300)
|
|
204
|
+
results["agent_responses"][agent_id] = result
|
|
205
|
+
except asyncio.TimeoutError:
|
|
206
|
+
results["agent_responses"][agent_id] = {"error": "timeout"}
|
|
207
|
+
|
|
208
|
+
# Agents should have communicated with each other during execution
|
|
209
|
+
# Collect their findings from shared context
|
|
210
|
+
results["shared_findings"] = self.shared_context
|
|
211
|
+
results["completed"] = datetime.now().isoformat()
|
|
212
|
+
|
|
213
|
+
return results
|
|
214
|
+
|
|
215
|
+
async def facilitate_conversation(
|
|
216
|
+
self, topic: str, participants: List[str], rounds: int = 3
|
|
217
|
+
) -> List[AgentMessage]:
|
|
218
|
+
"""
|
|
219
|
+
Facilitate a multi-round conversation between agents
|
|
220
|
+
Similar to Clawed/Moltbot group discussions
|
|
221
|
+
"""
|
|
222
|
+
conversation = []
|
|
223
|
+
|
|
224
|
+
for round_num in range(rounds):
|
|
225
|
+
logger.info(f"[Orchestrator] Conversation round {round_num + 1}/{rounds}")
|
|
226
|
+
|
|
227
|
+
for participant_id in participants:
|
|
228
|
+
if participant_id in self.agents:
|
|
229
|
+
agent = self.agents[participant_id]
|
|
230
|
+
|
|
231
|
+
# Request agent's thoughts on topic
|
|
232
|
+
msg = AgentMessage(
|
|
233
|
+
sender="orchestrator",
|
|
234
|
+
recipient=f"{agent.name}[{agent.id}]",
|
|
235
|
+
msg_type="conversation_round",
|
|
236
|
+
content=f"Round {round_num + 1}: Share your thoughts on {topic}",
|
|
237
|
+
context={
|
|
238
|
+
"round": round_num + 1,
|
|
239
|
+
"topic": topic,
|
|
240
|
+
"conversation_so_far": [m.to_dict() for m in conversation],
|
|
241
|
+
"shared_context": self.shared_context,
|
|
242
|
+
},
|
|
243
|
+
requires_response=True,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
await agent.receive_message(msg)
|
|
247
|
+
|
|
248
|
+
return conversation
|
|
249
|
+
|
|
250
|
+
async def start_all(self):
|
|
251
|
+
"""Start all registered agents"""
|
|
252
|
+
self.running = True
|
|
253
|
+
for agent in self.agents.values():
|
|
254
|
+
await agent.start()
|
|
255
|
+
|
|
256
|
+
async def stop_all(self):
|
|
257
|
+
"""Stop all agents gracefully"""
|
|
258
|
+
self.running = False
|
|
259
|
+
for agent in self.agents.values():
|
|
260
|
+
await agent.stop()
|
|
261
|
+
|
|
262
|
+
async def execute_post_scan_workflow(
|
|
263
|
+
self, target: str, scan_results: Dict[str, Any]
|
|
264
|
+
) -> Dict[str, Any]:
|
|
265
|
+
"""
|
|
266
|
+
Execute the complete post-scan pentester workflow
|
|
267
|
+
This runs automatically after every scan to ensure professional standards
|
|
268
|
+
|
|
269
|
+
Phases:
|
|
270
|
+
1. Manual Verification (false positive elimination)
|
|
271
|
+
2. Vulnerability Validation
|
|
272
|
+
3. Exploitation Attempts
|
|
273
|
+
4. Post-Exploitation (privilege escalation, lateral movement)
|
|
274
|
+
5. Evidence Collection
|
|
275
|
+
6. Loot Documentation
|
|
276
|
+
7. Cleanup & Restoration
|
|
277
|
+
8. Report Preparation
|
|
278
|
+
"""
|
|
279
|
+
from .post_scan_agent import PostScanAgent
|
|
280
|
+
|
|
281
|
+
logger.info(f"[Orchestrator] Starting post-scan workflow for {target}")
|
|
282
|
+
print(f"\n[Post-Scan Workflow] Initiating professional pentest follow-up...")
|
|
283
|
+
|
|
284
|
+
# Create and run post-scan agent
|
|
285
|
+
post_scan_agent = PostScanAgent()
|
|
286
|
+
|
|
287
|
+
# Extract findings from scan results
|
|
288
|
+
findings = scan_results.get("findings", [])
|
|
289
|
+
if not findings:
|
|
290
|
+
# Create sample findings if none exist
|
|
291
|
+
findings = self._generate_sample_findings(target)
|
|
292
|
+
|
|
293
|
+
# Execute the workflow
|
|
294
|
+
results = await post_scan_agent.run(target, findings)
|
|
295
|
+
|
|
296
|
+
# Store in shared context
|
|
297
|
+
await self.update_shared_context(f"post_scan_{target}", results, "orchestrator")
|
|
298
|
+
|
|
299
|
+
logger.info(f"[Orchestrator] Post-scan workflow complete for {target}")
|
|
300
|
+
return results
|
|
301
|
+
|
|
302
|
+
def _generate_sample_findings(self, target: str) -> List[Dict]:
|
|
303
|
+
"""Generate sample findings for demonstration"""
|
|
304
|
+
return [
|
|
305
|
+
{
|
|
306
|
+
"id": "CVE-2021-44228",
|
|
307
|
+
"title": "Log4j Remote Code Execution",
|
|
308
|
+
"severity": "critical",
|
|
309
|
+
"cvss_score": 10.0,
|
|
310
|
+
"description": "Log4Shell vulnerability allows RCE",
|
|
311
|
+
"port": 8080,
|
|
312
|
+
"service": "http",
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"id": "WEAK_SSH",
|
|
316
|
+
"title": "SSH Weak Cipher Suites",
|
|
317
|
+
"severity": "medium",
|
|
318
|
+
"cvss_score": 5.3,
|
|
319
|
+
"description": "SSH supports weak ciphers",
|
|
320
|
+
"port": 22,
|
|
321
|
+
"service": "ssh",
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
"id": "DEFAULT_CREDS",
|
|
325
|
+
"title": "Default Credentials Detected",
|
|
326
|
+
"severity": "high",
|
|
327
|
+
"cvss_score": 8.1,
|
|
328
|
+
"description": "Default admin/admin credentials work",
|
|
329
|
+
"port": 80,
|
|
330
|
+
"service": "http",
|
|
331
|
+
},
|
|
332
|
+
]
|
|
333
|
+
|
|
334
|
+
def get_system_status(self) -> Dict:
|
|
335
|
+
"""Get status of entire multi-agent system"""
|
|
336
|
+
return {
|
|
337
|
+
"agents": {
|
|
338
|
+
agent_id: agent.get_status() for agent_id, agent in self.agents.items()
|
|
339
|
+
},
|
|
340
|
+
"shared_context_keys": list(self.shared_context.keys()),
|
|
341
|
+
"message_count": len(self.message_history),
|
|
342
|
+
"active_research": list(self.research_coordination.keys()),
|
|
343
|
+
"role_distribution": {
|
|
344
|
+
role.value: len(agents) for role, agents in self.agent_by_role.items()
|
|
345
|
+
},
|
|
346
|
+
}
|