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.
Files changed (93) hide show
  1. hanus/__init__.py +5 -0
  2. hanus/__main__.py +10 -0
  3. hanus/action_handlers.py +76 -0
  4. hanus/action_parser.py +82 -0
  5. hanus/agent_runner.py +1445 -0
  6. hanus/analysis/__init__.py +5 -0
  7. hanus/analysis/debt.py +702 -0
  8. hanus/analysis/dependencies.py +475 -0
  9. hanus/cache/__init__.py +5 -0
  10. hanus/cache/response_cache.py +560 -0
  11. hanus/config.py +401 -0
  12. hanus/connectors/__init__.py +19 -0
  13. hanus/connectors/base.py +114 -0
  14. hanus/connectors/claude_connector.py +146 -0
  15. hanus/connectors/gemini_connector.py +141 -0
  16. hanus/connectors/glm_connector.py +160 -0
  17. hanus/connectors/ollama_connector.py +174 -0
  18. hanus/connectors/openai_connector.py +122 -0
  19. hanus/connectors/registry.py +26 -0
  20. hanus/context/__init__.py +7 -0
  21. hanus/context/manager.py +837 -0
  22. hanus/context/selective.py +626 -0
  23. hanus/error_recovery/__init__.py +5 -0
  24. hanus/error_recovery/auto_fix.py +605 -0
  25. hanus/hooks/__init__.py +5 -0
  26. hanus/hooks/manager.py +247 -0
  27. hanus/instincts/__init__.py +44 -0
  28. hanus/instincts/cli.py +372 -0
  29. hanus/instincts/detector.py +281 -0
  30. hanus/instincts/evolver.py +361 -0
  31. hanus/instincts/manager.py +343 -0
  32. hanus/instincts/types.py +253 -0
  33. hanus/logger.py +81 -0
  34. hanus/memory/__init__.py +8 -0
  35. hanus/memory/manager.py +265 -0
  36. hanus/memory/types.py +119 -0
  37. hanus/monitor.py +341 -0
  38. hanus/parallel/__init__.py +5 -0
  39. hanus/parallel/executor.py +300 -0
  40. hanus/permissions.py +182 -0
  41. hanus/plan/__init__.py +8 -0
  42. hanus/plan/mode.py +267 -0
  43. hanus/plan/models.py +152 -0
  44. hanus/plugin_manager.py +754 -0
  45. hanus/plugin_registry.py +391 -0
  46. hanus/plugins/__init__.py +1 -0
  47. hanus/plugins/arena.py +630 -0
  48. hanus/plugins/code_review.py +123 -0
  49. hanus/plugins/cortex.py +1750 -0
  50. hanus/plugins/deps_check.py +27 -0
  51. hanus/plugins/git_ops.py +33 -0
  52. hanus/plugins/metasploit.py +530 -0
  53. hanus/plugins/notes.py +583 -0
  54. hanus/plugins/search_code.py +59 -0
  55. hanus/plugins/searchsploit.py +495 -0
  56. hanus/plugins/strategist.py +175 -0
  57. hanus/plugins/webui.py +5200 -0
  58. hanus/profiles.py +479 -0
  59. hanus/profiles_builtin/__init__.py +0 -0
  60. hanus/profiles_builtin/architect/profile.yaml +12 -0
  61. hanus/profiles_builtin/architect/system_prompt.txt +71 -0
  62. hanus/profiles_builtin/deep/profile.yaml +12 -0
  63. hanus/profiles_builtin/deep/system_prompt.txt +66 -0
  64. hanus/profiles_builtin/developer/__init__.py +0 -0
  65. hanus/profiles_builtin/developer/profile.yaml +9 -0
  66. hanus/profiles_builtin/developer/system_prompt.txt +176 -0
  67. hanus/profiles_builtin/speed/profile.yaml +12 -0
  68. hanus/profiles_builtin/speed/system_prompt.txt +51 -0
  69. hanus/project_tools.py +177 -0
  70. hanus/query_engine.py +1594 -0
  71. hanus/rules/__init__.py +237 -0
  72. hanus/search/__init__.py +5 -0
  73. hanus/search/semantic.py +596 -0
  74. hanus/session_manager.py +547 -0
  75. hanus/skill_manager.py +702 -0
  76. hanus/skills/__init__.py +4 -0
  77. hanus/subagent/__init__.py +8 -0
  78. hanus/subagent/agents/__init__.py +253 -0
  79. hanus/subagent/manager.py +309 -0
  80. hanus/subagent/types.py +266 -0
  81. hanus/suggestions/__init__.py +5 -0
  82. hanus/suggestions/proactive.py +451 -0
  83. hanus/tasks/__init__.py +8 -0
  84. hanus/tasks/manager.py +330 -0
  85. hanus/tasks/models.py +106 -0
  86. hanus/terminal_prompt.py +166 -0
  87. hanus/tools.py +1849 -0
  88. hanus/ui.py +939 -0
  89. hanuscode-1.0.0.dist-info/METADATA +1151 -0
  90. hanuscode-1.0.0.dist-info/RECORD +93 -0
  91. hanuscode-1.0.0.dist-info/WHEEL +5 -0
  92. hanuscode-1.0.0.dist-info/entry_points.txt +2 -0
  93. 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."""