Módulo 1: Threat Modeling Andes Cargo

6. El radio de explosión, revisitado: ¿qué lo hubiera detenido?

Descripción

Si viniste de terraform-and-iac-guide o cicd-and-gitops-on-aws-guide, ya conoces el caso: en febrero de 2026, un agente de Claude Code corrió terraform destroy contra la infraestructura de producción de DataTalks.Club y borró, con un solo comando, 2,5 años de datos. Esta lección no lo vuelve a contar desde cero para generar alarma otra vez —lo retoma con un ángulo genuinamente nuevo: en vez de preguntar "¿qué salió mal?", pregunta "¿qué control automatizado, no dependiente de que un humano prestara atención en ese momento exacto, hubiera detenido esto?". La respuesta mapea, punto por punto, contra TM-06 de la lección 5 y contra los módulos que esta guía todavía tiene que construir.

Conexión con el módulo

TM-06 (Denial of service, lección 5) identificó que nada en andes-cargo-infra/ impide hoy un plan que destruye Shipments. Esta lección es la evidencia de por qué ese riesgo no es teórico: un caso real, con cifras verificadas, donde exactamente ese tipo de fallo —una destrucción no revisada con la atención suficiente— costó 2,5 años de datos de producción. La diferencia con terraform-and-iac-guide M8.5 y cicd-and-gitops-on-aws-guide: ahí el caso cerraba con "lee el plan completo, siempre". Aquí, el caso abre la pregunta que el resto de esta guía contesta: ¿qué pasa cuando la disciplina de leer el plan falla, como falló aquí, a pesar de la advertencia?


El caso, en las cifras exactas que sostienen esta lección

En febrero de 2026, Alexey Grigorev —fundador de DataTalks.Club, la plataforma de cursos gratuitos— usaba Claude Code para migrar un sitio estático hacia la infraestructura de Terraform ya existente de la plataforma, reutilizando el mismo proyecto en vez de crear uno separado, para ahorrar entre cinco y diez dólares mensuales de infraestructura duplicada. En algún punto de ese trabajo, el agente reemplazó el terraform.tfstate activo por una versión vieja, extraída de un archivo .zip archivado. Con ese state desactualizado como referencia, Claude Code interpretó buena parte de la infraestructura de producción real como recursos huérfanos, y corrió terraform destroy -auto-approve.

El resultado: la VPC completa, el clúster de ECS, los balanceadores de carga, el bastion host, la base de datos RDS y sus snapshots automáticos, todos borrados. Cerca de 2,5 años de datos —envíos de tareas de estudiantes, proyectos, tablas de posiciones de los cursos— desaparecieron con ese único comando. La tabla courses_answer, una de las afectadas, tenía 1.943.200 filas antes del incidente. La recuperación tomó cerca de 24 horas de trabajo con soporte empresarial de AWS, hasta que se encontró un snapshot interno que ni siquiera aparecía listado en la consola de AWS — el mecanismo que permitió restaurar la tabla con sus 1.943.200 filas intactas. Sin ese snapshot oculto, la pérdida habría sido permanente.

El detalle que hace este caso pedagógicamente valioso, no solo alarmante: Claude Code había señalado el riesgo del destroy antes de ejecutarlo, y el humano al mando lo aprobó de todas formas. No fue un agente actuando en secreto — fue una advertencia real, ignorada bajo la presión de seguir adelante con el trabajo.


La cadena de fallos, paso por paso

Antes de mapear controles, vale la pena descomponer el incidente en su secuencia exacta — porque la pregunta de esta lección ("¿qué lo hubiera detenido?") solo tiene sentido si sabes en qué punto exacto de la cadena cada control actuaría:

   LA CADENA DE FALLOS — DataTalks.Club, febrero 2026

   (1) El state activo se reemplaza por una versión vieja (.zip archivado)
              │
              ▼
   (2) Claude Code interpreta infraestructura real como huérfana
              │
              ▼
   (3) El agente propone `terraform destroy -auto-approve`
              │
              ▼
   (4) Claude Code SEÑALA el riesgo del destroy, explícitamente
              │
              ▼
   (5) El humano aprueba el destroy de todas formas, sin revisar el plan completo
              │
              ▼
   (6) `terraform destroy` se ejecuta: VPC, ECS, ALB, bastion, RDS + snapshots
              │
              ▼
   (7) Ningún deletion protection detiene la eliminación de RDS
              │
              ▼
   RESULTADO: 2,5 años de datos, 1.943.200 filas, ~24h de recuperación

Siete pasos. El punto (4) es el que hace de este caso algo más que "un agente descontrolado" — la advertencia existió. Los puntos (5) y (7) son los dos únicos lugares de la cadena donde algo —humano o mecanismo— podría haber roto la secuencia antes del resultado final, y ninguno de los dos lo hizo.


