Volver a la wiki

Auditoría Suprema · Módulo network sub-área 1 (Discovery/Auto-Provision) — Etapa 3

Resumen ejecutivo

Sesión s113 (2026-06-07): auditoría exhaustiva del motor de descubrimiento de dispositivos de red (Auto-Provision) de CreaRack Pro. La sub-área 1 comprende 4 slices funcionales y toca la mayor superficie de seguridad del módulo network (interacción con redes, manejo de credenciales SNMP/SSH, escaneos nmap).

Resultado: 37 hallazgos confirmados (11 ALTA / 16 MEDIA / 10 BAJA) auditorios en una tanda única (Regla 22), alineados con el plan de hardening. Sin migraciones de datos.


Alcance auditado

Slices analizados

  1. discovery-api: endpoints Ninja (discover, bulk_enrich, bulk_discover, deep_discover, deep_discover_status) + esquemas de Auto-Provision.

    • Foco crítico: autorización (require_perm faltante), SSRF/escaneo red interna, IDOR, fuga de credenciales, validación de inputs.
  2. orchestrator-snmp: orquestador central (service.py) + stages de provisión (provision_stages.py).

    • Foco crítico: inyección de argumentos a nmap, redirects sin control en HTTP, bulk sin límite, fugas de credenciales SNMP, trabajo síncrono pesado.
  3. snmp-auth-ssh: autenticación SNMPv3 (snmp_auth.py), helpers SNMP + SSH + LLDP (ssh_stage.py, lldp_stage.py).

    • Foco crítico: verificación de host-key SSH, credenciales en logs, inyección en comandos, parseo frágil de datos no confiables del dispositivo.
  4. classify-scoring-stencil: clasificación automática, scoring, matching de plantillas (auto_classifier.py, scoring.py, stencil_matching.py, constants.py).

    • Foco crítico: ReDoS en regex, aislamiento cross-tenant de stencils, lógica de confianza, rendimiento.

Hallazgos confirmados

ALTA severidad (11)

IDTítuloArchivoSolución aplicada
A1Broken Access Control: 5 endpoints sin require_permnetwork/api/discovery.pyAhora exigen require_perm(network, edit) para acciones / view para status
A2SSRF / escaneo red interna: IP/subnet arbitrarias sin guardnetwork/services/device_discovery/service.pyNuevo scan_guard.py: bloquea loopback/link-local/metadata/unspecified, permite RFC1918
A3detect_vendor_http sigue redirects a destinos arbitrariosnetwork/services/device_discovery/service.pyallow_redirects=False aplicado
A4DeviceProfileOut expone snmp_community al cliente (fuga cred)network/api/common.pyDeuda: PR propio — remover del schema Out
A5deep_discover_profile modo “rest” devuelve community/v3_keys en bodynetwork/api/discovery.pyDeuda: PR propio — rediseño de protocolo Agent
A6IDOR en deep_discover_status: carga caché sin filtrar por orgnetwork/api/discovery.pyAhora carga DeviceProfile por org antes de leer caché → 404 cross-tenant
A7discover_nmap argument injection: ip arbitraria como flag de nmapnetwork/services/device_discovery/service.pyIP validada como literal seguro en schema + servicio
A8SNMPv3 sin cifrado en reposo (credenciales en claro)network/models.pyDeuda: PR propio — cifrado en reposo (Plan Hardening E)
A9SSH sin verificación de host-key (TOFU/MITM)network/services/device_discovery/ssh_stage.pyDeuda: PR propio — activar host-key verification en scrapli
A10build_snmp_auth degrada en silencio a noAuth/noPriv + import roto v3network/services/device_discovery/snmp_auth.pyAhora lanza ValueError ante protocolo desconocido; import corregido a pysnmp.hlapi.v3arch.asyncio
A11match_stencil cross-tenant: sugerencia de stencil de otra orgnetwork/services/device_discovery/stencil_matching.pyFiltra org propia + globales; suggested_stencil_id aislado por tenant

MEDIA severidad (16)

