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
autonomous/react.py
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ReAct (Reasoning + Acting) Pattern Implementation
|
|
3
|
+
|
|
4
|
+
The ReAct loop allows the agent to:
|
|
5
|
+
1. Think about the current situation (Reasoning)
|
|
6
|
+
2. Decide what to do (Action)
|
|
7
|
+
3. Observe the result (Observation)
|
|
8
|
+
4. Repeat until goal is achieved
|
|
9
|
+
|
|
10
|
+
Based on: https://arxiv.org/abs/2210.03629
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import asyncio
|
|
14
|
+
import json
|
|
15
|
+
import uuid
|
|
16
|
+
from abc import ABC, abstractmethod
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from enum import Enum, auto
|
|
20
|
+
from typing import Any, Callable, Dict, List, Optional, TypeVar, Generic
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ActionType(Enum):
|
|
24
|
+
"""Types of actions the agent can take."""
|
|
25
|
+
THINK = auto() # Internal reasoning
|
|
26
|
+
TOOL_CALL = auto() # Execute a security tool
|
|
27
|
+
SEARCH_MEMORY = auto() # Recall past information
|
|
28
|
+
ASK_HUMAN = auto() # Request clarification
|
|
29
|
+
REPORT = auto() # Deliver findings
|
|
30
|
+
TERMINATE = auto() # End the session
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class Thought:
|
|
35
|
+
"""A reasoning step in the agent's thought process."""
|
|
36
|
+
content: str
|
|
37
|
+
context: Dict[str, Any] = field(default_factory=dict)
|
|
38
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
39
|
+
step_number: int = 0
|
|
40
|
+
|
|
41
|
+
def to_dict(self) -> Dict:
|
|
42
|
+
return {
|
|
43
|
+
'type': 'thought',
|
|
44
|
+
'content': self.content,
|
|
45
|
+
'context': self.context,
|
|
46
|
+
'timestamp': self.timestamp.isoformat(),
|
|
47
|
+
'step': self.step_number
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class Action:
|
|
53
|
+
"""An action the agent decides to take."""
|
|
54
|
+
type: ActionType
|
|
55
|
+
tool_name: Optional[str] = None
|
|
56
|
+
parameters: Dict[str, Any] = field(default_factory=dict)
|
|
57
|
+
reasoning: str = ""
|
|
58
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
59
|
+
step_number: int = 0
|
|
60
|
+
|
|
61
|
+
def to_dict(self) -> Dict:
|
|
62
|
+
return {
|
|
63
|
+
'type': 'action',
|
|
64
|
+
'action_type': self.type.name,
|
|
65
|
+
'tool': self.tool_name,
|
|
66
|
+
'parameters': self.parameters,
|
|
67
|
+
'reasoning': self.reasoning,
|
|
68
|
+
'timestamp': self.timestamp.isoformat(),
|
|
69
|
+
'step': self.step_number
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class Observation:
|
|
75
|
+
"""The result of executing an action."""
|
|
76
|
+
action: Action
|
|
77
|
+
result: Any
|
|
78
|
+
success: bool = True
|
|
79
|
+
error_message: Optional[str] = None
|
|
80
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
81
|
+
step_number: int = 0
|
|
82
|
+
|
|
83
|
+
def to_dict(self) -> Dict:
|
|
84
|
+
return {
|
|
85
|
+
'type': 'observation',
|
|
86
|
+
'action': self.action.to_dict(),
|
|
87
|
+
'result': self.result,
|
|
88
|
+
'success': self.success,
|
|
89
|
+
'error': self.error_message,
|
|
90
|
+
'timestamp': self.timestamp.isoformat(),
|
|
91
|
+
'step': self.step_number
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class ReActLoop:
|
|
96
|
+
"""
|
|
97
|
+
ReAct Reasoning Loop implementation.
|
|
98
|
+
|
|
99
|
+
Continuously cycles through:
|
|
100
|
+
1. Reason about current state
|
|
101
|
+
2. Decide on action
|
|
102
|
+
3. Execute action
|
|
103
|
+
4. Observe result
|
|
104
|
+
5. Update state
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(
|
|
108
|
+
self,
|
|
109
|
+
llm_client,
|
|
110
|
+
tool_executor,
|
|
111
|
+
memory_manager,
|
|
112
|
+
max_iterations: int = 50,
|
|
113
|
+
human_in_the_loop: bool = False
|
|
114
|
+
):
|
|
115
|
+
self.llm = llm_client
|
|
116
|
+
self.tools = tool_executor
|
|
117
|
+
self.memory = memory_manager
|
|
118
|
+
self.max_iterations = max_iterations
|
|
119
|
+
self.human_in_the_loop = human_in_the_loop
|
|
120
|
+
|
|
121
|
+
self.history: List[Dict] = []
|
|
122
|
+
self.current_step: int = 0
|
|
123
|
+
self.goal: Optional[str] = None
|
|
124
|
+
|
|
125
|
+
async def run(self, goal: str, context: Optional[Dict] = None) -> Dict:
|
|
126
|
+
"""
|
|
127
|
+
Execute the ReAct loop until goal is achieved or max iterations reached.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
goal: The high-level objective
|
|
131
|
+
context: Additional context (target info, scope, etc.)
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Final result with findings and execution trace
|
|
135
|
+
"""
|
|
136
|
+
self.goal = goal
|
|
137
|
+
self.current_step = 0
|
|
138
|
+
self.history = []
|
|
139
|
+
|
|
140
|
+
# Initialize with goal in memory
|
|
141
|
+
await self.memory.add_goal(goal, context)
|
|
142
|
+
|
|
143
|
+
print(f"[ReAct] Starting autonomous execution for goal: {goal}")
|
|
144
|
+
|
|
145
|
+
while self.current_step < self.max_iterations:
|
|
146
|
+
self.current_step += 1
|
|
147
|
+
print(f"\n[ReAct] Step {self.current_step}/{self.max_iterations}")
|
|
148
|
+
|
|
149
|
+
# 1. REASON: Think about current state
|
|
150
|
+
thought = await self._reason()
|
|
151
|
+
self.history.append(thought.to_dict())
|
|
152
|
+
print(f"[Thought] {thought.content[:100]}...")
|
|
153
|
+
|
|
154
|
+
# 2. ACT: Decide on action
|
|
155
|
+
action = await self._decide_action(thought)
|
|
156
|
+
self.history.append(action.to_dict())
|
|
157
|
+
print(f"[Action] {action.type.name}" + (f" ({action.tool_name})" if action.tool_name else ""))
|
|
158
|
+
|
|
159
|
+
# Check for termination
|
|
160
|
+
if action.type == ActionType.TERMINATE:
|
|
161
|
+
print("[ReAct] Goal achieved or terminated")
|
|
162
|
+
break
|
|
163
|
+
|
|
164
|
+
# 3. EXECUTE: Perform the action
|
|
165
|
+
observation = await self._execute_action(action)
|
|
166
|
+
self.history.append(observation.to_dict())
|
|
167
|
+
|
|
168
|
+
if observation.success:
|
|
169
|
+
result_str = str(observation.result)[:100] if observation.result else "None"
|
|
170
|
+
print(f"[Observation] Success: {result_str}...")
|
|
171
|
+
else:
|
|
172
|
+
print(f"[Observation] Failed: {observation.error_message}")
|
|
173
|
+
|
|
174
|
+
# 4. LEARN: Update memory
|
|
175
|
+
await self.memory.add_experience(thought, action, observation)
|
|
176
|
+
|
|
177
|
+
# Human checkpoint if enabled
|
|
178
|
+
if self.human_in_the_loop and action.type in [ActionType.TOOL_CALL, ActionType.REPORT]:
|
|
179
|
+
approved = await self._human_approval(action)
|
|
180
|
+
if not approved:
|
|
181
|
+
print("[ReAct] Human rejected action, stopping")
|
|
182
|
+
break
|
|
183
|
+
|
|
184
|
+
# Compile final result
|
|
185
|
+
return self._compile_result()
|
|
186
|
+
|
|
187
|
+
async def _reason(self) -> Thought:
|
|
188
|
+
"""Generate a thought about the current situation."""
|
|
189
|
+
context = await self.memory.get_relevant_context(self.goal)
|
|
190
|
+
|
|
191
|
+
prompt = f"""You are an autonomous penetration testing agent.
|
|
192
|
+
|
|
193
|
+
Goal: {self.goal}
|
|
194
|
+
|
|
195
|
+
Current Context:
|
|
196
|
+
{json.dumps(context, indent=2)}
|
|
197
|
+
|
|
198
|
+
Execution History (last 5 steps):
|
|
199
|
+
{json.dumps(self.history[-5:], indent=2)}
|
|
200
|
+
|
|
201
|
+
Step {self.current_step}: What is your assessment of the current situation?
|
|
202
|
+
What have you learned? What should you do next?
|
|
203
|
+
|
|
204
|
+
Provide your reasoning:"""
|
|
205
|
+
|
|
206
|
+
response = await self.llm.generate(prompt)
|
|
207
|
+
|
|
208
|
+
return Thought(
|
|
209
|
+
content=response,
|
|
210
|
+
context={'goal': self.goal, 'step': self.current_step},
|
|
211
|
+
step_number=self.current_step
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
async def _decide_action(self, thought: Thought) -> Action:
|
|
215
|
+
"""Decide on the next action based on reasoning."""
|
|
216
|
+
available_tools = self.tools.get_available_tools()
|
|
217
|
+
|
|
218
|
+
prompt = f"""Based on your reasoning: "{thought.content}"
|
|
219
|
+
|
|
220
|
+
Available Tools:
|
|
221
|
+
{json.dumps(available_tools, indent=2)}
|
|
222
|
+
|
|
223
|
+
Decide on the next action. Respond in JSON format:
|
|
224
|
+
{{
|
|
225
|
+
"action_type": "TOOL_CALL|SEARCH_MEMORY|ASK_HUMAN|REPORT|TERMINATE",
|
|
226
|
+
"tool_name": "name_of_tool_if_applicable",
|
|
227
|
+
"parameters": {{}},
|
|
228
|
+
"reasoning": "why this action"
|
|
229
|
+
}}
|
|
230
|
+
|
|
231
|
+
Rules:
|
|
232
|
+
- Use TOOL_CALL to execute security tools (nmap, nuclei, etc.)
|
|
233
|
+
- Use SEARCH_MEMORY to recall past findings
|
|
234
|
+
- Use REPORT when you have findings to deliver
|
|
235
|
+
- Use TERMINATE when goal is achieved or stuck"""
|
|
236
|
+
|
|
237
|
+
response = await self.llm.generate(prompt)
|
|
238
|
+
|
|
239
|
+
try:
|
|
240
|
+
# Extract JSON from response
|
|
241
|
+
json_start = response.find('{')
|
|
242
|
+
json_end = response.rfind('}') + 1
|
|
243
|
+
if json_start >= 0 and json_end > json_start:
|
|
244
|
+
decision = json.loads(response[json_start:json_end])
|
|
245
|
+
else:
|
|
246
|
+
decision = json.loads(response)
|
|
247
|
+
|
|
248
|
+
action_type = ActionType[decision.get('action_type', 'THINK')]
|
|
249
|
+
|
|
250
|
+
return Action(
|
|
251
|
+
type=action_type,
|
|
252
|
+
tool_name=decision.get('tool_name'),
|
|
253
|
+
parameters=decision.get('parameters', {}),
|
|
254
|
+
reasoning=decision.get('reasoning', ''),
|
|
255
|
+
step_number=self.current_step
|
|
256
|
+
)
|
|
257
|
+
except (json.JSONDecodeError, KeyError) as e:
|
|
258
|
+
# Fallback to thinking if parsing fails
|
|
259
|
+
return Action(
|
|
260
|
+
type=ActionType.THINK,
|
|
261
|
+
reasoning=f"Failed to parse action: {str(e)}",
|
|
262
|
+
step_number=self.current_step
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
async def _execute_action(self, action: Action) -> Observation:
|
|
266
|
+
"""Execute the decided action."""
|
|
267
|
+
try:
|
|
268
|
+
if action.type == ActionType.TOOL_CALL and action.tool_name:
|
|
269
|
+
result = await self.tools.execute(
|
|
270
|
+
action.tool_name,
|
|
271
|
+
action.parameters
|
|
272
|
+
)
|
|
273
|
+
return Observation(
|
|
274
|
+
action=action,
|
|
275
|
+
result=result,
|
|
276
|
+
success=True,
|
|
277
|
+
step_number=self.current_step
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
elif action.type == ActionType.SEARCH_MEMORY:
|
|
281
|
+
query = action.parameters.get('query', self.goal)
|
|
282
|
+
results = await self.memory.search(query)
|
|
283
|
+
return Observation(
|
|
284
|
+
action=action,
|
|
285
|
+
result=results,
|
|
286
|
+
success=True,
|
|
287
|
+
step_number=self.current_step
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
elif action.type == ActionType.REPORT:
|
|
291
|
+
findings = await self.memory.get_findings()
|
|
292
|
+
return Observation(
|
|
293
|
+
action=action,
|
|
294
|
+
result={'findings': findings, 'ready': True},
|
|
295
|
+
success=True,
|
|
296
|
+
step_number=self.current_step
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
else:
|
|
300
|
+
return Observation(
|
|
301
|
+
action=action,
|
|
302
|
+
result=None,
|
|
303
|
+
success=True,
|
|
304
|
+
step_number=self.current_step
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
except Exception as e:
|
|
308
|
+
return Observation(
|
|
309
|
+
action=action,
|
|
310
|
+
result=None,
|
|
311
|
+
success=False,
|
|
312
|
+
error_message=str(e),
|
|
313
|
+
step_number=self.current_step
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
async def _human_approval(self, action: Action) -> bool:
|
|
317
|
+
"""Request human approval for critical actions."""
|
|
318
|
+
print(f"\n[Human Approval Required]")
|
|
319
|
+
print(f"Action: {action.type.name}")
|
|
320
|
+
print(f"Tool: {action.tool_name}")
|
|
321
|
+
print(f"Parameters: {action.parameters}")
|
|
322
|
+
print(f"Reasoning: {action.reasoning}")
|
|
323
|
+
|
|
324
|
+
# In real implementation, this would be a UI prompt
|
|
325
|
+
# For now, auto-approve in autonomous mode
|
|
326
|
+
return True
|
|
327
|
+
|
|
328
|
+
def _compile_result(self) -> Dict:
|
|
329
|
+
"""Compile the final execution result."""
|
|
330
|
+
findings = [h for h in self.history if h.get('type') == 'observation']
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
'goal': self.goal,
|
|
334
|
+
'completed': self.current_step < self.max_iterations,
|
|
335
|
+
'steps_taken': self.current_step,
|
|
336
|
+
'history': self.history,
|
|
337
|
+
'findings': findings,
|
|
338
|
+
'execution_trace': f"Completed in {self.current_step} steps"
|
|
339
|
+
}
|