Mapeando los controles de esta guía contra la cadena exacta

Esta es la parte nueva de esta lección, la que no vas a encontrar en terraform-and-iac-guide M8.5: para cada control que los Módulos 2 a 7 de esta guía construyen, ¿en qué punto exacto de la cadena de arriba habría actuado, y lo habría detenido de verdad o solo lo habría hecho más visible?

Identidad federada y mínimo privilegio (Módulo 2) — actúa antes del punto (6), parcialmente

Una identidad OIDC con trust policy acotada a una rama y un repositorio específicos (M2.5) no habría evitado que un agente autorizado —que es exactamente lo que Claude Code era en este caso, no un atacante externo— propusiera el destroy. Donde sí actúa: mínimo privilegio (M2.7) aplicado a la identidad que ejecuta terraform apply en producción podría, en principio, excluir explícitamente iam:*/rds:DeleteDBInstance/acciones de eliminación de la política de esa identidad para ciertos recursos marcados como críticos — pero esto depende de que alguien haya diseñado esa restricción de antemano, con el mismo tipo de disciplina que falló en el punto (1). Conclusión honesta: reduce la superficie, no la elimina — el mismo problema de fondo (una identidad con permiso amplio, usada sin la revisión suficiente) sigue presente si el permiso de destroy no se restringió explícitamente.

Policy-as-code preventivo (Módulo 4) — actúa exactamente en el punto (5)/(6), y es la respuesta más directa

Esta es la pieza central de la respuesta de esta guía. Una política Rego evaluada con conftest test sobre el plan antes de que exista la posibilidad de aplicarlo —el mismo mecanismo que vas a construir en el M4.6 (no-destroy-shipments.rego) para el caso análogo de Andes Cargo— no depende de que un humano lea el plan con atención. Depende de que el plan contenga un action: "delete" sobre un recurso marcado como crítico, y de que esa condición, verificada mecánicamente, falle el job del pipeline sin excepción. Si DataTalks.Club hubiera tenido una política equivalente a no-destroy-shipments.rego sobre su RDS de producción, el paso (6) de la cadena nunca hubiera llegado a ejecutarse — no porque el agente hubiera "decidido" no proponerlo, sino porque el pipeline hubiera fallado el job de verificación de política antes de que apply fuera siquiera una opción disponible. Esta es la diferencia exacta entre una "compuerta" que depende de la atención humana (lo que falló en el punto (5)) y un guardrail "build in" que no.

Escaneo de IaC (Módulo 5) — no aplica directamente a este caso

Trivy y Checkov evalúan configuración declarada —qué recursos existen y cómo están configurados—, no acciones de destroy propuestas sobre el state. Esta pieza no era la respuesta a este incidente específico, y sería deshonesto forzar la conexión: se nombra aquí precisamente para que quede claro que no todo control de esta guía responde a todo incidente.

Cadena de suministro (Módulo 6) — no aplica directamente a este caso

cosign firma y verifica artefactos de despliegue —código que se despliega—, no comandos de infraestructura que se ejecutan contra el state. Misma razón que el Módulo 5: nombrada por completitud, no forzada como respuesta.

Guardrails detectivos: CloudTrail (Módulo 7) — actúa después del punto (6), y no habría acortado la recuperación

Un trail de CloudTrail hubiera confirmado, con certeza y sin depender de memoria humana, exactamente qué identidad ejecutó el destroy y en qué momento exacto — valioso para la reconstrucción posterior del incidente y para una alerta en tiempo real si existiera un sistema que reaccionara a ese evento. Pero es honesto decir con precisión qué NO habría hecho: el cuello de botella real de la recuperación —las 24 horas— fue encontrar un snapshot que ni siquiera aparecía listado en la consola, un problema de recuperación de datos, no de atribución. CloudTrail responde "quién y cuándo", no "cómo lo recuperamos más rápido". Es un guardrail real, pero de la categoría equivocada para acortar esta recuperación específica — la distinción exacta que el Módulo 7 de esta guía nombra entre preventivo y detectivo.

La pieza nombrada, no construida en esta guía: deletion protection / prevent_destroy

El punto (7) de la cadena —ningún deletion protection en la base RDS ni en el resto de los recursos— es, en los hechos, la última línea de defensa que faltó. Esta guía no construye esa salvaguarda para Andes Cargo como pieza central de ningún módulo —está fuera de su alcance definido—, pero vale la pena nombrarla aquí con precisión: el argumento prevent_destroy dentro de un bloque lifecycle de Terraform, o el deletion protection nativo de RDS/DynamoDB, actúa en el último eslabón posible de la cadena, después de que todo lo demás falló. Ninguna guía de este ecosistema la construye todavía para Andes Cargo — es, honestamente, un hueco declarado, no una pieza escondida.