IDTítuloCorrección
M1Escaneo /16 sin límite: bulk_discover_subnet(subnet) expande entero sin capSchema: subnet acotada a ≤1024 hosts; validación en parse + servicio
M2Validadores de schema débiles: ip/subnet/snmp_version/max_concurrent sin restriccionesValidadores Pydantic: IP literal + subnet formato correcto + snmp_version ∈ {v2c,v3} + max_concurrent 1–50
M3discover/bulk-discover devuelven {"error": str(e)}: fuga de detalle internoMensaje genérico; detalles solo en logger.error
M4org=None no genera error explícitoAhora org or raise 403 antes de instanciar servicio
M5Sin auditoría de acciones en discover/bulk/deeplog_action(NETWORK, ...) en cada endpoint
M6bulk_discover_subnet sin rate-limit: DoS por repeticiónDeuda menor (rate-limit a nivel middleware)
M7try_snmp_communities prueba 20+ communities: comportamiento de fuerza brutaDocumentado; lista capped a 20 (linea ya existente)
M8Parseo fragil de sys_descr / walk SNMP: datos no confiables del dispositivoparse_sys_descr con try/except; walk_if_table avisa si trunca por iteraciones
M9Logs SSH con str(exc) a nivel ERROR: fuga de credencialesLogs limpios: logger.debug para except; ERROR solo con mensaje genérico
M10discover_neighbors_snmp (LLDP/CDP) no funciona en SNMPv3Ahora acepta auth_data en la firma; except: pass → logger.debug
M11OIDs construidos: riesgo de OID injection bajo (pero documentable)OIDs son constantes; sin input directo del usuario
M12DeepDiscover sin verificación de queries cruzadasVerificación de org ya presente en get_object_or_404
M13match_stencil sin paginación/límite: carga todos + fuzzy match sin capFiltro .only(...) + cap de 200 candidatos en fuzzy
M14Dispositivo hallado solo por nmap: dead code en clasificaciónArreglado: se clasifica correctamente (no siempre other)
M15deep_discover_profile timeout sin feedback visualDeuda menor (UI/frontend)
M16Bulk_enrich: max_concurrent sin tope → DoSSchema: max_concurrent capped a 1–50

BAJA severidad (10)

IDTítuloCorrección
B1Código muerto / ternarios imposiblesLimpiado: calculate_confidence ternario sin rama
B2Sanitización de inputs (MAC, OID) frágilsanitize_agent_mac valida contra patrón MAC; _extract_enterprise_number tolera prefijo iso.
B3Imports lazy de v3 no thread-safeOrden de lazy-import corregido
B4sanitize_agent_mac sin validación post-saneadoAhora valida resultado contra patrón MAC real
B5Walk SNMP sin aviso al truncarAviso en logger.warning si se alcanza límite de iteraciones
B6Logs excesivos en discovery: ruido en observabilidadNiveles ajustados: debug para SNMP/SSH verboso
B7device_type no saneado antes de scrapli: posibles inyecciones blandasSaneado: solo [a-zA-Z0-9_-]
B8except genericos: except Exception: pass traga erroresReemplazados por except SpecificException: logger.debug
B9_extract_enterprise_number: parseo fragil de OIDTolera prefijo iso. y puntos en notación alternativa
B10Duplicación en validadores de SNMP versionUnificado: validador reutilizable _validate_snmp_version

Fixes aplicados en esta tanda

Seguridad (atacante)

Validación (inputs)

Auditoría + observabilidad

Robustez


Deuda explícita (PRs propios)

Cuatro mejoras requieren cambios de contrato o migración, así que van por separado:

IDTítuloMotivo
A4Remover snmp_community de DeviceProfileOutCambio de schema API (compat); clientes existentes no esperan este campo escondido
A5Rediseño protocolo Local Agent: distribución de credencialesCambio de protocolo WebSocket; todos los agentes deben actualizarse
A7Mover discover masivo a tarea Huey asincrónicaRequiere refactor del endpoint (request/response); posible cambio de schema
A8Cifrado en reposo de credenciales SNMP (Plan Hardening E)Migración de datos; necesita reversión/upgrade de schema Django
A9SSH host-key verification (scrapli auth_strict_key=True)Requiere inventario de host-keys o cambio de UX (user prompt)

Estado en CHANGELOG: “audit-only” (sesión s111, 2026-06-05). Las rectificaciones se planifican en sesión aparte por decisión de Edu.


Cobertura de tests

Nuevo fichero: tests/api/test_network_sa1.py (23 tests)

Suite completa: 349 passed, 1 skipped (network completo en verde).


Arquitectura tocada

network/
├── api/
│   ├── discovery.py          [MODIFICADO] — autorización + error genérico + log_action
│   └── common.py             [MODIFICADO] — validadores Pydantic (ip, subnet, snmp_version, max_concurrent)
├── services/device_discovery/
│   ├── service.py            [MODIFICADO] — nmap arg validation, http no-redirects, bulk cap
│   ├── provision_stages.py   [MODIFICADO] — cross-tenant checks reforzados
│   ├── snmp_auth.py          [MODIFICADO] — v3 fallback removed, import fixed
│   ├── ssh_stage.py          [MODIFICADO] — logs limpios, device_type sanitizado
│   ├── lldp_stage.py         [MODIFICADO] — SNMPv3 auth_data
│   ├── auto_classifier.py    [MODIFICADO] — dead code nmap-only
│   ├── scoring.py            [MODIFICADO] — ternario muerto
│   ├── stencil_matching.py   [MODIFICADO] — tenant_id filtering, cap 200
│   ├── scan_guard.py         [NUEVO] — bloquea loopback/metadata/unspecified, permite RFC1918
│   └── constants.py          [MODIFICADO] — COMMON_SNMP_COMMUNITIES cap
├── models.py                 [SIN CAMBIOS] — credenciales en claro (deuda A8)
└── tests/
    └── test_network_sa1.py   [NUEVO] — 23 tests de cobertura

core/
└── utils.py                  [POSIBLE] — log_action (puede existir ya)

Reglas y principios aplicados


Sesión de contexto


Véase también

Subir