Escáner de puertos con arquitectura especializada para auditorías de seguridad autorizadas: Python orquesta la sesión, Rust ejecuta el escaneo TCP y Go captura los banners solicitados.
CicadaPort 3.0.0-rc.2 (3.0.0rc2 en metadatos Python) es la nueva
Release Candidate empresarial en validación de rama. No es una versión estable
ni está publicada todavía. La matriz verificada continúa limitada a Linux
x86_64, Ubuntu 22.04/24.04 y Python 3.10-3.13; Windows, macOS, ARM64 y
Python 3.14 permanecen no soportados. Rust 1.97.1 y Go 1.26.5 siguen fijados
y los contratos JSONL públicos permanecen en versión 1.
El wheel Linux contiene los motores obligatorios Rust y Go. La construcción y prueba aislada de wheel/sdist se ejecuta con:
python -m pip install -r requirements-release.txt
./scripts/build_release_artifacts.sh
./scripts/test_release_artifacts.sh distTASK 4 — sesiones reproducibles, reanudables y observables — está consolidada,
cerrada y congelada sobre la implementación funcional
77ad51f0751b29b510f574e750c1a3fa65db4a60. Este cierre no convierte por sí
solo a 3.0.0-rc.2 en una versión estable ni autoriza capacidades fuera del
alcance TCP-connect y banner grabbing documentado.
TASK 5 — Enterprise Engine and Production Hardening — está en implementación
sobre feat/task-5-enterprise-engine-production-hardening. SUBTASKS 5.1–5.5
están cerradas, consolidadas y congeladas. SUBTASK 5.6 ejecuta la validación
empresarial integral y prepara 3.0.0-rc.2 sin alterar los contratos públicos
v1 ni service_evidence v2. La integración a main, el etiquetado y la
publicación continúan bloqueados por una puerta formal separada. Consulta
docs/task-5-status.md.
- Escaneo Multi-hilos: Alta velocidad con gestión eficiente de hilos
- Detección de Servicios: Identificación por puerto sin cargas de aplicación
- Múltiples Formatos: Reportes en TXT, JSON, CSV y HTML
- Salida Dual: Hallazgos ordenados en pantalla y reporte persistente
- CLI Profesional: Interfaz de línea de comandos intuitiva y robusta
- TUI Multiobjetivo: Dashboard terminal en vivo para sesiones simples o por lotes, sin lógica de red duplicada
- Perfiles Reproducibles:
safe,standard,deepycustom - Orquestación Multiobjetivo: Rangos, CIDR, archivos y exclusiones con concurrencia acotada
- Banner Grabbing Explícito: Solo con
--banner-grab, mediante el motor Go - Cancelación Cooperativa: Detención controlada de Rust y Go desde Python
- Validación Avanzada: Verificación completa de entradas y configuraciones
- Estadísticas Detalladas: Métricas completas del escaneo
El motor Rust conserva scan_request/port_result v1 y tcp_connect, pero
resuelve cada objetivo una sola vez, distribuye puertos mediante un índice
atómico y aplica backpressure con un canal acotado. La aceptación oficial se
ejecuta únicamente sobre loopback y compara rendimiento y recursos contra la
baseline congelada de TASK 5.1.
El motor Go mantiene banner_request/banner_result v1 y emite cada resultado
al finalizar el endpoint, sin acumular ni ordenar previamente toda la salida.
Una evidencia v2 opcional registra probe, fase, longitudes, truncamiento, hash,
timeouts y TLS observado por un descriptor separado. Solo los probes
passive-banner@1 y http-head@1 están permitidos por defecto; no se incorpora
detección de vulnerabilidades.
El estado congelado, la deuda transitoria clasificada, los subhitos restantes
del Hito 3 y su puerta formal de cierre se mantienen en
ROADMAP.md. La presencia de un subhito futuro en esa hoja lo deja
DEFINED, pero no autoriza su implementación: cada apertura requiere un
contrato provisional y una aprobación expresa.
El núcleo define un contrato versionado independiente de la interfaz. Cada
resultado de puerto conserva is_open como proyección derivada de compatibilidad
del estado canónico. state es la fuente de verdad, evidence.reason sustenta
la razón y el campo superior reason debe coincidir con ella. Además registra:
- estado canónico
open,closed,filtered,unfiltered,open|filteredoclosed|filtered; - razón técnica y evidencia que sustentan ese estado;
- objetivo solicitado, dirección resuelta y familia IPv4/IPv6;
- estado observado del host y técnica de escaneo utilizada;
- versión del contrato usada por la comunicación JSON Lines con Rust y Go.
El parser acepta especificaciones individuales, varias entradas,
CIDR, rangos IP completos y archivos con comentarios. Deduplica preservando el
orden, permite exclusiones y aplica un límite explícito de 4096 objetivos para
evitar expansiones masivas accidentales. La resolución usa getaddrinfo() y
puede conservar todas las direcciones IPv4 e IPv6 de un hostname.
La CLI conecta este contrato con el orquestador multiobjetivo. Cada dirección resuelta ejecuta su propio flujo especializado, conserva identidad y evidencia, genera un reporte independiente y puede fallar sin descartar los resultados correctos de otros objetivos. El progreso se consolida para toda la sesión y la cancelación cooperativa alcanza todos los motores activos.
--target-workers limita cuántos objetivos se procesan simultáneamente.
--threads es un presupuesto global: el orquestador lo reparte entre los
objetivos activos y nunca lo multiplica silenciosamente. El descubrimiento de
hosts, las técnicas raw y los escaneos no autorizados continúan fuera del
alcance.
Python entrega a Rust por stdin una solicitud scan_request v1 completa:
objetivo resuelto, puertos normalizados, timeout en milisegundos y concurrencia
efectiva. Ningún dato contractual viaja fragmentado en argumentos del proceso.
--request-stdin es la única interfaz operativa admitida por los binarios Rust
y Go. --help es la única operación informativa adicional. Cualquier argumento
histórico, desconocido, posicional o mezclado termina con código 2 antes de
leer stdin o iniciar actividad de red.
Rust emite por stdout un registro port_result v1 por línea en el orden real
de finalización y fuerza un flush después de cada resultado. Los diagnósticos
se reservan para stderr. El puente valida versión, tipo de registro, estado,
evidencia, puertos inesperados, duplicados y streams incompletos antes de
incorporar cada observación al núcleo.
El progreso de la CLI y del TUI procede ahora de puertos realmente completados: cada línea válida actualiza inmediatamente la cobertura y los hallazgos abiertos. El orden de llegada no se altera durante el stream; los resultados solo se ordenan al consolidar la sesión y generar los reportes.
Protocolo de entrada utilizado por Python para Rust:
{"contract_version":1,"record_type":"scan_request","target":"127.0.0.1","ports":[22,80,443],"timeout_ms":2000,"workers":3}La salida contiene objetos JSON independientes, uno por línea:
{"contract_version":1,"record_type":"port_result","target":"127.0.0.1","address":"127.0.0.1","address_family":"ipv4","host_state":"up","port":80,"protocol":"tcp","state":"open","reason":"connection_accepted","technique":"tcp_connect","service":"HTTP","banner":null,"response_time":0.001,"is_open":true,"evidence":{"reason":"connection_accepted","source":"rust","errno":0}}Go usa el mismo aislamiento: recibe un banner_request v1 completo mediante
--request-stdin y emite un banner_result v1 por cada puerto abierto
solicitado. Cada resultado declara explícitamente captured, empty o error;
los resultados vacíos y los fallos no desaparecen silenciosamente.
{"contract_version":1,"record_type":"banner_request","target":"127.0.0.1","ports":[80,443],"timeout_ms":3000}{"contract_version":1,"record_type":"banner_result","target":"127.0.0.1","port":80,"status":"captured","service":"HTTP","banner":"HTTP/1.0 200 OK","error":null,"source":"go"}Las rutas históricas se retiraron en el Subhito 3.2.9. Los binarios son
componentes internos; la interfaz pública continúa siendo cicadaport. La
etiqueta firmada subhito-3.2.8 conserva el último estado que admitía estas
formas, que ya no están soportadas:
rust-core --host 127.0.0.1 --ports 80,443
rust-core --host 127.0.0.1 --ports-stdin --timeout 1 --workers 2
go-banner --host 127.0.0.1 --ports 80,443 --timeout 1La migración consiste en enviar una solicitud v1 completa por stdin:
printf '%s\n' \
'{"contract_version":1,"record_type":"scan_request","target":"127.0.0.1","ports":[80,443],"timeout_ms":1000,"workers":2}' | \
rust-core --request-stdin
printf '%s\n' \
'{"contract_version":1,"record_type":"banner_request","target":"127.0.0.1","ports":[80,443],"timeout_ms":1000}' | \
go-banner --request-stdinEsta consolidación no cambia las versiones, los campos ni la semántica de los contratos JSONL v1.
Antes de incorporar registros al núcleo, Python rechaza versiones o tipos incorrectos, campos ausentes o desconocidos, objetivos y puertos no solicitados, duplicados, respuestas incompletas y combinaciones incoherentes de estado, banner y error.
El flujo público activo es siempre Python → Rust → Python → Go → Python:
- Python valida la solicitud, resuelve el objetivo y prepara el contrato.
- Rust ejecuta obligatoriamente el escaneo TCP y transmite resultados JSONL.
- Python valida y normaliza cada resultado.
- Go recibe únicamente los puertos confirmados como abiertos cuando
--banner-grabestá habilitado. - Python integra los banners y genera la salida, las estadísticas y el reporte.
Antes de resolver el objetivo o iniciar el escaneo, el orquestador comprueba el
binario Rust y, si se solicitaron banners, también el binario Go. Si falta un
motor requerido, la sesión falla con un diagnóstico claro y recomienda ejecutar
./scripts/build_all.sh; nunca cambia silenciosamente a Python.
La CLI no expone selectores de motor: Rust es siempre el motor TCP público y
Go es el único motor de banners cuando --banner-grab está habilitado. Las
opciones históricas --engine y --banner-engine ya no se reconocen y terminan
con código 2 antes de resolver objetivos o iniciar actividad de red. Para
migrar automatizaciones existentes, elimina esos argumentos; el flujo efectivo
permanece invariable y nunca utiliza fallback Python.
Las implementaciones Python de escaneo y banners permanecen en el repositorio como referencia interna y soporte de pruebas, pero no son seleccionables desde la interfaz pública.
# Clonar el repositorio
git clone https://github.com/DrewSC13/port-scanner.git
cd port-scanner
# Crear un entorno virtual e instalar la aplicación
python3 -m venv .venv
source .venv/bin/activate
python -m pip install .
# Compilar los motores obligatorios
./scripts/build_all.sh
# Mostrar la ayuda de la CLI instalada
cicadaport --helpLa CLI es la fuente de configuración tanto para automatización como para la
interfaz en vivo. Los objetivos y todas las opciones se escriben primero en la
consola; después, --tui abre el monitor dentro de esa misma terminal e inicia
la solicitud inmutable automáticamente. El TUI consume ScanOrchestrator.run()
para una dirección y ScanOrchestrator.run_many() para sesiones multiobjetivo;
no reimplementa resolución, concurrencia, escaneo, banners ni reportes:
cicadaport 192.168.1.10 --profile standard --tuiEl monitor usa un diseño terminal moderno inspirado en herramientas como
btop, con una estética de glasmorfismo adaptada a las capacidades reales de
la consola: fondo negro azulado, superficies azul petróleo, bordes de bajo
contraste y acentos cian, violeta y ámbar. Los paneles mantienen separación
visual y no emplean botones, formularios, desplegables ni una segunda línea de
comandos.
La telemetría distribuye la información por jerarquía: señal RTT y respuesta de puertos, tarjetas de velocidad y estados, cobertura con ETA, plan de ejecución, endpoints abiertos, eventos del motor y evidencia del último servicio. Todos los valores proceden del núcleo de escaneo; la interfaz no genera progreso ni resultados ficticios.
El TUI conserva el fondo predeterminado del emulador en lugar de rellenar la pantalla con un color opaco. Esto permite usar transparencia y desenfoque reales cuando el emulador y el compositor los ofrecen. En Konsole, el efecto se activa en el perfil utilizado por CicadaPort: edita su esquema de colores, reduce moderadamente la opacidad del fondo y habilita Desenfocar fondo. Si esas opciones no están disponibles, el monitor mantiene un fondo terminal normal sin afectar su legibilidad ni su funcionamiento.
# Perfil conservador y reporte de texto
cicadaport 192.168.1.10 --profile safe --tui
# TCP completo, enumeración de servicios y reporte JSON
cicadaport 192.168.1.10 --profile deep --format json --tui
# Rango de puertos definido manualmente y banners Go explícitos
cicadaport 192.168.1.10 --profile custom -p 20-443 \
--banner-grab --tui
# Dos objetivos locales con progreso global y reportes independientes
cicadaport 127.0.0.1 --target 127.0.0.2 \
-p 4444 --threads 4 --target-workers 2 --tui
# Rango de objetivos con exclusión monitorizado desde el dashboard
cicadaport 127.0.0.1-127.0.0.4 --exclude 127.0.0.3 \
-p 20-25 --threads 6 --target-workers 2 --tuiAtajos disponibles dentro del monitor:
| Atajo | Acción |
|---|---|
F1 |
Muestra el contexto operativo |
F5 |
Repite la misma solicitud ya validada |
Ctrl+X |
Cancela los motores de forma cooperativa |
Ctrl+L |
Limpia únicamente el flujo de eventos |
Q o F10 |
Sale del monitor |
Al terminar, la pantalla conserva los hallazgos ordenados, estadísticas, evidencia disponible y las rutas de los reportes. En sesiones multiobjetivo, cada endpoint muestra el objetivo solicitado y la dirección resuelta; el panel de ejecución mantiene contadores de objetivos activos, completados y fallidos, y el resumen final conserva los resultados correctos aunque otro objetivo falle.
Los perfiles fijan valores reproducibles, pero las opciones manuales siguen teniendo prioridad:
# Puertos comunes, baja concurrencia y sin banners
cicadaport 192.168.1.10 --profile safe
# TCP 1-1000 con Rust y banners Go
cicadaport 192.168.1.10 --profile standard
# TCP 1-65535 y enumeración de servicios abiertos
cicadaport 192.168.1.10 --profile deep
# Configuración manual conservando el flujo especializado
cicadaport 192.168.1.10 --profile custom -p 22-443Las opciones manuales de puertos, concurrencia, timeout, banners y salida siguen
teniendo prioridad sobre el perfil. El motor TCP permanece fijado en Rust y la
fase de banners, cuando está activa, permanece fijada en Go, sin selectores ni
fallback. El perfil deep amplía la cobertura TCP, pero no sustituye por sí
solo las técnicas de descubrimiento, UDP, SYN, identificación de sistema
operativo o scripting especializado de otras herramientas.
El objetivo posicional puede contener una IP, un hostname, un CIDR, un rango o
una lista separada por comas. --target añade especificaciones y puede
repetirse. --target-file incorpora archivos UTF-8 con comentarios iniciados
por #; --exclude elimina objetivos antes de resolverlos.
# Dos direcciones explícitas del laboratorio local
cicadaport 127.0.0.1 --target 127.0.0.2 \
-p 20-25 --threads 8 --target-workers 2
# Rango local con una exclusión
cicadaport 127.0.0.1-127.0.0.4 \
--exclude 127.0.0.3 \
-p 20-25 --threads 12 --target-workers 3
# Archivo de objetivos autorizados
cicadaport --target-file objetivos.txt \
--exclude 127.0.0.2 \
--report-dir reports/laboratorioCon varios objetivos, cada dirección resuelta recibe un nombre de reporte
único dentro de --report-dir. --output se reserva para sesiones con un solo
objetivo porque representa una ruta exacta. La salida de consola resume
objetivos solicitados, direcciones resueltas, éxitos, fallos, hallazgos y
presupuesto efectivo de concurrencia. Si al menos un objetivo falla, los
reportes correctos se conservan y la CLI termina con código 2.
El TUI admite el mismo contrato multiobjetivo que la salida lineal. El
progreso global, los fallos parciales y la identidad de cada endpoint proceden
de los eventos emitidos por ScanOrchestrator.run_many(). F5 repite el lote
inmutable completo y Ctrl+X propaga la cancelación cooperativa a todos los
motores activos. Los parámetros continúan bloqueados desde la CLI: el dashboard
monitoriza la sesión, pero no edita objetivos ni opciones durante la ejecución.
# Monitor multiobjetivo con presupuesto global de cuatro hilos
cicadaport 127.0.0.1 --target 127.0.0.2 \
-p 4444 --threads 4 --target-workers 2 --tuiEl escaneo TCP no envía cargas de aplicación por defecto. Para solicitar banners de forma explícita:
# Rust ejecuta TCP y Go captura los banners solicitados
cicadaport localhost --banner-grab
# Un perfil con banners puede desactivarlos explícitamente
cicadaport localhost --profile standard --no-banner-grabGo negocia TLS en los puertos cifrados conocidos, envía un único HEAD solo a
una lista cerrada de puertos HTTP/HTTPS y se limita a lectura pasiva en los
demás servicios.
Las sesiones nuevas usan Session Store v2 sobre SQLite WAL. Los resultados se
confirman por lotes normalizados y el checkpoint público v1 se reconstruye al
leer, por lo que la compatibilidad externa permanece estable sin generar dos
archivos completos por puerto. El perfil balanced confirma hasta 128
resultados por transacción; el perfil strict confirma uno por transacción.
Las sesiones v1 se migran de manera verificable, idempotente y de solo lectura:
los archivos fuente permanecen intactos para auditoría y rollback.
Los reportes, eventos y bundles usan directorios 0700, archivos 0600,
creación exclusiva, temporales en el mismo filesystem, fsync, rechazo de
symlinks y no sobrescritura por defecto. TXT, CSV y HTML neutralizan controles
C0/C1, ESC/BEL y marcas bidi/invisibles antes de presentarlos a una persona.
Cada escaneo muestra en la terminal todos los puertos abiertos, ordenados por
protocolo y número de puerto. Para cada hallazgo se incluyen el servicio, el
banner disponible y el tiempo de respuesta. El mismo escaneo guarda además un
reporte dentro de reports/.
# Muestra los hallazgos y crea automáticamente reports/scan_report_*.txt
cicadaport localhost -p 1-1000
# Muestra los mismos hallazgos y guarda el reporte persistente como JSON
cicadaport localhost -p 1-1000 --format json
# Un nombre simple se guarda dentro de reports/
cicadaport localhost -p 1-1000 --output auditoria --format html
# Cambiar la carpeta predeterminada
cicadaport localhost -p 1-1000 --report-dir resultados
# Una ruta explícita se respeta y sus carpetas se crean si no existen
cicadaport localhost -p 1-1000 --output resultados/cliente/reporte.csv --format csvLas extensiones automáticas son .txt, .json, .csv y .html. Los nombres
automáticos nunca sobrescriben un reporte existente: cuando coinciden objetivo
y segundo de ejecución, se añade un sufijo como _2 o _3. Si el escaneo no
detecta puertos abiertos, la terminal y el reporte TXT lo indican expresamente.
Los reportes añaden de forma compatible el estado, la razón, la dirección y la
técnica. TXT, JSON, CSV y HTML identifican también los motores efectivos
rust y go —o no usado cuando la fase de banners está desactivada—; JSON
incluye además la versión del contrato.
TASK 5.5 fija cada GitHub Action por SHA completa, instala las herramientas de release desde un lock Python con hashes, genera SBOM CycloneDX 1.6 y manifiestos de identidad, compara builds byte a byte y configura attestations Sigstore/SLSA. SUBTASK 5.6 reutiliza estos controles para construir y validar RC2 de forma reproducible. Los artefactos de CI siguen siendo candidatos privados: no se crea etiqueta, no se publica una GitHub Release y no se amplían capacidades de red.