La conclusión que reencuadra la advertencia de mercado

src/paths/aws-cloud-ecosystem/VALIDACION.md es explícito sobre por qué este caso importa para 2026: "el vector dominante de 2026 ya no es el humano descuidado sino el agente con credenciales". Vale la pena leer esa frase con precisión, porque es fácil malinterpretarla como "no confíes en los agentes". La lectura correcta, la que esta lección sostiene con la cadena completa de arriba: el vector de riesgo no es que un agente actúe — es que la única capa de control entre "algo propone una acción irreversible" y "la acción se ejecuta" siga siendo, en 2026, la atención de un humano en un momento específico, exactamente como lo era antes de que existieran agentes con acceso a infraestructura. Un humano apurado, aprobando un destroy sin leer el plan completo, produce el mismo resultado que un agente cuya advertencia se ignora. El punto (5) de la cadena de este incidente habría fallado igual con un ingeniero humano corriendo el mismo comando bajo la misma prisa.

Lo que cambió de verdad en 2026 no es "los agentes son peligrosos" — es que la velocidad a la que un agente puede proponer y ejecutar una secuencia completa de comandos comprime la ventana de tiempo en la que un humano tendría, en teoría, la oportunidad de revisar con cuidado. Menos tiempo entre "se propone" y "se ejecuta" significa que un control que depende de la atención humana tiene, literalmente, menos oportunidad de funcionar. La respuesta correcta no es "prohibir agentes" — es exactamente la tesis del Módulo 4 de esta guía: mover el punto de control de "un humano lee esto con cuidado" a "un mecanismo automático lo bloquea sin excepción", sin importar qué o quién propuso el cambio.


Errores comunes

Concluir que esta lección ya construyó la protección contra TM-06 (de expectativa, retomado de la lección 1). Qué pasa: alguien termina esta lección pensando que Andes Cargo ya está protegido contra un destroy accidental de Shipments. Cómo detectarlo: si buscas, después de esta lección, algún cambio real en andes-cargo-infra/. Cómo corregirlo: esta lección es análisis, no construcción — el mapeo de qué control hubiera actuado en qué punto de la cadena. La política real (no-destroy-shipments.rego) se construye recién en el Módulo 4, lección 6.

Buscar en este caso una respuesta única, ignorando que distintos controles actúan en distintos puntos de la cadena (de simplificación). Qué pasa: alguien, después de leer esta lección, concluye que "la respuesta" es únicamente conftest, y descarta CloudTrail o deletion protection como irrelevantes. Cómo detectarlo: si tu resumen del caso tiene un solo control "correcto" en vez de un mapa de qué hace qué. Cómo corregirlo: conftest es la respuesta más directa al punto (5)/(6) de la cadena —la más importante, porque previene el resultado en vez de solo detectarlo o mitigarlo—, pero un sistema de seguridad real usa capas independientes: CloudTrail no previene, pero permite reconstruir; deletion protection es la última línea si todo lo anterior falla. Ningún control único es "la solución completa" — la defensa en profundidad, el mismo principio que ya viste en terraform-and-iac-guide, sigue aplicando aquí.

Tratar "el agente señaló el riesgo" como si eso ya fuera suficiente control (de lectura del caso). Qué pasa: alguien lee que Claude Code advirtió el riesgo antes de ejecutar, y concluye que el sistema "funcionó como debía" porque la advertencia existió. Cómo detectarlo: si tu conclusión es "el agente hizo lo correcto, el humano falló". Cómo corregirlo: una advertencia que un humano puede ignorar sin ninguna consecuencia estructural no es un control — es información. La diferencia central entre esta lección y el punto (4) de la cadena es exactamente esa: los controles del Módulo 4 de esta guía no piden permiso ni advierten, bloquean. Una advertencia que se puede aprobar sin fricción es, en la práctica, equivalente a no tener ninguna advertencia.


Ejercicios

Ejercicio 1 — Ubica el punto exacto de la cadena donde conftest (M4) habría actuado. De los siete puntos de la cadena de fallos de esta lección, ¿en cuál actuaría exactamente una política no-destroy-shipments.rego equivalente, y por qué ese punto y no otro?

Ver solución

En el punto (6) — antes de que terraform destroy se ejecutara de verdad, evaluando el plan que el punto (3) ya había generado. La política no interviene en (1) (el state corrupto), ni en (2) (la interpretación del agente), ni en (4) (la advertencia) — interviene específicamente en el momento en que ese plan, ya generado, tendría que pasar por una verificación automática antes de convertirse en un apply real. Si el plan contiene una acción de eliminación sobre un recurso marcado como crítico, conftest test falla el job, y el pipeline nunca llega al punto (6) tal como ocurrió en el caso real.

