hanuscode 1.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.
- hanus/__init__.py +5 -0
- hanus/__main__.py +10 -0
- hanus/action_handlers.py +76 -0
- hanus/action_parser.py +82 -0
- hanus/agent_runner.py +1445 -0
- hanus/analysis/__init__.py +5 -0
- hanus/analysis/debt.py +702 -0
- hanus/analysis/dependencies.py +475 -0
- hanus/cache/__init__.py +5 -0
- hanus/cache/response_cache.py +560 -0
- hanus/config.py +401 -0
- hanus/connectors/__init__.py +19 -0
- hanus/connectors/base.py +114 -0
- hanus/connectors/claude_connector.py +146 -0
- hanus/connectors/gemini_connector.py +141 -0
- hanus/connectors/glm_connector.py +160 -0
- hanus/connectors/ollama_connector.py +174 -0
- hanus/connectors/openai_connector.py +122 -0
- hanus/connectors/registry.py +26 -0
- hanus/context/__init__.py +7 -0
- hanus/context/manager.py +837 -0
- hanus/context/selective.py +626 -0
- hanus/error_recovery/__init__.py +5 -0
- hanus/error_recovery/auto_fix.py +605 -0
- hanus/hooks/__init__.py +5 -0
- hanus/hooks/manager.py +247 -0
- hanus/instincts/__init__.py +44 -0
- hanus/instincts/cli.py +372 -0
- hanus/instincts/detector.py +281 -0
- hanus/instincts/evolver.py +361 -0
- hanus/instincts/manager.py +343 -0
- hanus/instincts/types.py +253 -0
- hanus/logger.py +81 -0
- hanus/memory/__init__.py +8 -0
- hanus/memory/manager.py +265 -0
- hanus/memory/types.py +119 -0
- hanus/monitor.py +341 -0
- hanus/parallel/__init__.py +5 -0
- hanus/parallel/executor.py +300 -0
- hanus/permissions.py +182 -0
- hanus/plan/__init__.py +8 -0
- hanus/plan/mode.py +267 -0
- hanus/plan/models.py +152 -0
- hanus/plugin_manager.py +754 -0
- hanus/plugin_registry.py +391 -0
- hanus/plugins/__init__.py +1 -0
- hanus/plugins/arena.py +630 -0
- hanus/plugins/code_review.py +123 -0
- hanus/plugins/cortex.py +1750 -0
- hanus/plugins/deps_check.py +27 -0
- hanus/plugins/git_ops.py +33 -0
- hanus/plugins/metasploit.py +530 -0
- hanus/plugins/notes.py +583 -0
- hanus/plugins/search_code.py +59 -0
- hanus/plugins/searchsploit.py +495 -0
- hanus/plugins/strategist.py +175 -0
- hanus/plugins/webui.py +5200 -0
- hanus/profiles.py +479 -0
- hanus/profiles_builtin/__init__.py +0 -0
- hanus/profiles_builtin/architect/profile.yaml +12 -0
- hanus/profiles_builtin/architect/system_prompt.txt +71 -0
- hanus/profiles_builtin/deep/profile.yaml +12 -0
- hanus/profiles_builtin/deep/system_prompt.txt +66 -0
- hanus/profiles_builtin/developer/__init__.py +0 -0
- hanus/profiles_builtin/developer/profile.yaml +9 -0
- hanus/profiles_builtin/developer/system_prompt.txt +176 -0
- hanus/profiles_builtin/speed/profile.yaml +12 -0
- hanus/profiles_builtin/speed/system_prompt.txt +51 -0
- hanus/project_tools.py +177 -0
- hanus/query_engine.py +1594 -0
- hanus/rules/__init__.py +237 -0
- hanus/search/__init__.py +5 -0
- hanus/search/semantic.py +596 -0
- hanus/session_manager.py +547 -0
- hanus/skill_manager.py +702 -0
- hanus/skills/__init__.py +4 -0
- hanus/subagent/__init__.py +8 -0
- hanus/subagent/agents/__init__.py +253 -0
- hanus/subagent/manager.py +309 -0
- hanus/subagent/types.py +266 -0
- hanus/suggestions/__init__.py +5 -0
- hanus/suggestions/proactive.py +451 -0
- hanus/tasks/__init__.py +8 -0
- hanus/tasks/manager.py +330 -0
- hanus/tasks/models.py +106 -0
- hanus/terminal_prompt.py +166 -0
- hanus/tools.py +1849 -0
- hanus/ui.py +939 -0
- hanuscode-1.0.0.dist-info/METADATA +1151 -0
- hanuscode-1.0.0.dist-info/RECORD +93 -0
- hanuscode-1.0.0.dist-info/WHEEL +5 -0
- hanuscode-1.0.0.dist-info/entry_points.txt +2 -0
- hanuscode-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
# plugins/searchsploit.py — SearchSploit Integration
|
|
2
|
+
"""
|
|
3
|
+
Plugin para buscar y gestionar exploits desde Exploit-DB.
|
|
4
|
+
Permite buscar por término, CVE, ver detalles y preparar exploits.
|
|
5
|
+
|
|
6
|
+
Uso:
|
|
7
|
+
/searchsploit search <término> — Buscar exploits
|
|
8
|
+
/searchsploit cve <CVE> — Buscar por CVE (ej: 2021-44228)
|
|
9
|
+
/searchsploit view <id> — Ver detalles de un exploit
|
|
10
|
+
/searchsploit copy <id> [dest] — Copiar exploit a directorio
|
|
11
|
+
/searchsploit mirror <id> [dest] — Mirror exploit con archivos
|
|
12
|
+
/searchsploit nmap <xml> — Buscar exploits desde Nmap XML
|
|
13
|
+
/searchsploit update — Actualizar base de datos
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
import subprocess
|
|
17
|
+
import shlex
|
|
18
|
+
import re
|
|
19
|
+
import os
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import List, Dict, Optional, Tuple
|
|
22
|
+
|
|
23
|
+
NAME = "searchsploit"
|
|
24
|
+
DESCRIPTION = "Buscar exploits en Exploit-DB por término, CVE, etc."
|
|
25
|
+
USAGE = "<comando> [args...] | search <term> | cve <CVE> | view <id> | copy <id>"
|
|
26
|
+
AGENT_DOC = """
|
|
27
|
+
Plugin para buscar y gestionar exploits desde Exploit-DB usando SearchSploit.
|
|
28
|
+
|
|
29
|
+
Comandos disponibles:
|
|
30
|
+
- search <término> — Buscar exploits por término
|
|
31
|
+
- cve <CVE> — Buscar por CVE (ej: cve 2021-44228)
|
|
32
|
+
- view <id> — Ver detalles y ruta de un exploit
|
|
33
|
+
- copy <id> [dest] — Copiar exploit al directorio actual o destino
|
|
34
|
+
- mirror <id> [dest] — Copiar exploit y archivos relacionados
|
|
35
|
+
- nmap <archivo.xml> — Buscar exploits basado en scan Nmap
|
|
36
|
+
- update — Actualizar base de datos de exploits
|
|
37
|
+
|
|
38
|
+
Ejemplos:
|
|
39
|
+
<run_plugin name="searchsploit" args="search apache struts"/>
|
|
40
|
+
<run_plugin name="searchsploit" args="cve 2021-44228"/>
|
|
41
|
+
<run_plugin name="searchsploit" args="view 46570"/>
|
|
42
|
+
<run_plugin name="searchsploit" args="copy 46570 ./exploits/"/>
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _run_cmd(cmd: str, timeout: int = 60) -> Tuple[int, str, str]:
|
|
47
|
+
"""Ejecuta un comando y retorna (returncode, stdout, stderr)."""
|
|
48
|
+
try:
|
|
49
|
+
result = subprocess.run(
|
|
50
|
+
cmd,
|
|
51
|
+
shell=True,
|
|
52
|
+
capture_output=True,
|
|
53
|
+
text=True,
|
|
54
|
+
timeout=timeout
|
|
55
|
+
)
|
|
56
|
+
return result.returncode, result.stdout, result.stderr
|
|
57
|
+
except subprocess.TimeoutExpired:
|
|
58
|
+
return -1, "", "Timeout expirado"
|
|
59
|
+
except Exception as e:
|
|
60
|
+
return -1, "", str(e)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _parse_exploit_table(output: str) -> List[Dict]:
|
|
64
|
+
"""Parsea la salida de searchsploit en formato tabla."""
|
|
65
|
+
exploits = []
|
|
66
|
+
lines = output.strip().split('\n')
|
|
67
|
+
|
|
68
|
+
for line in lines:
|
|
69
|
+
# Formato típico: " Exploit Title | Path"
|
|
70
|
+
# Luego: " Apache Struts 2 RCE | linux/webapps/42324.rb"
|
|
71
|
+
if '|' not in line:
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
# Ignorar líneas de header
|
|
75
|
+
if 'Exploit Title' in line or '---' in line:
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
parts = line.rsplit('|', 1)
|
|
79
|
+
if len(parts) != 2:
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
title = parts[0].strip()
|
|
83
|
+
path = parts[1].strip()
|
|
84
|
+
|
|
85
|
+
# Extraer ID del path
|
|
86
|
+
match = re.search(r'/(\d+)(?:\.\w+)?$', path)
|
|
87
|
+
exploit_id = match.group(1) if match else ""
|
|
88
|
+
|
|
89
|
+
exploits.append({
|
|
90
|
+
'id': exploit_id,
|
|
91
|
+
'title': title,
|
|
92
|
+
'path': path,
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
return exploits
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _search(term: str, exact: bool = False, title_only: bool = False) -> str:
|
|
99
|
+
"""Busca exploits por término."""
|
|
100
|
+
cmd = "searchsploit"
|
|
101
|
+
|
|
102
|
+
if title_only:
|
|
103
|
+
cmd += " -t"
|
|
104
|
+
|
|
105
|
+
if exact:
|
|
106
|
+
cmd += " -e"
|
|
107
|
+
|
|
108
|
+
# Escapar el término
|
|
109
|
+
cmd += f" {shlex.quote(term)}"
|
|
110
|
+
|
|
111
|
+
retcode, stdout, stderr = _run_cmd(cmd)
|
|
112
|
+
|
|
113
|
+
if retcode != 0 and not stdout:
|
|
114
|
+
return f"Error en búsqueda: {stderr}"
|
|
115
|
+
|
|
116
|
+
if not stdout.strip():
|
|
117
|
+
return f"No se encontraron exploits para: {term}"
|
|
118
|
+
|
|
119
|
+
exploits = _parse_exploit_table(stdout)
|
|
120
|
+
|
|
121
|
+
if not exploits:
|
|
122
|
+
return stdout # Devolver salida original si no se pudo parsear
|
|
123
|
+
|
|
124
|
+
# Formatear resultados
|
|
125
|
+
output = [f"Exploits encontrados para '{term}': {len(exploits)} resultados", "━" * 70]
|
|
126
|
+
|
|
127
|
+
for e in exploits:
|
|
128
|
+
output.append(f" [{e['id']:>6}] {e['title'][:55]:<55}")
|
|
129
|
+
output.append(f" {e['path']}")
|
|
130
|
+
|
|
131
|
+
output.append(f"\nPara ver detalles: searchsploit view <id>")
|
|
132
|
+
output.append(f"Para copiar: searchsploit copy <id> [destino]")
|
|
133
|
+
|
|
134
|
+
return "\n".join(output)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _search_cve(cve: str) -> str:
|
|
138
|
+
"""Busca exploits por CVE."""
|
|
139
|
+
# Normalizar CVE (aceptar con o sin prefijo CVE-)
|
|
140
|
+
cve_num = cve.replace("CVE-", "").replace("cve-", "").replace("-", "")
|
|
141
|
+
|
|
142
|
+
cmd = f"searchsploit --cve {shlex.quote(cve_num)}"
|
|
143
|
+
|
|
144
|
+
retcode, stdout, stderr = _run_cmd(cmd)
|
|
145
|
+
|
|
146
|
+
if retcode != 0 and not stdout:
|
|
147
|
+
return f"Error buscando CVE {cve}: {stderr}"
|
|
148
|
+
|
|
149
|
+
if not stdout.strip():
|
|
150
|
+
return f"No se encontraron exploits para CVE-{cve_num}"
|
|
151
|
+
|
|
152
|
+
exploits = _parse_exploit_table(stdout)
|
|
153
|
+
|
|
154
|
+
if not exploits:
|
|
155
|
+
return stdout
|
|
156
|
+
|
|
157
|
+
output = [f"Exploits para CVE-{cve_num}: {len(exploits)} resultados", "━" * 70]
|
|
158
|
+
|
|
159
|
+
for e in exploits:
|
|
160
|
+
output.append(f" [{e['id']:>6}] {e['title'][:55]:<55}")
|
|
161
|
+
output.append(f" {e['path']}")
|
|
162
|
+
|
|
163
|
+
return "\n".join(output)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _view_exploit(exploit_id: str) -> str:
|
|
167
|
+
"""Muestra detalles de un exploit."""
|
|
168
|
+
cmd = f"searchsploit -p {shlex.quote(exploit_id)}"
|
|
169
|
+
|
|
170
|
+
retcode, stdout, stderr = _run_cmd(cmd)
|
|
171
|
+
|
|
172
|
+
if retcode != 0:
|
|
173
|
+
return f"Error obteniendo exploit {exploit_id}: {stderr}"
|
|
174
|
+
|
|
175
|
+
if not stdout.strip():
|
|
176
|
+
return f"No se encontró el exploit {exploit_id}"
|
|
177
|
+
|
|
178
|
+
# Parsear la salida
|
|
179
|
+
lines = stdout.strip().split('\n')
|
|
180
|
+
info = {}
|
|
181
|
+
current_key = None
|
|
182
|
+
description_lines = []
|
|
183
|
+
|
|
184
|
+
for line in lines:
|
|
185
|
+
if ':' in line and not line.startswith(' '):
|
|
186
|
+
key, value = line.split(':', 1)
|
|
187
|
+
key = key.strip()
|
|
188
|
+
value = value.strip()
|
|
189
|
+
info[key] = value
|
|
190
|
+
elif line.startswith(' '):
|
|
191
|
+
description_lines.append(line.strip())
|
|
192
|
+
|
|
193
|
+
output = [f"Exploit ID: {exploit_id}", "━" * 60]
|
|
194
|
+
|
|
195
|
+
for key, value in info.items():
|
|
196
|
+
if key == 'Path':
|
|
197
|
+
output.append(f"\n{key}: {value}")
|
|
198
|
+
# Intentar detectar tipo de exploit
|
|
199
|
+
ext = Path(value).suffix.lower()
|
|
200
|
+
lang_map = {
|
|
201
|
+
'.py': 'Python',
|
|
202
|
+
'.rb': 'Ruby (Metasploit)',
|
|
203
|
+
'.c': 'C',
|
|
204
|
+
'.cpp': 'C++',
|
|
205
|
+
'.sh': 'Shell Script',
|
|
206
|
+
'.pl': 'Perl',
|
|
207
|
+
'.php': 'PHP',
|
|
208
|
+
'.js': 'JavaScript',
|
|
209
|
+
'.exe': 'Windows Executable',
|
|
210
|
+
}
|
|
211
|
+
lang = lang_map.get(ext, 'Desconocido')
|
|
212
|
+
output.append(f"Lenguaje: {lang}")
|
|
213
|
+
else:
|
|
214
|
+
output.append(f"{key}: {value}")
|
|
215
|
+
|
|
216
|
+
if description_lines:
|
|
217
|
+
output.append(f"\nDescripción:\n" + "\n".join(description_lines))
|
|
218
|
+
|
|
219
|
+
output.append(f"\n{'━' * 60}")
|
|
220
|
+
output.append(f"Para copiar: searchsploit copy {exploit_id}")
|
|
221
|
+
output.append(f"Para ver archivo: cat $EXPLOITDB_PATH/{info.get('Path', '')}")
|
|
222
|
+
|
|
223
|
+
return "\n".join(output)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _copy_exploit(exploit_id: str, dest: str = ".") -> str:
|
|
227
|
+
"""Copia un exploit al directorio especificado."""
|
|
228
|
+
dest_path = Path(dest).resolve()
|
|
229
|
+
dest_path.mkdir(parents=True, exist_ok=True)
|
|
230
|
+
|
|
231
|
+
cmd = f"searchsploit -m {shlex.quote(exploit_id)} {shlex.quote(str(dest_path))}"
|
|
232
|
+
|
|
233
|
+
retcode, stdout, stderr = _run_cmd(cmd)
|
|
234
|
+
|
|
235
|
+
if retcode != 0:
|
|
236
|
+
return f"Error copiando exploit: {stderr}"
|
|
237
|
+
|
|
238
|
+
# Buscar el archivo copiado
|
|
239
|
+
copied_files = list(dest_path.glob(f"*{exploit_id}*"))
|
|
240
|
+
|
|
241
|
+
output = [f"✓ Exploit {exploit_id} copiado a: {dest_path}"]
|
|
242
|
+
|
|
243
|
+
if copied_files:
|
|
244
|
+
for f in copied_files:
|
|
245
|
+
output.append(f" Archivo: {f.name}")
|
|
246
|
+
|
|
247
|
+
return "\n".join(output)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _mirror_exploit(exploit_id: str, dest: str = ".") -> str:
|
|
251
|
+
"""Copia exploit y archivos relacionados."""
|
|
252
|
+
dest_path = Path(dest).resolve()
|
|
253
|
+
dest_path.mkdir(parents=True, exist_ok=True)
|
|
254
|
+
|
|
255
|
+
# -m ya hace mirror, pero intentamos obtener más contexto
|
|
256
|
+
cmd = f"searchsploit -m {shlex.quote(exploit_id)} {shlex.quote(str(dest_path))}"
|
|
257
|
+
|
|
258
|
+
retcode, stdout, stderr = _run_cmd(cmd)
|
|
259
|
+
|
|
260
|
+
if retcode != 0:
|
|
261
|
+
return f"Error en mirror: {stderr}"
|
|
262
|
+
|
|
263
|
+
# Ver archivos creados
|
|
264
|
+
copied_files = list(dest_path.glob("*"))
|
|
265
|
+
|
|
266
|
+
output = [f"✓ Exploit {exploit_id} copiado con archivos a: {dest_path}"]
|
|
267
|
+
|
|
268
|
+
for f in sorted(copied_files):
|
|
269
|
+
size = f.stat().st_size if f.is_file() else ""
|
|
270
|
+
output.append(f" {'📁' if f.is_dir() else '📄'} {f.name} {f'({size} bytes)' if size else ''}")
|
|
271
|
+
|
|
272
|
+
return "\n".join(output)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _nmap_search(xml_file: str) -> str:
|
|
276
|
+
"""Busca exploits basado en un scan Nmap XML."""
|
|
277
|
+
xml_path = Path(xml_file)
|
|
278
|
+
if not xml_path.exists():
|
|
279
|
+
return f"Error: Archivo no encontrado: {xml_file}"
|
|
280
|
+
|
|
281
|
+
cmd = f"searchsploit --nmap {shlex.quote(str(xml_path))}"
|
|
282
|
+
|
|
283
|
+
retcode, stdout, stderr = _run_cmd(cmd, timeout=120)
|
|
284
|
+
|
|
285
|
+
if retcode != 0 and not stdout:
|
|
286
|
+
return f"Error en búsqueda Nmap: {stderr}"
|
|
287
|
+
|
|
288
|
+
return stdout if stdout.strip() else "No se encontraron exploits relevantes."
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _update_db() -> str:
|
|
292
|
+
"""Actualiza la base de datos de exploits."""
|
|
293
|
+
output = ["Actualizando base de datos de Exploit-DB...", "━" * 50]
|
|
294
|
+
|
|
295
|
+
cmd = "searchsploit -u"
|
|
296
|
+
|
|
297
|
+
retcode, stdout, stderr = _run_cmd(cmd, timeout=300)
|
|
298
|
+
|
|
299
|
+
if retcode != 0:
|
|
300
|
+
output.append(f"Error: {stderr}")
|
|
301
|
+
else:
|
|
302
|
+
output.append(stdout if stdout.strip() else "Base de datos actualizada.")
|
|
303
|
+
|
|
304
|
+
return "\n".join(output)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _analyze_exploit(exploit_id: str) -> str:
|
|
308
|
+
"""Analiza un exploit y sugiere cómo usarlo."""
|
|
309
|
+
# Primero obtener info del exploit
|
|
310
|
+
cmd = f"searchsploit -p {shlex.quote(exploit_id)}"
|
|
311
|
+
retcode, stdout, stderr = _run_cmd(cmd)
|
|
312
|
+
|
|
313
|
+
if retcode != 0 or not stdout.strip():
|
|
314
|
+
return f"No se pudo obtener info del exploit {exploit_id}"
|
|
315
|
+
|
|
316
|
+
# Parsear información
|
|
317
|
+
info = {}
|
|
318
|
+
for line in stdout.split('\n'):
|
|
319
|
+
if ':' in line and not line.startswith(' '):
|
|
320
|
+
key, value = line.split(':', 1)
|
|
321
|
+
info[key.strip()] = value.strip()
|
|
322
|
+
|
|
323
|
+
path = info.get('Path', '')
|
|
324
|
+
title = info.get('Exploit Title', 'Desconocido')
|
|
325
|
+
|
|
326
|
+
ext = Path(path).suffix.lower() if path else ''
|
|
327
|
+
|
|
328
|
+
output = [
|
|
329
|
+
f"ANÁLISIS DE EXPLOIT",
|
|
330
|
+
f"{'━' * 60}",
|
|
331
|
+
f"ID: {exploit_id}",
|
|
332
|
+
f"Título: {title}",
|
|
333
|
+
f"Tipo: {ext}",
|
|
334
|
+
f"",
|
|
335
|
+
]
|
|
336
|
+
|
|
337
|
+
# Sugerencias basadas en tipo de archivo
|
|
338
|
+
suggestions = {
|
|
339
|
+
'.py': [
|
|
340
|
+
"Python exploit detectado.",
|
|
341
|
+
"Pasos para ejecutar:",
|
|
342
|
+
"1. Copiar el exploit: searchsploit copy " + exploit_id,
|
|
343
|
+
"2. Revisar el código: cat <archivo>.py",
|
|
344
|
+
"3. Instalar dependencias si las hay",
|
|
345
|
+
"4. Ejecutar: python <archivo>.py <target> [opciones]",
|
|
346
|
+
"",
|
|
347
|
+
"Verificar variables de configuración:",
|
|
348
|
+
" - IP/URL del objetivo",
|
|
349
|
+
" - Puerto",
|
|
350
|
+
" - Payload o shellcode",
|
|
351
|
+
],
|
|
352
|
+
'.rb': [
|
|
353
|
+
"Módulo Metasploit detectado.",
|
|
354
|
+
"Pasos para usar:",
|
|
355
|
+
"1. Copiar a ~/.msf4/modules/exploits/",
|
|
356
|
+
"2. Iniciar msfconsole",
|
|
357
|
+
"3. Cargar: use exploit/<nombre>",
|
|
358
|
+
"4. Configurar: set RHOSTS <target>",
|
|
359
|
+
"5. Ejecutar: exploit",
|
|
360
|
+
],
|
|
361
|
+
'.c': [
|
|
362
|
+
"Exploit en C detectado.",
|
|
363
|
+
"Pasos para compilar:",
|
|
364
|
+
"1. Copiar: searchsploit copy " + exploit_id,
|
|
365
|
+
"2. Compilar: gcc -o exploit <archivo>.c",
|
|
366
|
+
"3. Si da error, probar: gcc -m32 -o exploit <archivo>.c",
|
|
367
|
+
"4. Ejecutar: ./exploit <argumentos>",
|
|
368
|
+
],
|
|
369
|
+
'.sh': [
|
|
370
|
+
"Shell script detectado.",
|
|
371
|
+
"Ejecutar directamente después de revisar:",
|
|
372
|
+
"1. Copiar: searchsploit copy " + exploit_id,
|
|
373
|
+
"2. Revisar: cat <archivo>.sh",
|
|
374
|
+
"3. Dar permisos: chmod +x <archivo>.sh",
|
|
375
|
+
"4. Ejecutar: ./<archivo>.sh",
|
|
376
|
+
],
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if ext in suggestions:
|
|
380
|
+
output.extend(suggestions[ext])
|
|
381
|
+
else:
|
|
382
|
+
output.extend([
|
|
383
|
+
"Tipo de exploit no reconocido automáticamente.",
|
|
384
|
+
"Revisar el archivo y seguir las instrucciones internas.",
|
|
385
|
+
f"Copiar con: searchsploit copy {exploit_id}",
|
|
386
|
+
])
|
|
387
|
+
|
|
388
|
+
output.append("")
|
|
389
|
+
output.append(f"Archivo original: {path}")
|
|
390
|
+
|
|
391
|
+
return "\n".join(output)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def run(args: str = "") -> str:
|
|
395
|
+
"""Punto de entrada del plugin."""
|
|
396
|
+
if not args.strip():
|
|
397
|
+
return f"Uso: {USAGE}\n\n{AGENT_DOC}"
|
|
398
|
+
|
|
399
|
+
parts = args.strip().split(maxsplit=2)
|
|
400
|
+
cmd = parts[0].lower()
|
|
401
|
+
cmd_args = parts[1:] if len(parts) > 1 else []
|
|
402
|
+
|
|
403
|
+
# ── SEARCH ────────────────────────────────────────────────────────────
|
|
404
|
+
if cmd == "search":
|
|
405
|
+
if not cmd_args:
|
|
406
|
+
return "Uso: search <término> [--exact] [--title]"
|
|
407
|
+
|
|
408
|
+
term = " ".join(cmd_args)
|
|
409
|
+
exact = "--exact" in term or "-e" in term
|
|
410
|
+
title_only = "--title" in term or "-t" in term
|
|
411
|
+
|
|
412
|
+
# Limpiar flags del término
|
|
413
|
+
for flag in ["--exact", "-e", "--title", "-t"]:
|
|
414
|
+
term = term.replace(flag, "")
|
|
415
|
+
term = term.strip()
|
|
416
|
+
|
|
417
|
+
return _search(term, exact=exact, title_only=title_only)
|
|
418
|
+
|
|
419
|
+
# ── CVE ────────────────────────────────────────────────────────────────
|
|
420
|
+
if cmd == "cve":
|
|
421
|
+
if not cmd_args:
|
|
422
|
+
return "Uso: cve <CVE>\nEjemplo: cve 2021-44228 o cve CVE-2021-44228"
|
|
423
|
+
|
|
424
|
+
cve = cmd_args[0]
|
|
425
|
+
return _search_cve(cve)
|
|
426
|
+
|
|
427
|
+
# ── VIEW ───────────────────────────────────────────────────────────────
|
|
428
|
+
if cmd == "view":
|
|
429
|
+
if not cmd_args:
|
|
430
|
+
return "Uso: view <id>"
|
|
431
|
+
|
|
432
|
+
exploit_id = cmd_args[0]
|
|
433
|
+
return _view_exploit(exploit_id)
|
|
434
|
+
|
|
435
|
+
# ── COPY ───────────────────────────────────────────────────────────────
|
|
436
|
+
if cmd == "copy":
|
|
437
|
+
if not cmd_args:
|
|
438
|
+
return "Uso: copy <id> [destino]"
|
|
439
|
+
|
|
440
|
+
exploit_id = cmd_args[0]
|
|
441
|
+
dest = cmd_args[1] if len(cmd_args) > 1 else "."
|
|
442
|
+
return _copy_exploit(exploit_id, dest)
|
|
443
|
+
|
|
444
|
+
# ── MIRROR ─────────────────────────────────────────────────────────────
|
|
445
|
+
if cmd == "mirror":
|
|
446
|
+
if not cmd_args:
|
|
447
|
+
return "Uso: mirror <id> [destino]"
|
|
448
|
+
|
|
449
|
+
exploit_id = cmd_args[0]
|
|
450
|
+
dest = cmd_args[1] if len(cmd_args) > 1 else "."
|
|
451
|
+
return _mirror_exploit(exploit_id, dest)
|
|
452
|
+
|
|
453
|
+
# ── NMAP ───────────────────────────────────────────────────────────────
|
|
454
|
+
if cmd == "nmap":
|
|
455
|
+
if not cmd_args:
|
|
456
|
+
return "Uso: nmap <archivo.xml>"
|
|
457
|
+
|
|
458
|
+
xml_file = cmd_args[0]
|
|
459
|
+
return _nmap_search(xml_file)
|
|
460
|
+
|
|
461
|
+
# ── UPDATE ─────────────────────────────────────────────────────────────
|
|
462
|
+
if cmd == "update":
|
|
463
|
+
return _update_db()
|
|
464
|
+
|
|
465
|
+
# ── ANALYZE ────────────────────────────────────────────────────────────
|
|
466
|
+
if cmd == "analyze":
|
|
467
|
+
if not cmd_args:
|
|
468
|
+
return "Uso: analyze <id> — Analiza y sugiere cómo usar el exploit"
|
|
469
|
+
|
|
470
|
+
exploit_id = cmd_args[0]
|
|
471
|
+
return _analyze_exploit(exploit_id)
|
|
472
|
+
|
|
473
|
+
# ── INFO ───────────────────────────────────────────────────────────────
|
|
474
|
+
if cmd in ("info", "help"):
|
|
475
|
+
return f"""SearchSploit — Buscar exploits en Exploit-DB
|
|
476
|
+
|
|
477
|
+
Comandos:
|
|
478
|
+
search <término> Buscar exploits por término
|
|
479
|
+
cve <CVE> Buscar por CVE (ej: 2021-44228)
|
|
480
|
+
view <id> Ver detalles de un exploit
|
|
481
|
+
copy <id> [dest] Copiar exploit al directorio
|
|
482
|
+
mirror <id> [dest] Copiar exploit con archivos relacionados
|
|
483
|
+
nmap <archivo.xml> Buscar exploits desde scan Nmap
|
|
484
|
+
analyze <id> Analizar y sugerir uso del exploit
|
|
485
|
+
update Actualizar base de datos
|
|
486
|
+
|
|
487
|
+
Ejemplos:
|
|
488
|
+
searchsploit search apache struts
|
|
489
|
+
searchsploit cve 2021-44228
|
|
490
|
+
searchsploit view 46570
|
|
491
|
+
searchsploit copy 46570 ./exploits/
|
|
492
|
+
searchsploit analyze 46570
|
|
493
|
+
"""
|
|
494
|
+
|
|
495
|
+
return f"Comando desconocido: {cmd}\nUsa: searchsploit info"
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# plugins/strategist.py — Plugin Estratega IA
|
|
2
|
+
"""
|
|
3
|
+
Llama a un modelo de IA para generar un plan estructurado paso a paso
|
|
4
|
+
antes de ejecutar tareas complejas.
|
|
5
|
+
"""
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import urllib.request
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
NAME = "strategist"
|
|
13
|
+
DESCRIPTION = "Usa un modelo IA para generar un plan detallado antes de actuar"
|
|
14
|
+
USAGE = "<descripción de la tarea compleja>"
|
|
15
|
+
AGENT_DOC = """
|
|
16
|
+
Úsalo cuando la tarea sea compleja o requiera planificación previa.
|
|
17
|
+
El plugin retorna un plan JSON estructurado que el agente debe seguir.
|
|
18
|
+
Ejemplo: <run_plugin name="strategist" args="Refactorizar el módulo de auth para OAuth2"/>
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def run(args: str = "") -> str:
|
|
23
|
+
if not args.strip():
|
|
24
|
+
return "Proporciona la tarea: <run_plugin name='strategist' args='descripción'/>"
|
|
25
|
+
|
|
26
|
+
# Intentar con Claude si hay API key
|
|
27
|
+
api_key = os.environ.get("ANTHROPIC_API_KEY", "")
|
|
28
|
+
if api_key:
|
|
29
|
+
result = _try_claude(args, api_key)
|
|
30
|
+
if result:
|
|
31
|
+
return result
|
|
32
|
+
|
|
33
|
+
# Intentar con Ollama local
|
|
34
|
+
result = _try_ollama(args)
|
|
35
|
+
if result:
|
|
36
|
+
return result
|
|
37
|
+
|
|
38
|
+
return _fallback_plan(args)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _try_claude(task: str, api_key: str) -> str:
|
|
42
|
+
"""Llama a Claude para planificar."""
|
|
43
|
+
try:
|
|
44
|
+
payload = json.dumps({
|
|
45
|
+
"model": "claude-haiku-4-5",
|
|
46
|
+
"max_tokens": 1500,
|
|
47
|
+
"system": _SYSTEM,
|
|
48
|
+
"messages": [{"role": "user", "content": _build_prompt(task)}],
|
|
49
|
+
}).encode()
|
|
50
|
+
req = urllib.request.Request(
|
|
51
|
+
"https://api.anthropic.com/v1/messages",
|
|
52
|
+
data=payload,
|
|
53
|
+
headers={
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
"x-api-key": api_key,
|
|
56
|
+
"anthropic-version": "2023-06-01",
|
|
57
|
+
},
|
|
58
|
+
method="POST",
|
|
59
|
+
)
|
|
60
|
+
with urllib.request.urlopen(req, timeout=30) as r:
|
|
61
|
+
data = json.loads(r.read())
|
|
62
|
+
text = data["content"][0]["text"]
|
|
63
|
+
try:
|
|
64
|
+
import re
|
|
65
|
+
m = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', text, re.DOTALL)
|
|
66
|
+
if m:
|
|
67
|
+
text = m.group(1)
|
|
68
|
+
plan_data = json.loads(text)
|
|
69
|
+
return _format_plan(task, plan_data)
|
|
70
|
+
except Exception:
|
|
71
|
+
return f"## 🧠 Plan Estratégico\n**Tarea:** {task}\n\n{text}"
|
|
72
|
+
except Exception:
|
|
73
|
+
return ""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _try_ollama(task: str) -> str:
|
|
77
|
+
"""Intenta usar Ollama local."""
|
|
78
|
+
try:
|
|
79
|
+
import requests
|
|
80
|
+
ollama_url = os.environ.get("OLLAMA_URL", "http://localhost:11434")
|
|
81
|
+
|
|
82
|
+
# Verificar que Ollama está disponible
|
|
83
|
+
try:
|
|
84
|
+
requests.get(f"{ollama_url}/api/tags", timeout=2)
|
|
85
|
+
except Exception:
|
|
86
|
+
return ""
|
|
87
|
+
|
|
88
|
+
payload = {
|
|
89
|
+
"model": "llama3",
|
|
90
|
+
"prompt": _build_prompt(task),
|
|
91
|
+
"system": _SYSTEM,
|
|
92
|
+
"stream": False,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
r = requests.post(f"{ollama_url}/api/generate", json=payload, timeout=60)
|
|
96
|
+
r.raise_for_status()
|
|
97
|
+
text = r.json().get("response", "")
|
|
98
|
+
|
|
99
|
+
if text:
|
|
100
|
+
try:
|
|
101
|
+
import re
|
|
102
|
+
m = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', text, re.DOTALL)
|
|
103
|
+
if m:
|
|
104
|
+
text = m.group(1)
|
|
105
|
+
plan_data = json.loads(text)
|
|
106
|
+
return _format_plan(task, plan_data)
|
|
107
|
+
except Exception:
|
|
108
|
+
return f"## 🧠 Plan Estratégico\n**Tarea:** {task}\n\n{text}"
|
|
109
|
+
except Exception:
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
return ""
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _build_prompt(task: str) -> str:
|
|
116
|
+
return (
|
|
117
|
+
f"Analiza esta tarea de programación y genera un plan detallado en JSON:\n\n"
|
|
118
|
+
f"TAREA: {task}\n\n"
|
|
119
|
+
f"Responde SOLO con JSON válido, sin texto adicional."
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _format_plan(task: str, data: dict) -> str:
|
|
124
|
+
lines = [
|
|
125
|
+
"## 🧠 Plan Estratégico",
|
|
126
|
+
f"**Tarea:** {task}",
|
|
127
|
+
"",
|
|
128
|
+
f"**Análisis:** {data.get('analysis', 'N/A')}",
|
|
129
|
+
f"**Pasos estimados:** {data.get('estimated_steps', len(data.get('plan', [])))}",
|
|
130
|
+
"",
|
|
131
|
+
"### Pasos a seguir:",
|
|
132
|
+
]
|
|
133
|
+
for step in data.get("plan", []):
|
|
134
|
+
n = step.get("step", "?")
|
|
135
|
+
act = step.get("action", "")
|
|
136
|
+
tools = ", ".join(step.get("tools", []))
|
|
137
|
+
why = step.get("rationale", "")
|
|
138
|
+
lines.append(f"\n**Paso {n}:** {act}")
|
|
139
|
+
if tools:
|
|
140
|
+
lines.append(f" → Herramientas: `{tools}`")
|
|
141
|
+
if why:
|
|
142
|
+
lines.append(f" → Razón: {why}")
|
|
143
|
+
|
|
144
|
+
risks = data.get("risks", [])
|
|
145
|
+
if risks:
|
|
146
|
+
lines.append("\n### ⚠️ Riesgos:")
|
|
147
|
+
for r in risks:
|
|
148
|
+
lines.append(f" - {r}")
|
|
149
|
+
|
|
150
|
+
return "\n".join(lines)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _fallback_plan(task: str) -> str:
|
|
154
|
+
return (
|
|
155
|
+
f"## Plan básico para: {task}\n\n"
|
|
156
|
+
"**Paso 1:** Explorar estructura del proyecto → `glob_search`, `read_file`\n"
|
|
157
|
+
"**Paso 2:** Leer archivos relevantes → `read_file`, `grep_search`\n"
|
|
158
|
+
"**Paso 3:** Analizar el código actual → `read_file`\n"
|
|
159
|
+
"**Paso 4:** Implementar los cambios → `write_file`, `edit_file`\n"
|
|
160
|
+
"**Paso 5:** Verificar y ejecutar tests → `exec_cmd`\n\n"
|
|
161
|
+
"_Para planes más detallados, configura ANTHROPIC_API_KEY o usa Ollama local._"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
_SYSTEM = """Eres un experto en arquitectura de software. Analiza la tarea y responde ÚNICAMENTE con JSON:
|
|
166
|
+
{
|
|
167
|
+
"analysis": "Breve análisis de la tarea",
|
|
168
|
+
"plan": [
|
|
169
|
+
{"step": 1, "action": "Acción concreta", "tools": ["read_file"], "rationale": "Por qué"},
|
|
170
|
+
{"step": 2, "action": "...", "tools": [...], "rationale": "..."}
|
|
171
|
+
],
|
|
172
|
+
"risks": ["Riesgo 1"],
|
|
173
|
+
"estimated_steps": 5
|
|
174
|
+
}
|
|
175
|
+
Sé específico con rutas de archivos y comandos exactos si los conoces."""
|