Resumen
Severidad: 🔴 CRÍTICA (ALTA)
Detectado: Auditoría Suprema Etapa 3 (2026-06-11)
Arreglado: commit@6f8a46d
Endpoints /api/workspace/perf-review?ai=true y /api/workspace/finops?ai=true mandaban a Google AI Studio datos agregados de todas las organizaciones (clientes) con sus nombres reales + texto de queries — una fuga cross-tenant total de la plataforma.
El problema
El análisis de rendimiento (Performance Review) y finanzas (FinOps) agrega métricas de todas las organizaciones (tenant_usage, slow_queries) en un diccionario. Cuando el usuario activa ?ai=true para obtener análisis inteligente, el código pasaba ese diccionario íntegro a Google AI Studio sin anonimización:
# ANTES (vulnerable)
return AIOperations.detect_anomalies(data, tone=tone)
# → data contiene {"tenant_usage": [{"id": 1, "name": "ACME Corp", ...}, {"id": 2, "name": "Globex", ...}], ...}
# → enviado crudamente a Google
Un observador externo (Google o un atacante entre medio) podría:
- Ver qué empresas son clientes de CreaRack
- Ver sus patrones de consumo (racks, dispositivos)
- Ver SQL crudo de queries lentas (posibles secretos: contraseñas, API keys)
La solución
Función _anonymize_for_ai() en core/workspace_api.py:
- Sustituye nombres reales por etiquetas opacas:
"ACME Corp"→"org-1" - Preserva la distribución: Los conteos (racks, dispositivos) se mantienen idénticos
- Redacta secretos en queries: Aplica
redact_secrets()al texto SQL - Trabaja sobre una copia: La respuesta al cliente sigue mostrando nombres reales
def _anonymize_for_ai(data):
"""Quita identificadores de tenant y redacta secretos ANTES de enviar a un LLM externo."""
d = copy.deepcopy(data)
for row in d.get("tenant_usage", []) or []:
if isinstance(row, dict) and "name" in row:
row["name"] = f"org-{row.get('id', '?')}"
for q in d.get("slow_queries", []) or []:
if isinstance(q, dict) and q.get("query"):
q["query"] = redact_secrets(str(q["query"]))
return d
Se invoca antes de pasar a AIOperations:
def _ai_analyze_perf(data, tone="technical"):
try:
from core.services.ai_operations import AIOperations
return AIOperations.detect_anomalies(_anonymize_for_ai(data), tone=tone)
except Exception as e:
logger.warning("workspace perf-review AI analysis failed: %s", redact_secrets(str(e)))
return "AI analysis temporarily unavailable"
Impacto
- Antes: Cualquiera con acceso a Google logs podía reidentificar clientes
- Después: Google AI (y cualquier tercero) ve distribuciones numéricas sin identidad
- El LLM sigue siendo capaz de analizar anomalías: La IA necesita números, no nombres
Tests
tests/api/test_config_ia.py:
test_anonymize_for_ai_strips_org_names_and_redacts_queries()— redacción correctatest_anonymize_for_ai_does_not_mutate_original()— no muta la respuesta al clientetest_anonymize_for_ai_tolerates_missing_keys()— robusto ante datos incompletos
Diferidos (motivo documentado)
Cambios de contrato / infra que no se cierran en esta tanda:
/media/servido sin auth → rompería rendering frontendglobal_exception_handlerfiltrastr(exc)a ~509 endpoints → requiere barrido transversal T4- Llamada IA síncrona en request → T2 (síncrono→Huey), toca contrato
Véase también
- [[feature—config-ia—auditoria-suprema-etapa-3]]
- [[entity—core—service—anonymize-for-ai]]
- [[concept—security—data-isolation]]
- [[decision—20260611—agent-jwt-secret-dedicada]]
- [[concept—saas—multi-tenancy]]