Ejercicio 2 — Explica por qué CloudTrail (M7) no es "inútil" para este caso, aunque no hubiera acortado la recuperación. Un compañero, después de leer que CloudTrail no habría reducido las 24 horas de recuperación, concluye que esa pieza "no sirve para nada" en este contexto. ¿Estás de acuerdo?

Ver solución

En desacuerdo. CloudTrail responde una pregunta distinta y también valiosa: no "¿cómo recuperamos los datos más rápido?", sino "¿podemos confirmar, con evidencia y no con memoria, exactamente qué pasó, quién lo ejecutó, y en qué momento?" — la misma distinción de Repudiation (TM-03) de la lección 5. Un guardrail detectivo no compite con uno preventivo por ser "el más útil" — cumple una función distinta, la de dejar evidencia consultable, que un guardrail preventivo por sí solo no provee (si conftest bloquea el destroy, no queda ningún incidente que investigar; pero un sistema real necesita ambas capas, porque no todo lo que sale mal fue bloqueado con éxito).

Ejercicio 3 — Diseña una política mínima, sin escribir el Rego completo. Si tuvieras que describir, en lenguaje natural, la regla que no-destroy-shipments.rego (M4.6) va a implementar, ¿qué condición exacta del plan en JSON debería buscar para bloquearlo? Pista: revisa la estructura resource_changes/actions que la lección 5 del Módulo 4 de esta guía va a explorar.

Ver solución

La regla, en lenguaje natural: "para cada elemento de resource_changes en el plan, si el tipo de recurso es aws_dynamodb_table, el nombre coincide con Shipments, y el arreglo change.actions contiene el valor \"delete\" (solo o combinado, como en [\"delete\", \"create\"] de un reemplazo), la política falla y devuelve un mensaje explicando qué recurso y por qué". No hace falta el Rego exacto todavía —eso llega en el Módulo 4 de esta guía—; el objetivo de este ejercicio es identificar correctamente que la condición vive en la combinación de type + name/address + actions dentro de cada elemento de resource_changes, la misma estructura que ya nombró el diseño de esta guía para el M4.5.


Resumen y siguiente paso

En esta lección retomaste el incidente del destroy de Claude Code contra DataTalks.Club —2,5 años de datos, 1.943.200 filas en courses_answer, ~24 horas de recuperación, la advertencia del agente ignorada por el humano al mando— desde un ángulo nuevo: descompusiste la cadena completa de siete fallos, y mapeaste cada control que los Módulos 2 a 7 de esta guía construyen contra el punto exacto donde habría actuado. La conclusión central: conftest (M4) es la respuesta más directa, porque actúa antes del apply, sin depender de la atención humana en el momento crítico; CloudTrail (M7) responde una pregunta distinta y también necesaria; y el vector de 2026 no es "no confíes en agentes" — es que la velocidad de un agente comprime la ventana donde un control dependiente de atención humana tiene oportunidad real de funcionar.

Antes de avanzar deberías poder: narrar la cadena completa de siete pasos, con las cifras exactas; explicar por qué conftest es la respuesta más directa a este caso específico, y por qué CloudTrail y deletion protection no son redundantes con ella; y explicar la reinterpretación correcta de "el vector de 2026 es el agente con credenciales" sin caer en "no uses agentes".

Las lecciones 7 y 8 cierran el módulo con los dos entregables que este análisis alimenta: el documento formal de modelo de amenazas, y el mapa de riesgos que va a gobernar, módulo por módulo, el resto de esta guía.

Recursos

  1. Alexey Grigorev — How I Dropped Our Production Database — el relato de primera mano del propio afectado; la fuente primaria de las cifras exactas (1.943.200 filas, 2,5 años de historial, ~24 h de recuperación) y de la fecha (26 de febrero de 2026).
  2. Hacker News — hilo original del incidente (#47278720) — la discusión pública que sacó el caso a la luz.
  3. incidentdatabase.ai — Incident 1424 — el registro estructurado e independiente del incidente.
  4. Hacker News — réplica interactiva del incidente (#47323915) — el hilo que documenta, entre otros detalles, que el agente señaló el riesgo antes de ejecutar el comando.
  5. terraform-and-iac-guide, Módulo 8, lección 5 (05-blast-radius-and-the-claude-code-destroy-incident.md) — la narración original completa de este caso, ya verificada, que esta lección no repite sino que reencuadra.
  6. src/paths/aws-cloud-ecosystem/VALIDACION.md — la fuente de la cita "el vector dominante de 2026 ya no es el humano descuidado sino el agente con credenciales", reinterpretada en esta lección.