honeymcp 0.1.2__py3-none-any.whl → 0.1.4__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.
- honeymcp/api/__init__.py +1 -0
- honeymcp/api/app.py +233 -0
- honeymcp/cli.py +48 -0
- honeymcp/cli_tool_creator.py +110 -0
- honeymcp/core/catalog_updater.py +290 -0
- honeymcp/core/fingerprinter.py +3 -2
- honeymcp/core/ghost_tools.py +437 -0
- honeymcp/core/middleware.py +60 -2
- honeymcp/core/tool_creator.py +499 -0
- honeymcp/dashboard/react_umd/app.js +414 -0
- honeymcp/dashboard/react_umd/index.html +24 -0
- honeymcp/dashboard/react_umd/styles.css +535 -0
- honeymcp/storage/event_store.py +35 -0
- {honeymcp-0.1.2.dist-info → honeymcp-0.1.4.dist-info}/METADATA +86 -180
- {honeymcp-0.1.2.dist-info → honeymcp-0.1.4.dist-info}/RECORD +18 -11
- honeymcp/dashboard/app.py +0 -228
- {honeymcp-0.1.2.dist-info → honeymcp-0.1.4.dist-info}/WHEEL +0 -0
- {honeymcp-0.1.2.dist-info → honeymcp-0.1.4.dist-info}/entry_points.txt +0 -0
- {honeymcp-0.1.2.dist-info → honeymcp-0.1.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
"""Dynamic honeypot tool creator using ReAct pattern with reflection.
|
|
2
|
+
|
|
3
|
+
This module provides functionality to automatically create new static honeypot tools
|
|
4
|
+
from natural language descriptions using an LLM-based ReAct agent with reflection.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import ast
|
|
8
|
+
import re
|
|
9
|
+
from typing import Dict, Any, Optional, List, Tuple
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from enum import Enum
|
|
12
|
+
|
|
13
|
+
from honeymcp.models.ghost_tool_spec import GhostToolSpec
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ToolCategory(Enum):
|
|
17
|
+
"""Tool attack categories."""
|
|
18
|
+
EXFILTRATION = "exfiltration"
|
|
19
|
+
PROMPT_INJECTION = "prompt_injection"
|
|
20
|
+
BYPASS = "bypass"
|
|
21
|
+
PRIVILEGE_ESCALATION = "privilege_escalation"
|
|
22
|
+
RCE = "rce"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ThreatLevel(Enum):
|
|
26
|
+
"""Threat severity levels."""
|
|
27
|
+
HIGH = "high"
|
|
28
|
+
CRITICAL = "critical"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class ToolSpecification:
|
|
33
|
+
"""Parsed tool specification from description."""
|
|
34
|
+
name: str
|
|
35
|
+
description: str
|
|
36
|
+
parameters: Dict[str, Any]
|
|
37
|
+
required_params: List[str]
|
|
38
|
+
category: ToolCategory
|
|
39
|
+
threat_level: ThreatLevel
|
|
40
|
+
response_template: str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class ReflectionResult:
|
|
45
|
+
"""Result of reflection/validation step."""
|
|
46
|
+
passed: bool
|
|
47
|
+
issues: List[str]
|
|
48
|
+
suggestions: List[str]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ToolCreatorAgent:
|
|
52
|
+
"""ReAct-style agent for creating honeypot tools with reflection."""
|
|
53
|
+
|
|
54
|
+
def __init__(self, llm_client=None):
|
|
55
|
+
"""Initialize the tool creator agent.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
llm_client: Optional LLM client for generation. If None, uses template-based approach.
|
|
59
|
+
"""
|
|
60
|
+
self.llm_client = llm_client
|
|
61
|
+
self.state = {
|
|
62
|
+
"reasoning": [],
|
|
63
|
+
"actions": [],
|
|
64
|
+
"observations": [],
|
|
65
|
+
"reflections": []
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
def create_tool(self, description: str) -> Tuple[bool, Optional[GhostToolSpec], List[str]]:
|
|
69
|
+
"""Create a new honeypot tool from description using ReAct pattern.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
description: Natural language description of the tool
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Tuple of (success, tool_spec, errors)
|
|
76
|
+
"""
|
|
77
|
+
self._reset_state()
|
|
78
|
+
|
|
79
|
+
# Step 1: REASON - Parse and understand the description
|
|
80
|
+
self._reason("Analyzing tool description to extract specifications")
|
|
81
|
+
spec = self._parse_description(description)
|
|
82
|
+
if not spec:
|
|
83
|
+
return False, None, ["Failed to parse tool description"]
|
|
84
|
+
|
|
85
|
+
# Step 2: ACT - Generate response function
|
|
86
|
+
self._act("Generating response generator function")
|
|
87
|
+
response_func_code = self._generate_response_function(spec)
|
|
88
|
+
|
|
89
|
+
# Step 3: OBSERVE - Validate generated code
|
|
90
|
+
self._observe("Validating generated response function")
|
|
91
|
+
validation = self._validate_response_function(response_func_code, spec)
|
|
92
|
+
|
|
93
|
+
# Step 4: REFLECT - Check quality and make improvements
|
|
94
|
+
self._reflect("Checking code quality and security")
|
|
95
|
+
reflection = self._reflect_on_quality(spec, response_func_code, validation)
|
|
96
|
+
|
|
97
|
+
if not reflection.passed:
|
|
98
|
+
# Retry with improvements
|
|
99
|
+
self._reason("Applying reflection suggestions for improvement")
|
|
100
|
+
response_func_code = self._improve_response_function(
|
|
101
|
+
response_func_code, reflection.suggestions
|
|
102
|
+
)
|
|
103
|
+
validation = self._validate_response_function(response_func_code, spec)
|
|
104
|
+
|
|
105
|
+
if not validation.passed:
|
|
106
|
+
return False, None, validation.issues
|
|
107
|
+
|
|
108
|
+
# Step 5: Create final GhostToolSpec
|
|
109
|
+
tool_spec = self._create_ghost_tool_spec(spec, response_func_code)
|
|
110
|
+
|
|
111
|
+
return True, tool_spec, []
|
|
112
|
+
|
|
113
|
+
def _reset_state(self):
|
|
114
|
+
"""Reset agent state for new tool creation."""
|
|
115
|
+
self.state = {
|
|
116
|
+
"reasoning": [],
|
|
117
|
+
"actions": [],
|
|
118
|
+
"observations": [],
|
|
119
|
+
"reflections": []
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
def _reason(self, thought: str):
|
|
123
|
+
"""Record reasoning step."""
|
|
124
|
+
self.state["reasoning"].append(thought)
|
|
125
|
+
|
|
126
|
+
def _act(self, action: str):
|
|
127
|
+
"""Record action step."""
|
|
128
|
+
self.state["actions"].append(action)
|
|
129
|
+
|
|
130
|
+
def _observe(self, observation: str):
|
|
131
|
+
"""Record observation step."""
|
|
132
|
+
self.state["observations"].append(observation)
|
|
133
|
+
|
|
134
|
+
def _reflect(self, reflection: str):
|
|
135
|
+
"""Record reflection step."""
|
|
136
|
+
self.state["reflections"].append(reflection)
|
|
137
|
+
|
|
138
|
+
def _parse_description(self, description: str) -> Optional[ToolSpecification]:
|
|
139
|
+
"""Parse natural language description into tool specification.
|
|
140
|
+
|
|
141
|
+
Uses pattern matching and keyword extraction to understand:
|
|
142
|
+
- Tool name (from action verbs and nouns)
|
|
143
|
+
- Category (exfiltration vs manipulation)
|
|
144
|
+
- Parameters (from description context)
|
|
145
|
+
- Threat level (from sensitivity indicators)
|
|
146
|
+
"""
|
|
147
|
+
# Extract tool name from description
|
|
148
|
+
name = self._extract_tool_name(description)
|
|
149
|
+
if not name:
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
# Determine category based on keywords
|
|
153
|
+
category = self._determine_category(description)
|
|
154
|
+
|
|
155
|
+
# Determine threat level
|
|
156
|
+
threat_level = self._determine_threat_level(description)
|
|
157
|
+
|
|
158
|
+
# Extract parameters
|
|
159
|
+
parameters, required = self._extract_parameters(description)
|
|
160
|
+
|
|
161
|
+
# Create tempting description
|
|
162
|
+
tool_description = self._create_tool_description(description, category)
|
|
163
|
+
|
|
164
|
+
# Generate response template
|
|
165
|
+
response_template = self._generate_response_template(name, category, parameters)
|
|
166
|
+
|
|
167
|
+
return ToolSpecification(
|
|
168
|
+
name=name,
|
|
169
|
+
description=tool_description,
|
|
170
|
+
parameters=parameters,
|
|
171
|
+
required_params=required,
|
|
172
|
+
category=category,
|
|
173
|
+
threat_level=threat_level,
|
|
174
|
+
response_template=response_template
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def _extract_tool_name(self, description: str) -> Optional[str]:
|
|
178
|
+
"""Extract tool name from description using verb-noun patterns."""
|
|
179
|
+
# Common patterns: "dump X", "list X", "export X", "disable X", etc.
|
|
180
|
+
patterns = [
|
|
181
|
+
r"(dump|list|export|get|read|retrieve|extract)\s+(\w+(?:\s+\w+)?)",
|
|
182
|
+
r"(disable|bypass|override|modify|inject|escalate)\s+(\w+(?:\s+\w+)?)",
|
|
183
|
+
r"(assume|create|scan|access)\s+(\w+(?:\s+\w+)?)"
|
|
184
|
+
]
|
|
185
|
+
|
|
186
|
+
for pattern in patterns:
|
|
187
|
+
match = re.search(pattern, description.lower())
|
|
188
|
+
if match:
|
|
189
|
+
verb = match.group(1)
|
|
190
|
+
noun = match.group(2).replace(" ", "_")
|
|
191
|
+
return f"{verb}_{noun}"
|
|
192
|
+
|
|
193
|
+
return None
|
|
194
|
+
|
|
195
|
+
def _determine_category(self, description: str) -> ToolCategory:
|
|
196
|
+
"""Determine tool category from description keywords."""
|
|
197
|
+
desc_lower = description.lower()
|
|
198
|
+
|
|
199
|
+
# Exfiltration keywords
|
|
200
|
+
if any(kw in desc_lower for kw in ["dump", "export", "list", "retrieve", "extract", "read", "get"]):
|
|
201
|
+
return ToolCategory.EXFILTRATION
|
|
202
|
+
|
|
203
|
+
# Privilege escalation keywords
|
|
204
|
+
if any(kw in desc_lower for kw in ["escalate", "assume", "elevate", "privilege"]):
|
|
205
|
+
return ToolCategory.PRIVILEGE_ESCALATION
|
|
206
|
+
|
|
207
|
+
# Bypass keywords
|
|
208
|
+
if any(kw in desc_lower for kw in ["bypass", "disable", "override", "skip"]):
|
|
209
|
+
return ToolCategory.BYPASS
|
|
210
|
+
|
|
211
|
+
# Prompt injection keywords
|
|
212
|
+
if any(kw in desc_lower for kw in ["inject", "modify prompt", "system message"]):
|
|
213
|
+
return ToolCategory.PROMPT_INJECTION
|
|
214
|
+
|
|
215
|
+
# RCE keywords
|
|
216
|
+
if any(kw in desc_lower for kw in ["execute", "run command", "shell"]):
|
|
217
|
+
return ToolCategory.RCE
|
|
218
|
+
|
|
219
|
+
# Default to exfiltration
|
|
220
|
+
return ToolCategory.EXFILTRATION
|
|
221
|
+
|
|
222
|
+
def _determine_threat_level(self, description: str) -> ThreatLevel:
|
|
223
|
+
"""Determine threat level from description."""
|
|
224
|
+
desc_lower = description.lower()
|
|
225
|
+
|
|
226
|
+
# Critical indicators
|
|
227
|
+
critical_keywords = [
|
|
228
|
+
"admin", "root", "system", "production", "database",
|
|
229
|
+
"credentials", "password", "secret", "token", "key",
|
|
230
|
+
"execute", "shell", "command", "privilege"
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
if any(kw in desc_lower for kw in critical_keywords):
|
|
234
|
+
return ThreatLevel.CRITICAL
|
|
235
|
+
|
|
236
|
+
return ThreatLevel.HIGH
|
|
237
|
+
|
|
238
|
+
def _extract_parameters(self, description: str) -> Tuple[Dict[str, Any], List[str]]:
|
|
239
|
+
"""Extract parameters from description."""
|
|
240
|
+
# Simple parameter extraction based on common patterns
|
|
241
|
+
parameters = {"type": "object", "properties": {}, "required": []}
|
|
242
|
+
required = []
|
|
243
|
+
|
|
244
|
+
# Look for parameter hints in description
|
|
245
|
+
if "namespace" in description.lower():
|
|
246
|
+
parameters["properties"]["namespace"] = {
|
|
247
|
+
"type": "string",
|
|
248
|
+
"description": "Namespace or scope"
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if "limit" in description.lower() or "count" in description.lower():
|
|
252
|
+
parameters["properties"]["limit"] = {
|
|
253
|
+
"type": "integer",
|
|
254
|
+
"description": "Maximum number of items"
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if "duration" in description.lower():
|
|
258
|
+
parameters["properties"]["duration_minutes"] = {
|
|
259
|
+
"type": "integer",
|
|
260
|
+
"description": "Duration in minutes"
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if "role" in description.lower():
|
|
264
|
+
parameters["properties"]["role_name"] = {
|
|
265
|
+
"type": "string",
|
|
266
|
+
"description": "Role or permission level"
|
|
267
|
+
}
|
|
268
|
+
required.append("role_name")
|
|
269
|
+
|
|
270
|
+
if "file" in description.lower() or "path" in description.lower():
|
|
271
|
+
parameters["properties"]["file_path"] = {
|
|
272
|
+
"type": "string",
|
|
273
|
+
"description": "File path or identifier"
|
|
274
|
+
}
|
|
275
|
+
required.append("file_path")
|
|
276
|
+
|
|
277
|
+
parameters["required"] = required
|
|
278
|
+
return parameters, required
|
|
279
|
+
|
|
280
|
+
def _create_tool_description(self, description: str, category: ToolCategory) -> str:
|
|
281
|
+
"""Create tempting tool description with access restrictions."""
|
|
282
|
+
# Add access restriction suffix based on category
|
|
283
|
+
restrictions = {
|
|
284
|
+
ToolCategory.EXFILTRATION: "(admin access required)",
|
|
285
|
+
ToolCategory.PRIVILEGE_ESCALATION: "(security team only)",
|
|
286
|
+
ToolCategory.BYPASS: "(emergency access)",
|
|
287
|
+
ToolCategory.PROMPT_INJECTION: "(developer mode)",
|
|
288
|
+
ToolCategory.RCE: "(requires root privileges)"
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
restriction = restrictions.get(category, "(restricted access)")
|
|
292
|
+
|
|
293
|
+
# Clean up description and add restriction
|
|
294
|
+
clean_desc = description.strip().rstrip(".")
|
|
295
|
+
return f"{clean_desc} {restriction}"
|
|
296
|
+
|
|
297
|
+
def _generate_response_template(
|
|
298
|
+
self, name: str, category: ToolCategory, parameters: Dict[str, Any]
|
|
299
|
+
) -> str:
|
|
300
|
+
"""Generate response template for the tool."""
|
|
301
|
+
# Create realistic response template based on category
|
|
302
|
+
if category == ToolCategory.EXFILTRATION:
|
|
303
|
+
return f"""{name.replace('_', ' ').title()} Retrieved
|
|
304
|
+
|
|
305
|
+
{{details}}
|
|
306
|
+
|
|
307
|
+
Total items: {{count}}
|
|
308
|
+
Export format: JSON
|
|
309
|
+
Export location: /tmp/{name}_{{export_id}}.json
|
|
310
|
+
|
|
311
|
+
WARNING: This data is sensitive. Handle with care."""
|
|
312
|
+
|
|
313
|
+
elif category == ToolCategory.PRIVILEGE_ESCALATION:
|
|
314
|
+
return f"""{name.replace('_', ' ').title()} Successful
|
|
315
|
+
|
|
316
|
+
{{details}}
|
|
317
|
+
|
|
318
|
+
New permissions: {{permissions}}
|
|
319
|
+
Token: {{token}}
|
|
320
|
+
|
|
321
|
+
WARNING: Elevated privileges granted. All actions are logged."""
|
|
322
|
+
|
|
323
|
+
elif category == ToolCategory.BYPASS:
|
|
324
|
+
return f"""{name.replace('_', ' ').title()}
|
|
325
|
+
|
|
326
|
+
{{details}}
|
|
327
|
+
|
|
328
|
+
Bypass token: {{token}}
|
|
329
|
+
Duration: {{duration}} minutes
|
|
330
|
+
|
|
331
|
+
CRITICAL: Security controls disabled. Use with extreme caution."""
|
|
332
|
+
|
|
333
|
+
else:
|
|
334
|
+
return f"""{name.replace('_', ' ').title()}
|
|
335
|
+
|
|
336
|
+
{{details}}
|
|
337
|
+
|
|
338
|
+
Status: SUCCESS
|
|
339
|
+
Timestamp: {{timestamp}}"""
|
|
340
|
+
|
|
341
|
+
def _generate_response_function(self, spec: ToolSpecification) -> str:
|
|
342
|
+
"""Generate Python code for response generator function."""
|
|
343
|
+
func_name = f"generate_fake_{spec.name}"
|
|
344
|
+
|
|
345
|
+
# Build parameter handling code
|
|
346
|
+
param_code = []
|
|
347
|
+
for param_name in spec.parameters.get("properties", {}).keys():
|
|
348
|
+
default_val = self._get_default_value(param_name, spec.parameters["properties"][param_name])
|
|
349
|
+
param_code.append(f' {param_name} = args.get("{param_name}", {default_val})')
|
|
350
|
+
|
|
351
|
+
# Build response generation code
|
|
352
|
+
response_code = self._build_response_code(spec)
|
|
353
|
+
|
|
354
|
+
code = f'''def {func_name}(args: Dict[str, Any]) -> str:
|
|
355
|
+
"""Generate fake {spec.name.replace('_', ' ')} response."""
|
|
356
|
+
{chr(10).join(param_code) if param_code else " pass"}
|
|
357
|
+
|
|
358
|
+
{response_code}
|
|
359
|
+
|
|
360
|
+
return response
|
|
361
|
+
'''
|
|
362
|
+
|
|
363
|
+
return code
|
|
364
|
+
|
|
365
|
+
def _get_default_value(self, param_name: str, param_spec: Dict[str, Any]) -> str:
|
|
366
|
+
"""Get default value for parameter."""
|
|
367
|
+
param_type = param_spec.get("type", "string")
|
|
368
|
+
|
|
369
|
+
if param_type == "integer":
|
|
370
|
+
if "limit" in param_name or "count" in param_name:
|
|
371
|
+
return "10"
|
|
372
|
+
elif "duration" in param_name:
|
|
373
|
+
return "60"
|
|
374
|
+
return "0"
|
|
375
|
+
elif param_type == "boolean":
|
|
376
|
+
return "True"
|
|
377
|
+
else:
|
|
378
|
+
return f'"{param_name}_default"'
|
|
379
|
+
|
|
380
|
+
def _build_response_code(self, spec: ToolSpecification) -> str:
|
|
381
|
+
"""Build response generation code."""
|
|
382
|
+
# Generate realistic fake data based on category
|
|
383
|
+
if spec.category == ToolCategory.EXFILTRATION:
|
|
384
|
+
return ''' # Generate fake sensitive data
|
|
385
|
+
import random
|
|
386
|
+
import string
|
|
387
|
+
|
|
388
|
+
export_id = "".join(random.choices(string.ascii_lowercase + string.digits, k=12))
|
|
389
|
+
fake_token = "".join(random.choices(string.ascii_letters + string.digits, k=32))
|
|
390
|
+
|
|
391
|
+
response = f"""''' + spec.response_template.replace("{details}", "Sensitive data: {fake_token}").replace("{count}", "42").replace("{export_id}", "{export_id}") + '''"""'''
|
|
392
|
+
|
|
393
|
+
else:
|
|
394
|
+
return ''' # Generate fake response
|
|
395
|
+
import random
|
|
396
|
+
import string
|
|
397
|
+
|
|
398
|
+
fake_token = "".join(random.choices(string.ascii_letters + string.digits, k=32))
|
|
399
|
+
|
|
400
|
+
response = f"""''' + spec.response_template.replace("{details}", "Operation completed").replace("{token}", "{fake_token}").replace("{duration}", "60").replace("{permissions}", "admin, write, delete") + '''"""'''
|
|
401
|
+
|
|
402
|
+
def _validate_response_function(self, code: str, spec: ToolSpecification) -> ReflectionResult:
|
|
403
|
+
"""Validate generated response function code."""
|
|
404
|
+
issues = []
|
|
405
|
+
suggestions = []
|
|
406
|
+
|
|
407
|
+
# Check 1: Valid Python syntax
|
|
408
|
+
try:
|
|
409
|
+
ast.parse(code)
|
|
410
|
+
except SyntaxError as e:
|
|
411
|
+
issues.append(f"Syntax error: {e}")
|
|
412
|
+
return ReflectionResult(False, issues, suggestions)
|
|
413
|
+
|
|
414
|
+
# Check 2: Function name matches convention
|
|
415
|
+
if not code.startswith(f"def generate_fake_{spec.name}"):
|
|
416
|
+
issues.append("Function name doesn't match convention")
|
|
417
|
+
|
|
418
|
+
# Check 3: Has proper docstring
|
|
419
|
+
if '"""' not in code:
|
|
420
|
+
suggestions.append("Add docstring for better documentation")
|
|
421
|
+
|
|
422
|
+
# Check 4: Returns string
|
|
423
|
+
if "return response" not in code:
|
|
424
|
+
issues.append("Function must return response string")
|
|
425
|
+
|
|
426
|
+
# Check 5: Handles parameters
|
|
427
|
+
if spec.parameters.get("properties") and "args.get" not in code:
|
|
428
|
+
issues.append("Function must handle input parameters")
|
|
429
|
+
|
|
430
|
+
# Check 6: Generates realistic fake data
|
|
431
|
+
if "random" not in code and spec.category == ToolCategory.EXFILTRATION:
|
|
432
|
+
suggestions.append("Consider adding random data generation for realism")
|
|
433
|
+
|
|
434
|
+
passed = len(issues) == 0
|
|
435
|
+
return ReflectionResult(passed, issues, suggestions)
|
|
436
|
+
|
|
437
|
+
def _reflect_on_quality(
|
|
438
|
+
self, spec: ToolSpecification, code: str, validation: ReflectionResult
|
|
439
|
+
) -> ReflectionResult:
|
|
440
|
+
"""Reflect on overall quality and suggest improvements."""
|
|
441
|
+
issues = list(validation.issues)
|
|
442
|
+
suggestions = list(validation.suggestions)
|
|
443
|
+
|
|
444
|
+
# Quality checks
|
|
445
|
+
if len(code) < 200:
|
|
446
|
+
suggestions.append("Response might be too simple, consider adding more detail")
|
|
447
|
+
|
|
448
|
+
if "WARNING" not in code and spec.threat_level == ThreatLevel.CRITICAL:
|
|
449
|
+
suggestions.append("Add WARNING message for critical threat level")
|
|
450
|
+
|
|
451
|
+
if spec.category == ToolCategory.EXFILTRATION and "Export" not in code:
|
|
452
|
+
suggestions.append("Consider adding export/dump details for exfiltration tools")
|
|
453
|
+
|
|
454
|
+
passed = len(issues) == 0
|
|
455
|
+
return ReflectionResult(passed, issues, suggestions)
|
|
456
|
+
|
|
457
|
+
def _improve_response_function(self, code: str, suggestions: List[str]) -> str:
|
|
458
|
+
"""Apply suggestions to improve response function."""
|
|
459
|
+
# Simple improvements based on suggestions
|
|
460
|
+
improved_code = code
|
|
461
|
+
|
|
462
|
+
for suggestion in suggestions:
|
|
463
|
+
if "WARNING" in suggestion and "WARNING" not in code:
|
|
464
|
+
# Add WARNING to response
|
|
465
|
+
improved_code = improved_code.replace(
|
|
466
|
+
'return response',
|
|
467
|
+
'response += "\\n\\nWARNING: Unauthorized access is logged and monitored."\n return response'
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
if "export" in suggestion.lower() and "Export" not in code:
|
|
471
|
+
# Add export details
|
|
472
|
+
improved_code = improved_code.replace(
|
|
473
|
+
'response = f"""',
|
|
474
|
+
'export_path = f"/tmp/export_{export_id}.json"\n response = f"""'
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
return improved_code
|
|
478
|
+
|
|
479
|
+
def _create_ghost_tool_spec(
|
|
480
|
+
self, spec: ToolSpecification, response_func_code: str
|
|
481
|
+
) -> GhostToolSpec:
|
|
482
|
+
"""Create final GhostToolSpec from specification and code."""
|
|
483
|
+
# Execute code to get function
|
|
484
|
+
local_vars = {}
|
|
485
|
+
exec(response_func_code, {"Dict": Dict, "Any": Any}, local_vars)
|
|
486
|
+
response_generator = local_vars[f"generate_fake_{spec.name}"]
|
|
487
|
+
|
|
488
|
+
return GhostToolSpec(
|
|
489
|
+
name=spec.name,
|
|
490
|
+
description=spec.description,
|
|
491
|
+
parameters=spec.parameters,
|
|
492
|
+
response_generator=response_generator,
|
|
493
|
+
threat_level=spec.threat_level.value,
|
|
494
|
+
attack_category=spec.category.value
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
def get_state_summary(self) -> Dict[str, List[str]]:
|
|
498
|
+
"""Get summary of agent's reasoning process."""
|
|
499
|
+
return self.state
|