Volver a la wiki

Cola de auditoría B (task #286): CNS e insights de IA — WS sin gate de permiso, apply/rollback sin Agente, prompts sin sanear

Cuándo

04-09-2026 · commit fbcfec01 (PR #497, v1.105.0). Segunda entrega de la “cola de auditoría” del task #286 sobre el dominio monitoring — la primera (v1.104.0, “cola auditoría A”) cubrió sondas y targets; esta cierra CNS e insights de IA con 11 hallazgos MEDIA propios más 11 adicionales de una revisión en Opus sobre el mismo PR. 34 tests nuevos en tests/api/test_audit_monitoring_b.py.

Síntomas visibles

  1. WebSocket de Observatory (MonitoringConsumer.connect) aceptaba cualquier usuario de la organización sin comprobar observatory:view, y el handler insight_update reenviaba diagnósticos de CNS (resumen, nivel de riesgo) a usuarios sin cns:view — el aislamiento por tenant no sustituye el nivel de permiso dentro de la org, mismo patrón que la Tanda 1 de la Auditoría Suprema 2 (ver relacionada) pero esta vez en un canal WS que no pasa por el decorador REST habitual.
  2. AIInsight.save() asignaba case_number con un patrón leer-luego-escribir sin bloqueo: dos insights creados a la vez en la misma organización podían chocar contra unique_case_number_per_org.
  3. apply/rollback marcaban el insight como EXECUTING y solo DESPUÉS intentaban despachar el comando al Agente — si no había Agente conectado, o el envío fallaba entre el gate y el despacho, el insight quedaba EXECUTING para siempre: ni reaplicable ni caducable, con una respuesta 200 que mentía sobre el resultado.
  4. El endpoint /result aceptaba resultados de ejecución sin comprobar que el insight estuviera realmente en EXECUTING, y el output no tenía límite de tamaño.
  5. explain/revise construían el prompt al LLM concatenando a mano campos de AIInsight (diagnóstico, nombre del target, IP) sin pasar por wrap_untrusted — esos campos vienen de telemetría SNMP/SSH no confiable o de un nombre de dispositivo manipulable, así que un dato malicioso podía inyectar directivas en el prompt de seguimiento.
  6. receive_insight (la ingesta del Agente) no aplicaba ai_cost_guard en la sesión, y varios endpoints de listado no acotaban la paginación.

Causa raíz

Cada punto tiene su causa puntual (falta de gate en el WS, falta de lock, orden de operaciones optimista, falta de saneado), pero todos comparten el patrón ya visto en tandas anteriores: código que asume el camino feliz — Agente siempre conectado, LLM siempre bien portado, WS con el mismo nivel de confianza que la sesión HTTP — sin comprobar el caso adverso antes de mutar estado o de construir el prompt.

Fix aplicado

Commit fbcfec012d46a7e3c4c244f67936954701d52d09 (PR #497):

Lecciones

El gate por nivel de permiso en WebSocket es el mismo hueco de siempre —aislamiento por tenant no es lo mismo que nivel de permiso—, pero esta vez en un canal que no hereda el chequeo del decorador REST: cada AsyncWebsocketConsumer de Channels necesita su propio gate explícito. Y el saneado de prompts (wrap_untrusted) tiene que aplicarse en TODOS los puntos donde se construye contexto para el LLM, no solo en el primero que se auditó — explain/revise llevaban meses construyendo el prompt a mano sin pasar por él.

Preventivos futuros

Véase también

Subir