Files
Aegis/aegiscompleteplan.md

167 KiB
Raw Blame History

🛡️ Aegis — Plan de Tareas Completo (V2 + V3)

Plataforma Avanzada de Gestión y Validación de Cobertura MITRE ATT&CK

Instrucciones de uso: Cada tarea (T-XXX) es una unidad de trabajo independiente que debe resultar en un commit. Están ordenadas secuencialmente — cada tarea puede depender de las anteriores pero nunca de las posteriores. Cada tarea incluye una sección de validación: no hagas commit hasta que todos los checks pasen.

Contexto: Este plan se ejecuta DESPUÉS de completar el MVP de Aegis (T-001 a T-036). Se divide en dos grandes bloques:

  • V2 (T-100 a T-134): Flujo Red Team / Blue Team con validación dual, templates y notificaciones
  • V3 (T-200 a T-237): Plataforma enterprise con múltiples fuentes, threat actors, scoring, compliance, campañas y heatmap avanzado

PARTE 1 — AEGIS V2: Sistema de Tests de Validación Red Team / Blue Team


Visión General del Flujo de Validación

┌─────────────────────────────────────────────────────────────────────────┐
│                        CICLO DE VIDA DE UN TEST                        │
│                                                                         │
│  ┌──────┐    ┌──────────────┐    ┌─────────────────┐    ┌───────────┐  │
│  │ DRAFT│───▶│RED_EXECUTING │───▶│ BLUE_EVALUATING  │───▶│ IN_REVIEW │  │
│  └──────┘    └──────────────┘    └─────────────────┘    └───────────┘  │
│                                                              │          │
│                                         ┌────────────────────┤          │
│                                         ▼                    ▼          │
│                                   ┌──────────┐        ┌──────────┐     │
│                                   │ REJECTED │        │VALIDATED │     │
│                                   └──────────┘        └──────────┘     │
│                                         │                               │
│                                         └──────▶ Vuelve a DRAFT        │
└─────────────────────────────────────────────────────────────────────────┘

Estados del Test:
  - draft:            Creado, pendiente de ejecución por Red Team
  - red_executing:    Red Team documenta ataque y sube evidencias
  - blue_evaluating:  Blue Team documenta detección y sube evidencias
  - in_review:        Ambos managers revisan evidencias
  - validated:        Aprobado por ambos managers
  - rejected:         Rechazado — vuelve a draft para rehacer

Estados editables por equipo:
  - Red Team puede editar en: draft, red_executing
  - Blue Team puede editar en: blue_evaluating
  - Evidencias Red se pueden subir/borrar en: draft, red_executing
  - Evidencias Blue se pueden subir/borrar en: blue_evaluating
  - Managers validan/rechazan en: in_review

Roles involucrados:
  - red_tech:   Crea tests, documenta ataques, sube evidencias de ataque
  - blue_tech:  Documenta detección, sube evidencias de detección
  - red_lead:   Valida/rechaza la parte de Red Team
  - blue_lead:  Valida/rechaza la parte de Blue Team
  - admin:      Acceso total

Catálogo de Tests Básicos por TTP

Los tests básicos se obtienen de varias fuentes:

  1. Atomic Red Team (Red Canary): repositorio open-source con tests atómicos mapeados a MITRE ATT&CK
  2. MITRE ATT&CK procedures: procedimientos documentados en la propia base de datos de MITRE
  3. Tests personalizados: creados manualmente por los equipos según su entorno

FASE 10 — Evolución del Modelo de Datos para Red/Blue Team

T-100: Ampliar estados del Test (TestState)

Objetivo: Añadir los nuevos estados al ciclo de vida del test que permitan diferenciar las fases de Red Team ejecutando, Blue Team evaluando, y revisión por managers.

Archivos a modificar:

  • backend/app/models/enums.py

Cambios en TestState:

class TestState(str, enum.Enum):
    draft = "draft"
    red_executing = "red_executing"      # NUEVO: Red Team documentando ataque
    blue_evaluating = "blue_evaluating"  # NUEVO: Blue Team evaluando detección
    in_review = "in_review"
    validated = "validated"
    rejected = "rejected"

Generar migración Alembic para actualizar el enum en PostgreSQL.

⚠️ IMPORTANTE — Migración de enums en PostgreSQL: PostgreSQL no permite modificar enums con un simple ALTER TABLE. Alembic no maneja esto bien automáticamente. La migración generada por --autogenerate probablemente NO funcione. Hay que escribir la migración manualmente usando:

from alembic import op

def upgrade():
    # PostgreSQL requiere ALTER TYPE para añadir valores a un enum existente
    op.execute("ALTER TYPE teststate ADD VALUE IF NOT EXISTS 'red_executing' AFTER 'draft'")
    op.execute("ALTER TYPE teststate ADD VALUE IF NOT EXISTS 'blue_evaluating' AFTER 'red_executing'")

def downgrade():
    # Downgrade de enums en PostgreSQL es complejo — requiere recrear el tipo
    # Solo implementar si es estrictamente necesario
    pass

No usar --autogenerate para esta migración. Crearla manualmente con alembic revision -m "add_new_test_states".

Validación:

  • alembic upgrade head aplica la migración sin errores
  • Los tests existentes con estados antiguos siguen funcionando
  • Se pueden crear tests con los nuevos estados vía SQL directo: INSERT INTO tests (..., state) VALUES (..., 'red_executing')
  • SELECT enum_range(NULL::teststate) en psql muestra todos los valores incluyendo los nuevos

T-101: Modelo EvidenceTeam — separar evidencias Red/Blue

Objetivo: Añadir un campo team a las evidencias para distinguir si pertenecen al Red Team (evidencia de ataque) o al Blue Team (evidencia de detección).

Archivos a modificar:

  • backend/app/models/enums.py — añadir enum TeamSide
  • backend/app/models/evidence.py — añadir campos team y notes

Nuevo enum:

class TeamSide(str, enum.Enum):
    red = "red"
    blue = "blue"

Nuevos campos en Evidence:

Campo Tipo Restricciones
team Enum(TeamSide) not null, default "red"
notes Text nullable (notas sobre la evidencia)

⚠️ Migración: Usar la misma técnica de T-100 para crear el enum teamside manualmente en PostgreSQL, luego añadir la columna. El default red asegura que las evidencias existentes se asignen correctamente.

Generar migración.

Validación:

  • alembic upgrade head añade la columna team y notes a la tabla evidences
  • Las evidencias existentes tienen team = 'red' por defecto
  • Se puede insertar una evidencia con team = 'blue'
  • La columna notes acepta texto largo

T-102: Campos de validación dual en Test (red_lead + blue_lead)

Objetivo: Extender el modelo Test para soportar validación independiente por Red Lead y Blue Lead, de manera que un test solo pase a validated cuando ambos managers lo aprueban.

Archivos a modificar:

  • backend/app/models/test.py

Deprecar campos existentes del MVP:

Los campos validated_by y validated_at del MVP quedan obsoletos con la validación dual. La migración debe:

  1. Renombrar validated_bylegacy_validated_by y validated_atlegacy_validated_at (preservar datos)
  2. O bien eliminarlos directamente si no hay datos de producción relevantes

Si se decide eliminar, añadir en la migración:

op.drop_column('tests', 'validated_by')
op.drop_column('tests', 'validated_at')

Nuevos campos:

Campo Tipo Restricciones
red_validated_by UUID FK → users.id, nullable
red_validated_at DateTime nullable
red_validation_status String nullable (pending/approved/rejected)
red_validation_notes Text nullable
blue_validated_by UUID FK → users.id, nullable
blue_validated_at DateTime nullable
blue_validation_status String nullable (pending/approved/rejected)
blue_validation_notes Text nullable
red_summary Text nullable (resumen del ataque por red)
blue_summary Text nullable (resumen de detección por blue)
detection_result Enum(TestResult) nullable (resultado de detección blue)
attack_success Boolean nullable (si el ataque tuvo éxito)

Relaciones nuevas:

red_validator = relationship("User", foreign_keys=[red_validated_by])
blue_validator = relationship("User", foreign_keys=[blue_validated_by])

Generar migración.

Validación:

  • alembic upgrade head crea las nuevas columnas y elimina/renombra las antiguas sin errores
  • Los tests existentes tienen los nuevos campos como null
  • Se puede actualizar red_validation_status y blue_validation_status independientemente
  • Las FKs a users.id funcionan correctamente
  • No quedan referencias en el código a los campos validated_by/validated_at antiguos

T-103: Modelo TestTemplate — catálogo de tests predefinidos

Objetivo: Crear un modelo para almacenar plantillas de tests predefinidos (basados en Atomic Red Team, MITRE procedures, etc.) que los usuarios pueden instanciar como tests reales.

Archivo a crear: backend/app/models/test_template.py

Campos:

Campo Tipo Restricciones
id UUID PK, default uuid4
mitre_technique_id String not null (ej: "T1059.001")
name String not null
description Text nullable
source String not null (ej: "atomic_red_team", "mitre", "custom")
source_url String nullable (URL al test original)
attack_procedure Text nullable (procedimiento de ataque sugerido)
expected_detection Text nullable (qué debería detectar blue team)
platform String nullable (windows, linux, macos)
tool_suggested String nullable (herramienta sugerida)
severity String nullable (low, medium, high, critical)
atomic_test_id String nullable (ID del test en Atomic Red Team)
is_active Boolean default True
created_at DateTime default utcnow

Índices a crear:

Index('ix_test_templates_mitre_technique_id', TestTemplate.mitre_technique_id)
Index('ix_test_templates_source', TestTemplate.source)
Index('ix_test_templates_platform', TestTemplate.platform)
Index('ix_test_templates_severity', TestTemplate.severity)

Actualizar models/__init__.py para importar TestTemplate.

Generar migración.

Validación:

  • alembic upgrade head crea la tabla test_templates con los índices
  • Se puede insertar un template con todos los campos
  • El campo source acepta los valores esperados
  • La tabla soporta múltiples templates para la misma técnica MITRE
  • EXPLAIN de una query filtrando por mitre_technique_id muestra uso del índice

T-104: Schemas Pydantic para los nuevos modelos

Objetivo: Crear schemas de request/response para los modelos modificados y nuevos.

Archivos a crear/modificar:

  • backend/app/schemas/test.py — actualizar con nuevos campos
  • backend/app/schemas/evidence.py — añadir team y notes
  • backend/app/schemas/test_template.py — nuevo

Schemas de Test actualizados:

  • TestOut: añadir campos de validación dual (red_validated_by, blue_validated_by, red_validation_status, blue_validation_status, red_summary, blue_summary, etc.). Eliminar referencias a los campos validated_by/validated_at antiguos.
  • TestRedUpdate: name, description, procedure_text, tool_used, attack_success, red_summary (campos que rellena Red Team)
  • TestBlueUpdate: detection_result, blue_summary (campos que rellena Blue Team)
  • TestRedValidate: red_validation_status (approved/rejected), red_validation_notes
  • TestBlueValidate: blue_validation_status (approved/rejected), blue_validation_notes

Schemas de Evidence actualizados:

  • EvidenceOut: añadir team y notes
  • EvidenceUpload: añadir team (requerido) y notes (opcional)

Schemas de TestTemplate:

  • TestTemplateOut: todos los campos
  • TestTemplateCreate: para crear templates personalizados
  • TestTemplateSummary: id, mitre_technique_id, name, source, platform, severity (para listados)
  • TestTemplateInstantiate: template_id, technique_id (para crear un test real desde un template)

Validación:

  • Todos los schemas se importan sin errores
  • TestOut incluye los campos de validación dual y NO incluye los antiguos
  • TestTemplateCreate valida correctamente los campos requeridos
  • EvidenceOut incluye team y notes

T-105: Índices de base de datos para V2

Objetivo: Crear índices para los campos que se usarán frecuentemente en filtros y consultas, evitando queries lentas a medida que crece el volumen de datos.

Archivo a crear: migración Alembic manual

Índices a crear:

# Tests
Index('ix_tests_state', Test.state)
Index('ix_tests_technique_id', Test.technique_id)
Index('ix_tests_created_by', Test.created_by)
Index('ix_tests_red_validation_status', Test.red_validation_status)
Index('ix_tests_blue_validation_status', Test.blue_validation_status)

# Evidences
Index('ix_evidences_test_id', Evidence.test_id)
Index('ix_evidences_team', Evidence.team)

# Techniques (si no existen ya del MVP)
Index('ix_techniques_tactic', Technique.tactic)
Index('ix_techniques_status_global', Technique.status_global)
Index('ix_techniques_review_required', Technique.review_required)

Validación:

  • alembic upgrade head crea todos los índices
  • \di en psql lista los nuevos índices
  • EXPLAIN ANALYZE de una query filtrando tests por state muestra Index Scan

FASE 11 — Lógica de Negocio del Flujo Red/Blue

T-106: Servicio de transiciones de estado del Test

Objetivo: Crear un servicio que controle las transiciones de estado válidas del test y garantice que solo se puedan hacer los cambios permitidos.

Archivo a crear: backend/app/services/test_workflow_service.py

Transiciones válidas:

VALID_TRANSITIONS = {
    TestState.draft:           [TestState.red_executing],
    TestState.red_executing:   [TestState.blue_evaluating],
    TestState.blue_evaluating: [TestState.in_review],
    TestState.in_review:       [TestState.validated, TestState.rejected],
    TestState.rejected:        [TestState.draft],
    TestState.validated:       [],  # estado final (o puede reabrirse)
}

Funciones a implementar:

  • can_transition(test: Test, target_state: TestState) -> bool
  • transition_state(db, test, target_state, user) -> Test — valida transición, cambia estado, log de auditoría
  • start_execution(db, test, user) -> Test — mueve de draft a red_executing
  • submit_red_evidence(db, test, user) -> Test — marca como blue_evaluating cuando Red Team termina
  • submit_blue_evidence(db, test, user) -> Test — marca como in_review cuando Blue Team termina
  • validate_as_red_lead(db, test, user, status, notes) -> Test — valida parte Red
  • validate_as_blue_lead(db, test, user, status, notes) -> Test — valida parte Blue
  • check_dual_validation(db, test) -> Test — si ambos aprobaron, pasa a validated; si alguno rechazó, pasa a rejected
  • reopen_test(db, test, user) -> Test — mueve de rejected a draft, limpia campos de validación

Validación:

  • Transición draft → red_executing funciona
  • Transición draft → validated falla (no permitida)
  • Transición red_executing → blue_evaluating funciona
  • check_dual_validation pasa a validated solo si ambos managers aprobaron
  • check_dual_validation pasa a rejected si algún manager rechazó
  • reopen_test limpia red_validation_status, blue_validation_status y campos asociados
  • Cada transición genera un log de auditoría

T-107: Actualizar servicio de recalculación de status

Objetivo: Mejorar status_service.py para tener en cuenta los nuevos estados y la validación dual.

Archivo a modificar: backend/app/services/status_service.py

Nueva lógica:

def recalculate_technique_status(db, technique):
    tests = technique.tests
    if not tests:
        technique.status_global = TechniqueStatus.not_evaluated
    elif all(t.state == TestState.validated for t in tests):
        # Todos validados — revisar resultados de detección
        results = [t.detection_result for t in tests if t.detection_result]
        if all(r == "detected" for r in results):
            technique.status_global = TechniqueStatus.validated
        elif any(r == "partially_detected" for r in results):
            technique.status_global = TechniqueStatus.partial
        else:
            technique.status_global = TechniqueStatus.not_covered
    elif any(t.state == TestState.validated for t in tests):
        technique.status_global = TechniqueStatus.partial
    else:
        technique.status_global = TechniqueStatus.in_progress
    db.commit()

Validación:

  • Sin tests → not_evaluated
  • Todos validated con detection=detected → validated
  • Algunos validated, otros en progreso → partial
  • Todos en estados intermedios → in_progress
  • Todos validated con detection=not_detected → not_covered

T-108: Servicio de importación de Atomic Red Team

Objetivo: Crear un servicio que importe tests predefinidos desde el repositorio de Atomic Red Team de Red Canary y los almacene como TestTemplates.

Archivo a crear: backend/app/services/atomic_import_service.py

⚠️ Estrategia de descarga desde GitHub: La API de GitHub sin autenticación solo permite 60 requests/hora. El repositorio de Atomic Red Team tiene 1500+ archivos YAML, por lo que NO se puede hacer un request por archivo. La estrategia correcta es:

  1. Opción preferida: Descargar el ZIP del repositorio completo via https://github.com/redcanaryco/atomic-red-team/archive/refs/heads/master.zip
  2. Descomprimir en memoria o en directorio temporal
  3. Parsear todos los ficheros YAML de atomics/T*/T*.yaml
  4. Limpiar el directorio temporal al finalizar

Alternativamente, si se quiere usar la API de GitHub, configurar un token de acceso personal en settings (GITHUB_TOKEN) para tener 5000 requests/hora.

Lógica:

  1. Descargar ZIP del repositorio de Atomic Red Team
  2. Descomprimir y localizar ficheros YAML en atomics/
  3. Para cada archivo YAML (organizados por técnica MITRE atomics/T1059.001/T1059.001.yaml):
    • Parsear el YAML que contiene una lista de atomic_tests
    • Cada test atómico tiene: name, description, supported_platforms, executor (tipo y command)
  4. Por cada test atómico:
    • Crear un TestTemplate con source = "atomic_red_team"
    • Setear atomic_test_id con el ID del test (formato {technique_id}-{index})
    • Setear platform desde supported_platforms
    • Setear attack_procedure desde executor.command
    • Mapear a la técnica MITRE correspondiente
  5. No duplicar templates que ya existen (comparar por atomic_test_id)
  6. Log de auditoría con resumen

Validación:

  • Ejecutar la importación crea TestTemplates en la BD
  • Cada template tiene source = "atomic_red_team" y datos válidos
  • Ejecutar dos veces no duplica templates
  • Los templates se mapean correctamente a técnicas MITRE existentes
  • Se importan al menos 500+ templates
  • La descarga del ZIP funciona sin alcanzar rate limits

FASE 12 — Endpoints API Red/Blue

T-109: Endpoints actualizados de Tests con flujo Red/Blue

Objetivo: Modificar y añadir endpoints al router de tests para soportar el nuevo flujo de trabajo.

Archivo a modificar: backend/app/routers/tests.py

Endpoints nuevos/modificados:

Método Ruta Auth Descripción
GET /tests autenticado Listar tests con filtros (state, technique_id)
POST /tests red_tech, admin Crear test (nuevo o desde template)
POST /tests/from-template red_tech, admin Crear test instanciando un template
GET /tests/{id} autenticado Detalle con evidencias separadas red/blue
PATCH /tests/{id}/red red_tech, admin Red Team actualiza su parte
PATCH /tests/{id}/blue blue_tech, admin Blue Team actualiza su parte
POST /tests/{id}/start-execution red_tech, admin Mover de draft → red_executing
POST /tests/{id}/submit-red red_tech, admin Red Team finaliza → pasa a blue_evaluating
POST /tests/{id}/submit-blue blue_tech, admin Blue Team finaliza → pasa a in_review
POST /tests/{id}/validate-red red_lead, admin Red Lead valida/rechaza parte red
POST /tests/{id}/validate-blue blue_lead, admin Blue Lead valida/rechaza parte blue
POST /tests/{id}/reopen red_lead, blue_lead, admin Reabrir test rechazado → draft
GET /tests/{id}/timeline autenticado Timeline de cambios de estado del test

Estados permitidos para edición:

  • PATCH /tests/{id}/red: permitido en draft y red_executing (Red Team prepara y ejecuta)
  • PATCH /tests/{id}/blue: permitido solo en blue_evaluating
  • POST /tests/{id}/start-execution: solo desde draft
  • POST /tests/{id}/submit-red: solo desde red_executing
  • POST /tests/{id}/submit-blue: solo desde blue_evaluating

Detalle del endpoint GET /tests/{id}:

La respuesta debe incluir:

{
  "id": "...",
  "name": "...",
  "state": "blue_evaluating",
  "red_evidences": [],
  "blue_evidences": [],
  "red_summary": "...",
  "blue_summary": "...",
  "attack_success": true,
  "detection_result": "detected",
  "red_validation_status": "approved",
  "blue_validation_status": "pending",
  "timeline": []
}

Validación:

  • POST /tests crea un test en estado draft
  • POST /tests/from-template crea un test con datos pre-rellenados del template
  • POST /tests/{id}/start-execution mueve de draft a red_executing
  • PATCH /tests/{id}/red funciona en draft y red_executing
  • PATCH /tests/{id}/red falla en blue_evaluating (400)
  • PATCH /tests/{id}/blue solo funciona en blue_evaluating
  • POST /tests/{id}/submit-red cambia estado a blue_evaluating
  • POST /tests/{id}/submit-blue cambia estado a in_review
  • POST /tests/{id}/validate-red solo accesible por red_lead/admin
  • POST /tests/{id}/validate-blue solo accesible por blue_lead/admin
  • Cuando ambos validan como approved → test pasa a validated
  • Cuando alguno rechaza → test pasa a rejected
  • POST /tests/{id}/reopen solo funciona en tests rejected
  • GET /tests/{id}/timeline retorna el historial ordenado cronológicamente
  • Cada operación genera audit log

T-110: Endpoints de Evidence con separación Red/Blue

Objetivo: Modificar el router de evidencias para soportar la separación por equipo.

Archivo a modificar: backend/app/routers/evidence.py

Endpoints modificados:

Método Ruta Auth Descripción
POST /tests/{test_id}/evidence autenticado Subir evidencia indicando team (red/blue)
GET /tests/{test_id}/evidence autenticado Listar evidencias del test, filtrable por team
GET /evidence/{id} autenticado Obtener URL pre-firmada
DELETE /evidence/{id} creador o admin Eliminar evidencia (solo en estados editables)

Lógica de control explícita:

  • Red Team (red_tech) solo puede subir evidencias con team=red cuando el test está en draft o red_executing
  • Blue Team (blue_tech) solo puede subir evidencias con team=blue cuando el test está en blue_evaluating
  • Admin puede subir en cualquier momento con cualquier team
  • DELETE de evidencias Red: permitido en draft y red_executing
  • DELETE de evidencias Blue: permitido en blue_evaluating
  • DELETE en estados in_review, validated, rejected: NO permitido (403)

Validación:

  • Un red_tech puede subir evidencia con team=red en estado red_executing
  • Un red_tech puede subir evidencia con team=red en estado draft
  • Un red_tech NO puede subir evidencia con team=blue (403)
  • Un blue_tech puede subir evidencia con team=blue en estado blue_evaluating
  • Un blue_tech NO puede subir evidencia con team=red (403)
  • GET /tests/{id}/evidence?team=red filtra correctamente
  • DELETE /evidence/{id} en estado in_review → 403
  • DELETE /evidence/{id} en estado red_executing para evidencia red → 200
  • Admin puede subir cualquier tipo de evidencia en cualquier momento

T-111: Endpoints CRUD de TestTemplates

Objetivo: Crear endpoints para gestionar el catálogo de templates de tests.

Archivo a crear: backend/app/routers/test_templates.py

Endpoints:

Método Ruta Auth Descripción
GET /test-templates autenticado Listar templates con filtros
GET /test-templates/{id} autenticado Detalle de un template
POST /test-templates admin Crear template personalizado
PATCH /test-templates/{id} admin Actualizar template
DELETE /test-templates/{id} admin Desactivar template (soft delete)
GET /test-templates/by-technique/{mitre_id} autenticado Templates para una técnica MITRE específica

Filtros del GET /test-templates:

  • source: atomic_red_team, mitre, custom
  • platform: windows, linux, macos
  • severity: low, medium, high, critical
  • mitre_technique_id: filtrar por técnica
  • search: búsqueda por nombre/descripción
  • Paginación: offset + limit (default limit=50)

Validación:

  • GET /test-templates retorna lista paginada
  • GET /test-templates?source=atomic_red_team filtra por fuente
  • GET /test-templates?platform=windows filtra por plataforma
  • GET /test-templates/by-technique/T1059.001 retorna templates para esa técnica
  • POST /test-templates solo accesible por admin
  • DELETE /test-templates/{id} hace soft delete (is_active=False)
  • El filtro search busca en name y description

T-112: Endpoint de importación de Atomic Red Team

Objetivo: Exponer la importación de Atomic Red Team como endpoint del sistema.

Archivo a modificar: backend/app/routers/system.py

Endpoint:

POST /api/v1/system/import-atomic-tests
Auth: admin only
Response: {"message": "Import completed", "imported": X, "skipped": Y, "errors": Z}

Validación:

  • POST /system/import-atomic-tests ejecuta la importación y retorna estadísticas
  • Solo admin puede ejecutar
  • Audit log registra la importación
  • Ejecutar dos veces no duplica — incrementa skipped

FASE 13 — Frontend: Tipos y API Clients

T-113: Actualizar tipos TypeScript

Objetivo: Actualizar los tipos del frontend para reflejar los cambios del backend.

Archivo a modificar: frontend/src/types/models.ts

Tipos a añadir/modificar:

// Actualizar TestState
export type TestState =
  | "draft"
  | "red_executing"
  | "blue_evaluating"
  | "in_review"
  | "validated"
  | "rejected";

// Nuevo tipo TeamSide
export type TeamSide = "red" | "blue";

// Estados editables por equipo
export const RED_EDITABLE_STATES: TestState[] = ["draft", "red_executing"];
export const BLUE_EDITABLE_STATES: TestState[] = ["blue_evaluating"];

// Actualizar Evidence
export interface Evidence {
  id: string;
  test_id: string;
  file_name: string;
  file_path: string;
  sha256_hash: string;
  uploaded_by: string | null;
  uploaded_at: string;
  team: TeamSide;
  notes: string | null;
}

// Actualizar Test con campos duales
export interface Test {
  id: string;
  technique_id: string;
  name: string;
  description: string | null;
  platform: string | null;
  procedure_text: string | null;
  tool_used: string | null;
  state: TestState;
  created_by: string | null;
  created_at: string;
  red_summary: string | null;
  blue_summary: string | null;
  attack_success: boolean | null;
  detection_result: TestResult | null;
  red_validation_status: ValidationStatus | null;
  blue_validation_status: ValidationStatus | null;
  red_validation_notes: string | null;
  blue_validation_notes: string | null;
  red_validated_by: string | null;
  blue_validated_by: string | null;
  red_evidences: Evidence[];
  blue_evidences: Evidence[];
}

export type ValidationStatus = "pending" | "approved" | "rejected";

// Nuevo tipo TestTemplate
export interface TestTemplate {
  id: string;
  mitre_technique_id: string;
  name: string;
  description: string | null;
  source: string;
  source_url: string | null;
  attack_procedure: string | null;
  expected_detection: string | null;
  platform: string | null;
  tool_suggested: string | null;
  severity: string | null;
  atomic_test_id: string | null;
  is_active: boolean;
  created_at: string;
}

// Timeline
export interface TestTimelineEntry {
  id: string;
  action: string;
  user: string;
  timestamp: string;
  details: Record<string, unknown>;
}

Validación:

  • TypeScript compila sin errores
  • Todos los tipos nuevos están exportados
  • Los tipos coinciden con los schemas del backend
  • No hay referencias a los tipos antiguos validated_by/validated_at

T-114: Nuevos API clients

Objetivo: Crear/actualizar los clientes API del frontend para los nuevos endpoints.

Archivos a crear/modificar:

  • frontend/src/api/tests.ts — actualizar con nuevos endpoints
  • frontend/src/api/evidence.ts — actualizar con parámetro team
  • frontend/src/api/test-templates.ts — nuevo

Funciones nuevas en tests.ts:

export const createTestFromTemplate = (templateId: string, techniqueId: string) => ...
export const updateTestRed = (testId: string, data: RedUpdateData) => ...
export const updateTestBlue = (testId: string, data: BlueUpdateData) => ...
export const startExecution = (testId: string) => ...
export const submitRedEvidence = (testId: string) => ...
export const submitBlueEvidence = (testId: string) => ...
export const validateAsRedLead = (testId: string, data: RedValidation) => ...
export const validateAsBlueLead = (testId: string, data: BlueValidation) => ...
export const reopenTest = (testId: string) => ...
export const getTestTimeline = (testId: string) => ...

Funciones nuevas en evidence.ts:

export const uploadEvidence = (testId: string, file: File, team: TeamSide, notes?: string) => ...
export const getTestEvidences = (testId: string, team?: TeamSide) => ...
export const deleteEvidence = (evidenceId: string) => ...

Funciones en test-templates.ts:

export const getTemplates = (filters?: TemplateFilters) => ...
export const getTemplateById = (id: string) => ...
export const getTemplatesByTechnique = (mitreId: string) => ...
export const createTemplate = (data: CreateTemplate) => ...
export const importAtomicTests = () => ...

Validación:

  • Todos los imports funcionan sin errores TypeScript
  • Cada función envía la petición al endpoint correcto
  • uploadEvidence incluye el campo team en FormData
  • getTestEvidences envía el query param team correctamente

FASE 14 — Frontend: Página de Test Rediseñada con Pestañas Red/Blue

T-115: Componente TestDetailHeader

Objetivo: Crear el header del detalle del test con información del estado, progreso y acciones contextuales.

Archivo a crear: frontend/src/components/test-detail/TestDetailHeader.tsx

Contenido:

  • Nombre del test y badge de estado con color
  • Barra de progreso visual (5 pasos: draft → red → blue → review → validated)
  • Nombre de la técnica asociada (link)
  • Botones de acción contextuales según rol y estado:
    • Red Tech en draft: botón "Start Execution"
    • Red Tech en red_executing: botón "Submit to Blue Team"
    • Blue Tech en blue_evaluating: botón "Submit for Review"
    • Red Lead en in_review: botón "Approve/Reject Red"
    • Blue Lead en in_review: botón "Approve/Reject Blue"
  • Indicadores de validación dual (checkmarks para red_lead y blue_lead)

Validación:

  • El header muestra toda la información correcta
  • La barra de progreso refleja el estado actual
  • Los botones aparecen solo cuando el rol y estado lo permiten
  • El botón "Start Execution" aparece solo en draft para red_tech
  • Los indicadores de validación dual se actualizan correctamente

T-116: Componente de pestañas Red Team / Blue Team

Objetivo: Crear el sistema de pestañas que separa las evidencias y el contenido entre Red Team y Blue Team.

Archivo a crear: frontend/src/components/test-detail/TeamTabs.tsx

Estructura de pestañas:

┌──────────────────────────────────────────────────────────────┐
│  [🔴 Red Team]  [🔵 Blue Team]  [📋 Summary]  [📜 Timeline] │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  Contenido de la pestaña seleccionada                       │
│                                                              │
└──────────────────────────────────────────────────────────────┘

Pestaña Red Team:

  • Procedimiento de ataque (editable en draft y red_executing)
  • Herramienta utilizada (editable en draft y red_executing)
  • Indicador de éxito del ataque (switch: sí/no)
  • Resumen del Red Team (textarea)
  • Lista de evidencias Red con upload (solo en draft y red_executing para red_tech)
  • Estado de validación del Red Lead (si aplica)

Pestaña Blue Team:

  • Resultado de detección (detected/not_detected/partially_detected)
  • Resumen del Blue Team (textarea)
  • Lista de evidencias Blue con upload (solo en blue_evaluating para blue_tech)
  • Estado de validación del Blue Lead (si aplica)

Pestaña Summary:

  • Vista resumen con ambos lados lado a lado
  • Comparativa visual: ataque vs detección
  • Resultado final

Pestaña Timeline:

  • Historial cronológico de todos los cambios del test
  • Cada entrada con usuario, acción, fecha y detalles

Validación:

  • Las pestañas se renderizan correctamente
  • Cambiar de pestaña muestra el contenido correcto
  • Los campos son editables solo en el estado y rol apropiados
  • Upload de evidencias funciona dentro de cada pestaña
  • La pestaña Summary muestra comparativa correcta

T-117: Página TestDetailPage rediseñada

Objetivo: Integrar los nuevos componentes en la página de detalle del test, reemplazando el diseño actual.

Archivo a modificar: frontend/src/pages/TestDetailPage.tsx

Estructura:

┌─────────────────────────────────────────────────────────┐
│  TestDetailHeader (estado, progreso, acciones)          │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  TeamTabs                                               │
│  ┌─────────────────────────────────────────────────────┐│
│  │  Pestaña seleccionada (Red/Blue/Summary/Timeline)  ││
│  └─────────────────────────────────────────────────────┘│
│                                                         │
├─────────────────────────────────────────────────────────┤
│  Sidebar: Metadata del test                             │
│  - Técnica asociada                                     │
│  - Plataforma                                           │
│  - Creador                                              │
│  - Fechas                                               │
│  - Template origen (si aplica)                          │
└─────────────────────────────────────────────────────────┘

Interacciones:

  • Toda acción usa mutations de react-query con invalidación
  • Modales de confirmación para validar/rechazar
  • Toast notifications para feedback
  • Loading states en todas las operaciones

Validación:

  • La página carga y muestra todos los datos del test
  • Las pestañas Red/Blue/Summary/Timeline funcionan
  • Las acciones de validación dual funcionan correctamente
  • La transición de estado se refleja en tiempo real tras cada acción
  • Los permisos de edición se respetan según rol y estado
  • La subida de evidencias funciona dentro de las pestañas

T-118: Modal de Validación Dual

Objetivo: Crear un modal de validación que permita a los managers aprobar o rechazar su parte del test, con notas obligatorias en caso de rechazo.

Archivo a crear: frontend/src/components/test-detail/ValidationModal.tsx

Contenido:

  • Título: "Validate as Red Lead" / "Validate as Blue Lead"
  • Resumen de evidencias del equipo correspondiente
  • Opciones: Approve / Reject
  • Textarea para notas (obligatorio en rechazo)
  • Indicador visual del estado de la otra validación
  • Botón de confirmar con loading state

Validación:

  • El modal aparece al hacer click en Validate
  • Se puede seleccionar Approve o Reject
  • Reject requiere notas obligatorias — botón deshabilitado sin notas
  • Approve envía la petición y cierra el modal
  • Se muestra el estado de la validación del otro manager
  • Loading state funciona durante la petición

FASE 15 — Frontend: Catálogo de Tests y Creación desde Templates

T-119: Página de catálogo de TestTemplates

Objetivo: Crear una página donde los usuarios puedan explorar el catálogo de tests disponibles, filtrar por técnica, plataforma y fuente, y ver el detalle de cada template.

Archivo a crear: frontend/src/pages/TestCatalogPage.tsx

Componentes necesarios:

  • Barra de búsqueda y filtros (source, platform, severity, technique)
  • Grid/lista de templates con cards
  • Cada card muestra: nombre, técnica MITRE, plataforma, severidad, fuente (badge), botón "Use Template"
  • Paginación

Ruta: /test-catalog — añadir al router y al sidebar.

Validación:

  • La página carga y muestra templates del backend
  • Los filtros funcionan (source, platform, severity, search)
  • Cada card muestra la información correcta
  • El botón "Use Template" navega o abre modal de instanciación
  • Responsive en móvil y desktop

T-120: Modal/Página de instanciación de Template

Objetivo: Permitir crear un test real a partir de un template, pre-rellenando los campos y permitiendo modificaciones.

Archivo a crear: frontend/src/components/TestFromTemplateForm.tsx

Flujo:

  1. El usuario selecciona un template (desde el catálogo o desde la vista de técnica)
  2. Se abre un formulario pre-rellenado con los datos del template
  3. El usuario puede modificar los campos
  4. Al guardar, se crea un test real con state=draft

Campos del formulario:

  • Nombre (pre-rellenado)
  • Descripción (pre-rellenado)
  • Técnica asociada (pre-rellenado si se viene de una técnica)
  • Plataforma (pre-rellenado)
  • Procedimiento de ataque sugerido (pre-rellenado, editable)
  • Herramienta sugerida (pre-rellenado, editable)
  • Detección esperada (pre-rellenado, readonly — referencia para blue team)

Validación:

  • El formulario se pre-rellena con datos del template
  • Se puede modificar cualquier campo editable
  • Submit crea el test y redirige al detalle
  • El test creado tiene referencia al template origen
  • Campos requeridos se validan antes de submit

T-121: Integrar catálogo en vista de Técnica

Objetivo: Desde la página de detalle de una técnica, permitir ver los templates disponibles y crear tests directamente.

Archivo a modificar: frontend/src/pages/TechniqueDetailPage.tsx

Cambios:

  • Añadir sección "Available Test Templates" debajo de los tests existentes
  • Mostrar cards resumidas de templates disponibles para esa técnica
  • Botón "Run This Test" en cada template que abre el formulario de instanciación
  • Si no hay templates, mostrar mensaje y link al catálogo general

Validación:

  • La sección de templates aparece en la página de técnica
  • Se muestran solo los templates para esa técnica MITRE
  • "Run This Test" pre-rellena correctamente el formulario
  • Si no hay templates se muestra mensaje apropiado
  • La creación del test actualiza la lista de tests de la técnica

FASE 16 — Frontend: Vistas de Gestión y Dashboard Mejorado

T-122: Vista de Tests mejorada con filtros por estado y equipo

Objetivo: Mejorar la página de listado de tests con filtros avanzados y vistas específicas por equipo.

Archivo a modificar: frontend/src/pages/TestsPage.tsx

Mejoras:

  • Filtros: por estado (todos los nuevos estados), por equipo asignado, por técnica, por plataforma
  • Vista de "Mis tareas pendientes" según rol:
    • Red Tech: tests en draft o red_executing creados por mí
    • Blue Tech: tests en blue_evaluating
    • Red Lead: tests en in_review pendientes de validación red
    • Blue Lead: tests en in_review pendientes de validación blue
  • Estadísticas rápidas: contadores por estado (cards superiores)
  • Tabla con columnas: nombre, técnica, estado, equipo actual, última actualización, acciones

Validación:

  • Los filtros por estado funcionan con los nuevos estados
  • "Mis tareas pendientes" filtra correctamente según el rol del usuario
  • Los contadores por estado son correctos
  • La tabla muestra toda la información necesaria
  • Click en un test navega al detalle

T-123: Dashboard mejorado con métricas Red/Blue

Objetivo: Añadir al dashboard métricas específicas del flujo de validación Red/Blue.

Archivos a modificar:

  • backend/app/routers/metrics.py — añadir nuevos endpoints
  • backend/app/schemas/metrics.py — añadir nuevos schemas
  • frontend/src/pages/DashboardPage.tsx — añadir nuevas secciones

Nuevos endpoints de métricas:

GET /metrics/test-pipeline    → contadores por estado del pipeline
GET /metrics/team-activity    → actividad por equipo (tests completados, pendientes)
GET /metrics/validation-rate  → tasa de aprobación/rechazo por manager

Nuevas secciones del dashboard:

  1. Pipeline de Tests: gráfico de funnel mostrando cuántos tests hay en cada estado
  2. Actividad por equipo: Red Team vs Blue Team — tests completados, tiempo medio
  3. Tasa de validación: porcentaje de aprobación por Red Lead y Blue Lead
  4. Tests recientes: tabla con los últimos 10 tests actualizados

Validación:

  • Los nuevos endpoints retornan datos correctos
  • El dashboard muestra las nuevas secciones
  • El pipeline de tests refleja los estados reales
  • Las métricas de equipo se calculan correctamente
  • La sección de tests recientes se actualiza

T-124: Panel de administración de Templates

Objetivo: Añadir al panel de sistema la gestión de templates: importar Atomic Red Team, crear templates personalizados, ver estadísticas del catálogo.

Archivo a modificar: frontend/src/pages/SystemPage.tsx

Nuevas secciones:

  1. Importar Atomic Red Team: botón para ejecutar importación, con progreso y resultado
  2. Estadísticas del catálogo: total templates, por fuente, por plataforma
  3. Crear template personalizado: formulario inline o modal
  4. Gestionar templates: tabla con opción de activar/desactivar

Validación:

  • Botón de importación ejecuta y muestra resultados
  • Las estadísticas del catálogo se muestran correctamente
  • Se puede crear un template personalizado
  • Se puede desactivar un template
  • Solo admin puede acceder a estas funciones

FASE 17 — Backend Tests Automatizados

T-125: Tests del flujo de trabajo Red/Blue

Objetivo: Crear tests automatizados que verifiquen todo el ciclo de vida de un test de seguridad.

Archivo a crear: backend/tests/test_workflow.py

Tests a implementar:

class TestWorkflow:
    def test_full_happy_path():
        """draft → red_executing → blue_evaluating → in_review → validated"""

    def test_rejection_and_reopen():
        """in_review → rejected → draft → red_executing → ..."""

    def test_invalid_transitions():
        """Verificar que transiciones no válidas fallan"""

    def test_red_tech_cannot_access_blue_phase():
        """Red tech no puede editar en blue_evaluating"""

    def test_blue_tech_cannot_access_red_phase():
        """Blue tech no puede editar en red_executing"""

    def test_dual_validation_both_approve():
        """Ambos managers aprueban → validated"""

    def test_dual_validation_one_rejects():
        """Un manager rechaza → rejected"""

    def test_evidence_team_separation():
        """Evidencias red y blue se separan correctamente"""

    def test_red_edit_allowed_in_draft_and_red_executing():
        """PATCH /tests/{id}/red funciona en draft y red_executing"""

    def test_reopen_clears_validation_fields():
        """Reopen limpia red/blue_validation_status y campos asociados"""

Validación:

  • pytest tests/test_workflow.py ejecuta todos los tests
  • Todos los tests pasan (verde)
  • Cobertura del flujo completo

T-126: Tests de TestTemplates

Objetivo: Tests automatizados para el CRUD de templates y la instanciación.

Archivo a crear: backend/tests/test_templates.py

Tests:

class TestTemplates:
    def test_create_template():
        """Admin puede crear un template"""

    def test_list_templates_with_filters():
        """Filtros de source, platform, severity funcionan"""

    def test_get_templates_by_technique():
        """Filtrar templates por técnica MITRE"""

    def test_instantiate_template():
        """Crear test desde template pre-rellena campos"""

    def test_soft_delete_template():
        """Desactivar template no lo borra físicamente"""

    def test_non_admin_cannot_create_template():
        """Solo admin puede crear templates"""

Validación:

  • pytest tests/test_templates.py pasa todos los tests
  • Cobertura de CRUD y filtros
  • Cobertura de permisos

T-127: Tests de métricas actualizadas

Objetivo: Tests automatizados para los nuevos endpoints de métricas.

Archivo a crear: backend/tests/test_metrics_v2.py

Tests:

class TestMetricsV2:
    def test_pipeline_metrics():
        """Contadores por estado del pipeline correctos"""

    def test_team_activity_metrics():
        """Actividad por equipo calculada correctamente"""

    def test_technique_status_recalculation_with_new_states():
        """Recalculación funciona con los nuevos estados"""

    def test_coverage_with_dual_validation():
        """Cobertura correcta tras validación dual"""

Validación:

  • pytest tests/test_metrics_v2.py pasa todos los tests
  • Las métricas coinciden con los datos de prueba

FASE 18 — Notificaciones y Sidebar de Actividad

T-128: Modelo de notificaciones

Objetivo: Crear un sistema básico de notificaciones in-app para alertar a los usuarios cuando necesitan actuar.

Archivo a crear: backend/app/models/notification.py

Campos:

Campo Tipo Restricciones
id UUID PK, default uuid4
user_id UUID FK → users.id, not null
type String not null (test_assigned, validation_needed, test_rejected, etc.)
title String not null
message Text nullable
entity_type String nullable (test, technique)
entity_id UUID nullable
read Boolean default False
created_at DateTime default utcnow

Índices:

Index('ix_notifications_user_id', Notification.user_id)
Index('ix_notifications_read', Notification.read)
Index('ix_notifications_created_at', Notification.created_at)

Generar migración.

Servicio backend/app/services/notification_service.py:

def create_notification(db, user_id, type, title, message, entity_type, entity_id)
def mark_as_read(db, notification_id, user_id)
def mark_all_as_read(db, user_id)
def get_unread_count(db, user_id) -> int
def cleanup_old_notifications(db, days=90) -> int  # Elimina notificaciones leídas > 90 días

Disparar notificaciones automáticamente:

  • Cuando un test pasa a red_executing → notificar al creador (confirmación)
  • Cuando un test pasa a blue_evaluating → notificar a todos los blue_tech
  • Cuando un test pasa a in_review → notificar a red_lead y blue_lead
  • Cuando un test es rechazado → notificar al creador
  • Cuando un test es validado → notificar al creador

Job de limpieza: Programar un job APScheduler diario que ejecute cleanup_old_notifications(db, days=90).

Validación:

  • Se crea una notificación cuando un test cambia a blue_evaluating
  • Se crea una notificación para managers cuando un test llega a in_review
  • Se crea una notificación al creador cuando un test es rechazado
  • Se crea una notificación al creador cuando un test es validado
  • get_unread_count retorna el número correcto
  • cleanup_old_notifications elimina notificaciones antiguas leídas

T-129: Endpoints y frontend de notificaciones

Objetivo: Endpoints API y UI de notificaciones.

Archivos a crear:

  • backend/app/routers/notifications.py
  • frontend/src/api/notifications.ts
  • frontend/src/components/NotificationBell.tsx
  • frontend/src/components/NotificationDropdown.tsx

Endpoints:

Método Ruta Auth Descripción
GET /notifications autenticado Listar notificaciones del user (paginado, limit=20, offset)
GET /notifications/unread-count autenticado Contador de no leídas
PATCH /notifications/{id}/read autenticado Marcar como leída
POST /notifications/read-all autenticado Marcar todas como leídas

Paginación obligatoria en GET /notifications: limit (default 20, max 100) y offset.

Frontend:

  • NotificationBell: icono de campana en el header con badge de conteo
  • NotificationDropdown: dropdown con lista de notificaciones (últimas 20)
  • Click en notificación navega a la entidad correspondiente y marca como leída
  • Polling cada 30 segundos para actualizar conteo (usar react-query con refetchInterval: 30000)
  • Botón "Mark all as read" en el dropdown

Validación:

  • La campana muestra el conteo correcto de no leídas
  • El dropdown lista las notificaciones ordenadas por fecha
  • Click en una notificación navega correctamente y marca como leída
  • "Mark all as read" limpia el conteo
  • Las notificaciones se generan automáticamente con los cambios de estado
  • El polling actualiza el conteo sin refrescar la página

FASE 19 — Mejoras de Remediación y Reportes

T-130: Campo de remediación en tests y templates

Objetivo: Añadir campos de remediación y recomendaciones, inspirados en el enfoque de Validato de "step-by-step remediation".

Archivos a modificar:

  • backend/app/models/test.py — nuevos campos
  • backend/app/models/test_template.py — nuevo campo
  • Schemas correspondientes

Nuevos campos en Test:

Campo Tipo Restricciones
remediation_steps Text nullable
remediation_status String nullable (pending, in_progress, completed, not_applicable)
remediation_assignee UUID FK → users.id, nullable

Nuevo campo en TestTemplate:

Campo Tipo Restricciones
suggested_remediation Text nullable

Generar migración.

Validación:

  • Los nuevos campos se crean en la BD
  • Se pueden asignar pasos de remediación a un test
  • Se puede asignar un responsable de remediación
  • El template puede sugerir remediación al instanciar

T-131: Endpoint y UI de reportes

Objetivo: Crear un sistema básico de reportes que permita exportar el estado de cobertura en diferentes formatos.

Archivos a crear:

  • backend/app/routers/reports.py
  • frontend/src/pages/ReportsPage.tsx

Endpoints:

Método Ruta Auth Descripción
GET /reports/coverage-summary autenticado Reporte JSON completo
GET /reports/coverage-csv autenticado Export CSV de cobertura
GET /reports/test-results autenticado Reporte de resultados de tests
GET /reports/remediation-status autenticado Reporte de estado de remediación

Página de reportes:

  • Selector de tipo de reporte
  • Filtros (rango de fechas, tácticas, plataformas)
  • Preview del reporte
  • Botones de descarga (CSV, JSON)
  • Resumen visual con métricas clave

Ruta: /reports — añadir al router y sidebar.

Validación:

  • Cada endpoint retorna datos correctos
  • El CSV se descarga y abre correctamente en Excel
  • Los filtros funcionan en el frontend
  • La preview del reporte se muestra correctamente
  • Solo usuarios autenticados pueden acceder

FASE 20 — Pulido Final V2 y Documentación

T-132: Actualizar navegación y routing

Objetivo: Integrar todas las nuevas páginas en la navegación de la aplicación.

Archivos a modificar:

  • frontend/src/App.tsx — nuevas rutas
  • frontend/src/components/Sidebar.tsx — nuevos items

Nuevas rutas:

/test-catalog     → TestCatalogPage
/tests/:testId    → TestDetailPage (rediseñada)
/reports          → ReportsPage

Items del sidebar:

  • Dashboard
  • ATT&CK Matrix (Techniques)
  • Tests (con submenu)
    • All Tests
    • My Pending Tasks
    • Test Catalog
  • Reports
  • System (admin)
    • MITRE Sync
    • Intel Scan
    • Templates Management
    • Users
    • Audit Log

Validación:

  • Todas las rutas nuevas funcionan
  • El sidebar muestra los items correctos según el rol
  • La navegación entre páginas es fluida
  • No hay rutas rotas o 404

T-133: Error handling y edge cases

Objetivo: Asegurar que todos los nuevos flujos manejan errores correctamente.

Backend:

  • Todos los endpoints nuevos tienen manejo de 404, 400, 403
  • Las transiciones de estado inválidas retornan errores descriptivos con el formato:
  {"detail": "Cannot transition from 'draft' to 'validated'. Valid transitions: ['red_executing']", "code": "INVALID_TRANSITION"}
  • Los permisos de equipo se validan en cada endpoint

Frontend:

  • Loading states en todas las operaciones nuevas
  • Error messages descriptivos en validaciones y transiciones
  • Confirmación antes de acciones destructivas (rechazar, reabrir)
  • Feedback visual tras cada acción exitosa (toast)

Validación:

  • Intentar transición inválida muestra error descriptivo con las transiciones válidas
  • Permisos incorrectos muestran 403 con mensaje claro
  • Loading states aparecen en todas las operaciones
  • Toast de éxito tras cada acción exitosa
  • Modal de confirmación antes de rechazar un test

T-134: Backend tests finales de integración V2

Objetivo: Suite final de tests que verifica el sistema completo end-to-end.

Archivo a crear: backend/tests/test_integration_v2.py

Tests:

class TestIntegrationV2:
    def test_full_e2e_flow():
        """
        1. Admin importa Atomic Red Team templates
        2. Red Tech crea test desde template
        3. Red Tech inicia ejecución (start-execution)
        4. Red Tech sube evidencias y submite
        5. Blue Tech evalúa y sube evidencias
        6. Blue Tech submite para review
        7. Red Lead y Blue Lead validan
        8. Verificar que la técnica cambia de estado
        """

    def test_rejection_recovery_flow():
        """Flujo completo con rechazo y recuperación"""

    def test_notification_flow():
        """Verificar que las notificaciones se generan correctamente"""

    def test_metrics_accuracy():
        """Verificar que las métricas son correctas tras operaciones"""

    def test_report_generation():
        """Verificar generación de reportes"""

Validación:

  • pytest tests/test_integration_v2.py pasa todos los tests
  • El flujo E2E completo funciona sin errores
  • Las métricas son consistentes tras todas las operaciones

T-135: Actualizar documentación V2

Objetivo: Actualizar README y documentación API para reflejar todos los cambios.

Archivos a modificar:

  • README.md — actualizar con nuevas funcionalidades
  • docs/API.md — documentar nuevos endpoints

Secciones nuevas en README:

  • Descripción del flujo Red Team / Blue Team
  • Descripción de los roles y permisos
  • Diagrama del ciclo de vida del test
  • Cómo importar tests de Atomic Red Team
  • Cómo usar el catálogo de templates
  • Explicación del flujo de validación dual

Documentación API:

  • Nuevos endpoints de tests (flujo Red/Blue)
  • Endpoints de templates
  • Endpoints de notificaciones
  • Endpoints de reportes
  • Nuevos endpoints de métricas

Validación:

  • El README refleja todas las funcionalidades nuevas
  • La documentación API cubre todos los endpoints nuevos
  • Swagger UI en /docs muestra todos los endpoints correctamente
  • Siguiendo el README, un nuevo desarrollador puede entender el flujo completo

Resumen de Fases V2

Fase Tareas Descripción
10 T-100 a T-105 Evolución del modelo de datos para Red/Blue Team
11 T-106 a T-108 Lógica de negocio del flujo Red/Blue
12 T-109 a T-112 Endpoints API Red/Blue
13 T-113 a T-114 Frontend: tipos y API clients
14 T-115 a T-118 Frontend: página de test con pestañas Red/Blue
15 T-119 a T-121 Frontend: catálogo de tests y templates
16 T-122 a T-124 Frontend: vistas de gestión y dashboard mejorado
17 T-125 a T-127 Backend tests automatizados
18 T-128 a T-129 Notificaciones in-app
19 T-130 a T-131 Remediación y reportes
20 T-132 a T-135 Pulido final y documentación

Total V2: 36 tareas = 36 commits mínimo



PARTE 2 — AEGIS V3: Plataforma Avanzada de Validación de Seguridad


Lo que aporta V3 sobre V2

Área V2 ya cubre V3 añade
Fuentes de tests Atomic Red Team +Sigma Rules, +LOLBAS, +GTFOBins, +CALDERA, +Adversary Emulation Library, +Elastic Detection Rules
Defensa Evidencias Blue Team +MITRE D3FEND mapping, +Sigma rule sugerida por test, +detection rule validation
Threat actors Ninguno Perfiles de grupo APT, campañas, priorización por sector/geografía
Métricas Pipeline y cobertura +MTTD, +MTTR, +Detection Efficacy, +tendencias temporales
Visualización Matriz ATT&CK básica +Heatmap estilo Navigator, +capas superpuestas, +export de layers
Compliance Ninguno Mapeo a NIST 800-53, DORA, NIS2, ISO 27001, reportes de compliance
Kill chain Tests individuales +Campañas (cadenas de tests), +attack path visualization
Scoring Estados binarios +Score de cobertura por técnica, +score global, +benchmark
Comparativa Ninguna +Comparar snapshots temporales, +antes/después de remediación
Automatización Manual +Scheduling de campañas, +re-test automático post-remediación

Fuentes de Tests Adicionales

Fuentes para Red Team (Procedimientos de Ataque)

Fuente Descripción Formato Tests aprox. URL
Atomic Red Team Tests atómicos individuales por técnica YAML 1,500+ GitHub
MITRE CALDERA Abilities (acciones) ejecutables por agente YAML 400+ GitHub
Adversary Emulation Library Planes completos de emulación de APTs YAML/JSON/PDF 15+ planes GitHub
LOLBAS Binarios legítimos de Windows abusables YAML/JSON 400+ GitHub
GTFOBins Binarios legítimos de Unix/Linux abusables Markdown/JSON 350+ GitHub
MITRE ATT&CK Procedures Procedimientos documentados en la propia framework STIX/JSON 1,000+ TAXII Server

Fuentes para Blue Team (Reglas de Detección)

Fuente Descripción Formato Reglas aprox. URL
SigmaHQ Reglas de detección genéricas para SIEM YAML 3,000+ GitHub
Elastic Detection Rules Reglas de detección de Elastic SIEM TOML 1,000+ GitHub
MITRE D3FEND Framework de contramedidas defensivas OWL/JSON 200+ técnicas d3fend.mitre.org
Splunk Security Content Reglas de detección para Splunk YAML 1,500+ GitHub

Fuentes de Threat Intelligence

Fuente Descripción Formato URL
MITRE CTI Datos de ATT&CK en STIX 2.0 (grupos, software, campañas) STIX/JSON GitHub
MITRE ATT&CK Groups Perfiles de 140+ grupos APT con sus TTPs STIX attack.mitre.org/groups

FASE 21 — Seed de Datos de Demo y Modelo de Fuentes

T-200: Seed de datos de demo para V3

Objetivo: Crear un script de seed que genere un volumen realista de datos para poder validar las funcionalidades de V3 (heatmaps, scoring, compliance). Sin datos suficientes, muchas tareas de V3 no se pueden validar.

Archivo a crear: backend/app/seed_demo.py

Datos a generar:

  1. 5 usuarios de cada rol (red_tech, blue_tech, red_lead, blue_lead, admin)
  2. 50 técnicas con diferentes estados (validated, partial, not_covered, in_progress, not_evaluated) — distribuidas entre varias tácticas
  3. 100 tests en diferentes estados del pipeline (draft, red_executing, blue_evaluating, in_review, validated, rejected)
  4. 50 evidencias (archivos dummy) asociadas a tests
  5. 20 audit logs variados
  6. 30 notificaciones para diferentes usuarios
  7. 10 templates manuales (además de los de Atomic Red Team)

Ejecutable con: python -m app.seed_demo

⚠️ Requisito: Solo ejecutar sobre una BD con el sync MITRE ya completado (necesita técnicas reales).

Validación:

  • El seed genera todos los datos sin errores
  • Las métricas del dashboard muestran datos variados
  • El heatmap tiene técnicas de diferentes colores
  • Los tests están en diferentes estados del pipeline
  • Ejecutar el seed dos veces no falla (limpia o usa upsert)

T-201: Modelo unificado de fuente de datos (DataSource)

Objetivo: Crear un sistema de gestión de fuentes de datos que permita registrar, configurar y monitorizar las distintas fuentes de tests y reglas de detección.

Archivo a crear: backend/app/models/data_source.py

Campos:

Campo Tipo Restricciones
id UUID PK, default uuid4
name String unique, not null (ej: "atomic_red_team")
display_name String not null (ej: "Atomic Red Team")
type String not null (attack_procedure / detection_rule / threat_intel / defensive_technique)
url String nullable (URL base del repositorio/API)
description Text nullable
is_enabled Boolean default True
last_sync_at DateTime nullable
last_sync_status String nullable (success/error/in_progress)
last_sync_stats JSONB nullable ({"imported": X, "updated": Y, ...})
sync_frequency String nullable (daily/weekly/monthly/manual)
config JSONB nullable (configuración específica de la fuente)
created_at DateTime default utcnow

Generar migración.

Seed de fuentes iniciales: crear un script que registre todas las fuentes conocidas:

  • atomic_red_team (attack_procedure)
  • sigma (detection_rule)
  • lolbas (attack_procedure)
  • gtfobins (attack_procedure)
  • caldera (attack_procedure)
  • elastic_rules (detection_rule)
  • d3fend (defensive_technique)
  • mitre_cti (threat_intel)

Validación:

  • alembic upgrade head crea la tabla data_sources
  • El seed crea las fuentes iniciales con tipos, URLs y configuración correctos
  • Se pueden activar/desactivar fuentes individualmente

T-202: Modelo DetectionRule

Objetivo: Crear el modelo para almacenar reglas de detección de múltiples fuentes (Sigma, Elastic, Splunk, custom).

Archivo a crear: backend/app/models/detection_rule.py

Campos:

Campo Tipo Restricciones
id UUID PK, default uuid4
mitre_technique_id String not null
title String not null
description Text nullable
source String not null (sigma, elastic, splunk, custom)
source_id String nullable (ID en la fuente original)
source_url String nullable
rule_content Text not null (contenido YAML/KQL de la regla)
rule_format String not null (sigma_yaml, kql, spl, custom)
severity String nullable (informational, low, medium, high, critical)
platforms JSONB nullable, default []
log_sources JSONB nullable (ej: {"product": "windows", "service": "sysmon"})
false_positive_rate String nullable (low, medium, high)
is_active Boolean default True
created_at DateTime default utcnow

Índices:

Index('ix_detection_rules_mitre_technique_id', DetectionRule.mitre_technique_id)
Index('ix_detection_rules_source', DetectionRule.source)
Index('ix_detection_rules_severity', DetectionRule.severity)

Generar migración.

Validación:

  • La tabla se crea con los índices correctos
  • Se puede insertar una regla con todos los campos
  • El campo rule_content acepta YAML largo

FASE 22 — Importación de Fuentes de Tests

T-203: Servicio de importación de Sigma Rules

Objetivo: Importar reglas de detección Sigma del repositorio SigmaHQ y almacenarlas como DetectionRules.

Archivo a crear: backend/app/services/sigma_import_service.py

Dependencia a añadir: pySigma en requirements.txt (para parsear YAML de Sigma correctamente, ya que algunos tienen formatos edge-case que un parser YAML genérico no maneja bien).

⚠️ Estrategia de descarga desde GitHub: El repositorio SigmaHQ tiene 3000+ archivos YAML. Usar la misma estrategia que Atomic Red Team:

  1. Descargar ZIP del repositorio completo vía https://github.com/SigmaHQ/sigma/archive/refs/heads/main.zip
  2. Descomprimir en directorio temporal
  3. Parsear todos los ficheros YAML dentro de rules/
  4. Limpiar al finalizar

Lógica:

  1. Descargar y descomprimir el repositorio
  2. Para cada regla .yml en rules/:
    • Parsear YAML usando pySigma (título, descripción, logsource, detection, tags, level)
    • Extraer tags de ATT&CK: attack.t1059.001T1059.001 (normalizar a uppercase)
    • Ignorar reglas sin tags de ATT&CK (no se pueden mapear)
    • Extraer severidad del campo level
    • Extraer logsource para el campo log_sources
    • Almacenar como DetectionRule con source = "sigma", rule_format = "sigma_yaml"
    • Usar el path relativo del fichero como source_id para deduplicación
  3. No duplicar reglas existentes (comparar por source + source_id)
  4. Actualizar data_source.last_sync_at y stats

Validación:

  • La importación crea DetectionRules en la BD
  • Cada regla tiene su técnica MITRE mapeada correctamente
  • El contenido YAML de la regla se almacena completo
  • Ejecutar dos veces no duplica
  • Se importan al menos 2000+ reglas (las que tienen tags ATT&CK)
  • Las severidades se mapean correctamente
  • La descarga del ZIP funciona sin rate limiting

T-204: Servicio de importación de LOLBAS y GTFOBins

Objetivo: Importar binarios y técnicas de "living off the land" desde LOLBAS (Windows) y GTFOBins (Linux) como templates de ataque.

Archivo a crear: backend/app/services/lolbas_import_service.py

⚠️ Estrategia de descarga:

  • LOLBAS: Descargar ZIP del repositorio https://github.com/LOLBAS-Project/LOLBAS/archive/refs/heads/master.zip y parsear los YAMLs de yml/OSBinaries/, yml/OSLibraries/, yml/OSScripts/
  • GTFOBins: Descargar ZIP de https://github.com/GTFOBins/GTFOBins.github.io/archive/refs/heads/master.zip y parsear los markdowns de _gtfobins/

Lógica para LOLBAS:

  1. Descargar y descomprimir el repositorio
  2. Cada YAML contiene: Name, Description, Commands (lista con Description, Command, Usecase, MitreID), Paths
  3. Por cada binario y cada comando con MitreID:
    • Crear TestTemplate con source = "lolbas", platform = "windows"
    • El attack_procedure incluye el Command documentado
    • El tool_suggested es el nombre del binario
    • El mitre_technique_id viene del campo MitreID
  4. Usar source + Name + MitreID como clave de deduplicación

Lógica para GTFOBins:

  1. Descargar y descomprimir el repositorio
  2. Cada markdown en _gtfobins/ contiene front-matter YAML con funciones (shell, file-upload, file-download, sudo, suid, etc.)
  3. Por cada binario y función:
    • Crear TestTemplate con source = "gtfobins", platform = "linux"
    • Mapear funciones a técnicas MITRE cuando sea posible (shell → T1059, file-download → T1105, etc.)
    • El attack_procedure incluye los ejemplos de comandos
  4. Nota: GTFOBins no tiene mapeos directos a MITRE — crear un diccionario de mapeo estático de función → técnica MITRE

Validación:

  • LOLBAS importa templates para Windows
  • GTFOBins importa templates para Linux
  • Cada template tiene su técnica MITRE mapeada
  • Los comandos de ejemplo se almacenan en attack_procedure
  • No se duplican en ejecuciones posteriores
  • Las descargas de ZIP funcionan sin rate limiting

T-205: Servicio de importación de MITRE CALDERA abilities

Objetivo: Importar abilities (acciones ejecutables) del framework CALDERA como templates de tests.

Archivo a crear: backend/app/services/caldera_import_service.py

⚠️ Estrategia de descarga: Descargar ZIP del repositorio CALDERA: https://github.com/mitre/caldera/archive/refs/heads/master.zip. Las abilities están en data/abilities/ organizadas por táctica, cada una es un fichero YAML.

Lógica:

  1. Descargar y descomprimir el repositorio
  2. Navegar data/abilities/{tactic}/ — cada subdirectorio corresponde a una táctica MITRE
  3. Cada YAML de ability contiene: id, name, description, tactic, technique.attack_id, platforms (dict con OS → executors)
  4. Por cada ability:
    • Crear TestTemplate con source = "caldera"
    • Setear mitre_technique_id desde technique.attack_id
    • Setear platform desde las keys de platforms (windows, linux, darwin)
    • Extraer los comandos de ejecución de platforms.{os}.{executor}.command
    • Setear attack_procedure con los comandos
    • Usar el id de la ability como atomic_test_id para deduplicación
  5. No duplicar abilities existentes

Validación:

  • Se importan abilities de CALDERA
  • Cada template tiene la técnica MITRE correcta
  • Las plataformas soportadas se registran
  • Los comandos de ejecución se almacenan
  • Se importan al menos 300+ templates

T-206: Servicio de importación de Elastic Detection Rules

Objetivo: Importar reglas de detección del repositorio open-source de Elastic como DetectionRules.

Archivo a crear: backend/app/services/elastic_import_service.py

Dependencia a añadir: toml en requirements.txt

⚠️ Estrategia de descarga: Descargar ZIP del repositorio: https://github.com/elastic/detection-rules/archive/refs/heads/main.zip. Las reglas están en rules/ organizadas por OS/plataforma.

Lógica:

  1. Descargar y descomprimir el repositorio
  2. Parsear cada fichero .toml en rules/
  3. Cada regla TOML contiene secciones:
    • [metadata] — creation_date, maturity, etc.
    • [rule] — name, description, query (KQL), severity, type
    • [[rule.threat]] — array con framework="MITRE ATT&CK", technique (id, name)
  4. Por cada regla:
    • Extraer mappings de ATT&CK del campo rule.threat
    • Crear DetectionRule con source = "elastic", rule_format = "kql"
    • Almacenar el query KQL en rule_content
    • Usar el nombre del fichero TOML como source_id
  5. No duplicar en re-ejecuciones

Validación:

  • Se importan reglas de Elastic
  • Cada regla tiene su técnica MITRE mapeada
  • El KQL se almacena completo y correctamente
  • Las severidades se mapean
  • Se importan al menos 500+ reglas

T-207: Endpoint unificado de importación y panel de fuentes

Objetivo: Crear un panel de administración centralizado para gestionar todas las fuentes de datos.

Archivos a crear/modificar:

  • backend/app/routers/data_sources.py
  • frontend/src/pages/DataSourcesPage.tsx

Endpoints:

Método Ruta Auth Descripción
GET /data-sources admin Listar todas las fuentes
PATCH /data-sources/{id} admin Activar/desactivar, cambiar config
POST /data-sources/{id}/sync admin Ejecutar importación de una fuente
POST /data-sources/sync-all admin Importar de todas las fuentes activas
GET /data-sources/{id}/stats admin Estadísticas de la fuente

Backend — Dispatcher de sync:

Crear un dispatcher que mapee cada data_source.name a su servicio de importación:

SYNC_HANDLERS = {
    "atomic_red_team": atomic_import_service.sync,
    "sigma": sigma_import_service.sync,
    "lolbas": lolbas_import_service.sync,
    "gtfobins": lolbas_import_service.sync_gtfobins,
    "caldera": caldera_import_service.sync,
    "elastic_rules": elastic_import_service.sync,
    # d3fend y mitre_cti se añaden en fases posteriores
}

Frontend — Panel de fuentes:

  • Tabla con todas las fuentes: nombre, tipo, estado (badge), última sync, stats
  • Toggle para activar/desactivar
  • Botón de sync individual con loading state y resultado
  • Botón de "Sync All" con progreso (ejecuta secuencialmente)
  • Estadísticas: total de items importados por fuente, última fecha, errores

Validación:

  • El panel muestra todas las fuentes registradas
  • Se puede activar/desactivar cada fuente
  • Sync individual ejecuta la importación correcta y muestra resultado
  • "Sync All" ejecuta todas las fuentes activas secuencialmente
  • Las estadísticas se actualizan tras cada sync
  • Solo admin puede acceder

FASE 23 — Perfiles de Amenaza (Threat Actor Profiles)

T-208: Modelo ThreatActor

Objetivo: Crear un modelo para almacenar perfiles de grupos de amenaza (APTs) con sus TTPs asociadas.

Archivo a crear: backend/app/models/threat_actor.py

Campos de ThreatActor:

Campo Tipo Restricciones
id UUID PK, default uuid4
mitre_id String unique, nullable (ej: "G0016" para APT29)
name String not null
aliases JSONB nullable, default []
description Text nullable
country String nullable
target_sectors JSONB nullable, default []
target_regions JSONB nullable, default []
motivation String nullable (espionage, financial, destruction, etc.)
sophistication String nullable (low, medium, high, advanced)
first_seen String nullable
last_seen String nullable
references JSONB nullable, default []
mitre_url String nullable
is_active Boolean default True
created_at DateTime default utcnow

Modelo ThreatActorTechnique (tabla intermedia):

Campo Tipo Restricciones
id UUID PK, default uuid4
threat_actor_id UUID FK → threat_actors.id, not null
technique_id UUID FK → techniques.id, not null
usage_description Text nullable
first_seen_using String nullable

Índices:

Index('ix_threat_actor_techniques_actor', ThreatActorTechnique.threat_actor_id)
Index('ix_threat_actor_techniques_technique', ThreatActorTechnique.technique_id)
UniqueConstraint('threat_actor_id', 'technique_id', name='uq_actor_technique')

Generar migración.

Validación:

  • Las tablas se crean correctamente con índices
  • Se puede asociar un threat actor a múltiples técnicas
  • Una técnica puede estar asociada a múltiples threat actors
  • No se permite duplicar la misma relación actor-técnica (unique constraint)

T-209: Importación de Threat Actors desde MITRE CTI

Objetivo: Importar perfiles de grupos de amenaza desde el repositorio MITRE CTI (STIX 2.0).

Archivo a crear: backend/app/services/threat_actor_import_service.py

⚠️ Estrategia de descarga: Descargar ZIP de https://github.com/mitre/cti/archive/refs/heads/master.zip. Los datos están en enterprise-attack/.

⚠️ Complejidad del formato STIX 2.0: Los datos STIX no son triviales de parsear. Los threat actors (grupos) no contienen directamente sus técnicas. La estructura es:

  1. Objetos intrusion-set = grupos APT (lo que queremos como ThreatActor)
  2. Objetos relationship de tipo uses = conectan un intrusion-set con un attack-pattern (técnica)
  3. Objetos attack-pattern = técnicas MITRE (ya las tenemos en la BD)

Lógica detallada:

  1. Descargar y descomprimir el repositorio
  2. Cargar el JSON bundle principal: enterprise-attack/enterprise-attack.json
  3. Paso 1 — Parsear intrusion-sets: Filtrar objetos donde type == "intrusion-set"
    • Para cada grupo: extraer name, description, aliases, external_references
    • De external_references extraer el MITRE ID (donde source_name == "mitre-attack")
    • Crear ThreatActor en BD
  4. Paso 2 — Parsear relationships: Filtrar objetos donde type == "relationship" y relationship_type == "uses"
    • Filtrar relaciones donde source_ref apunte a un intrusion-set y target_ref apunte a un attack-pattern
    • Para cada relación:
      • Resolver source_ref → encontrar el ThreatActor en BD
      • Resolver target_ref → extraer el MITRE ID del attack-pattern y encontrar la Technique en BD
      • Crear ThreatActorTechnique con usage_description del campo description de la relación
  5. No duplicar en re-ejecuciones (comparar por mitre_id)

Validación:

  • Se importan 140+ threat actors
  • Cada actor tiene sus técnicas asociadas
  • Las relaciones actor-técnica son correctas (verificar APT29/G0016 con datos de MITRE)
  • Los aliases y descriptions se importan
  • Re-ejecutar no duplica
  • El campo usage_description contiene la descripción de cómo el grupo usa la técnica

T-210: Endpoints de Threat Actors

Objetivo: API para gestionar y consultar threat actors con sus técnicas y cobertura.

Archivo a crear: backend/app/routers/threat_actors.py

Endpoints:

Método Ruta Auth Descripción
GET /threat-actors autenticado Listar con filtros
GET /threat-actors/{id} autenticado Detalle con técnicas
GET /threat-actors/{id}/coverage autenticado Porcentaje de cobertura contra este actor
GET /threat-actors/{id}/gaps autenticado Técnicas del actor sin tests validados

Filtros del listado:

  • country, target_sectors, motivation, sophistication
  • search (busca en name, aliases, description)
  • Paginación: offset + limit

Lógica de /coverage:

  • Obtener todas las técnicas del actor
  • Contar cuántas tienen status_global = validated o partial
  • Retornar porcentaje y desglose

Lógica de /gaps:

  • Obtener técnicas del actor donde status_global NOT IN (validated)
  • Retornar lista con info de cada técnica y templates disponibles

Validación:

  • El listado muestra threat actors con filtros funcionales
  • El detalle incluye las técnicas del actor
  • El coverage calcula correctamente el porcentaje
  • El gap analysis identifica técnicas sin tests validados
  • La paginación funciona

T-211: Frontend de Threat Actors — Listado

Objetivo: Página de listado de threat actors con filtros y cards.

Archivos a crear:

  • frontend/src/api/threat-actors.ts
  • frontend/src/pages/ThreatActorsPage.tsx

Contenido:

  • Grid de cards con: nombre, país (bandera), sectores, motivación, nº técnicas, cobertura %
  • Filtros laterales por sector, región, motivación
  • Buscador
  • Paginación

Ruta: /threat-actors — añadir al router y al sidebar.

Validación:

  • La página carga y muestra threat actors del backend
  • Los filtros funcionan
  • Cada card muestra la información correcta
  • Click en un actor navega al detalle
  • La ruta aparece en el sidebar

T-212: Frontend de Threat Actors — Detalle con heatmap y gap analysis

Objetivo: Página de detalle de un threat actor con su perfil, heatmap de técnicas y gap analysis.

Archivo a crear: frontend/src/pages/ThreatActorDetailPage.tsx

Secciones:

  1. Header: nombre, aliases, country, motivación, sophistication
  2. Description: texto descriptivo del grupo
  3. Heatmap de técnicas: mini-matriz ATT&CK mostrando solo las técnicas de este actor, coloreadas por estado de cobertura
  4. Coverage gap analysis: tabla de técnicas NO cubiertas, con templates disponibles
  5. Lista de tests existentes vinculados a técnicas de este actor

Validación:

  • El perfil completo se muestra
  • El heatmap renderiza solo las técnicas del actor
  • Los colores corresponden al estado de cobertura
  • El gap analysis lista técnicas sin cobertura
  • Si hay templates disponibles para una gap, se muestra un indicador

FASE 24 — MITRE D3FEND: Contramedidas Defensivas

T-213: Modelo y importación de D3FEND

Objetivo: Integrar el framework MITRE D3FEND para mapear cada técnica ATT&CK a las contramedidas defensivas recomendadas.

Archivo a crear: backend/app/models/defensive_technique.py

Campos de DefensiveTechnique:

Campo Tipo Restricciones
id UUID PK, default uuid4
d3fend_id String unique, not null (ej: "D3-AL")
name String not null
description Text nullable
tactic String nullable (D3FEND tactic: Detect, Isolate, etc.)
d3fend_url String nullable
created_at DateTime default utcnow

Modelo DefensiveTechniqueMapping (mapeo ATT&CK → D3FEND):

Campo Tipo Restricciones
id UUID PK, default uuid4
attack_technique_id UUID FK → techniques.id, not null
defensive_technique_id UUID FK → defensive_techniques.id, not null

Servicio de importación backend/app/services/d3fend_import_service.py:

  1. Usar la API de D3FEND (https://d3fend.mitre.org/api/) para obtener técnicas defensivas
  2. Usar el endpoint de mappings ATT&CK-D3FEND (/api/offensive-technique/{attack_id}.json) para establecer relaciones
  3. Almacenar técnicas defensivas y sus mapeos

Validación:

  • Se importan 200+ técnicas defensivas D3FEND
  • Los mappings ATT&CK → D3FEND se crean correctamente
  • Desde una técnica ATT&CK se pueden consultar sus contramedidas D3FEND

T-214: UI de contramedidas en vista de técnica y test

Objetivo: Mostrar las contramedidas D3FEND recomendadas en la vista de detalle de técnica y en la pestaña Blue Team del test.

Archivos a modificar:

  • frontend/src/pages/TechniqueDetailPage.tsx — nueva sección "Recommended Defenses"
  • frontend/src/components/test-detail/TeamTabs.tsx — en pestaña Blue, mostrar contramedidas

Sección en TechniqueDetailPage:

  • Lista de contramedidas D3FEND recomendadas para esta técnica
  • Cada contramedida con: nombre, descripción, tactic D3FEND, link a documentación

En pestaña Blue Team del test:

  • Panel "Recommended Detection Approaches"
  • Lista de contramedidas D3FEND aplicables
  • Reglas de detección Sigma/Elastic disponibles para esta técnica (del catálogo)

Validación:

  • La vista de técnica muestra contramedidas D3FEND
  • La pestaña Blue Team muestra las contramedidas y reglas de detección
  • Los links a documentación D3FEND funcionan

FASE 25 — Reglas de Detección Sugeridas por Test

T-215: Asociar DetectionRules a tests y templates

Objetivo: Vincular reglas de detección a los tests y templates para que el Blue Team sepa qué regla debería haber detectado el ataque.

Modelo a crear: tabla intermedia test_template_detection_rules:

Campo Tipo Restricciones
id UUID PK, default uuid4
test_template_id UUID FK → test_templates.id, nullable
detection_rule_id UUID FK → detection_rules.id, not null
is_primary Boolean default False

Lógica de auto-asociación:

Al importar templates y reglas de detección, asociar automáticamente por técnica MITRE:

  • Un template de ataque T1059.001 se asocia a todas las reglas Sigma/Elastic para T1059.001
  • Marcar como is_primary las reglas cuya severidad sea >= high

Endpoints nuevos:

GET /test-templates/{id}/detection-rules → reglas de detección sugeridas
GET /detection-rules?technique={mitre_id} → reglas para una técnica
GET /detection-rules?source={source} → reglas por fuente

Validación:

  • Los templates se asocian automáticamente a sus reglas de detección
  • GET /test-templates/{id}/detection-rules retorna las reglas correctas
  • Las reglas primarias se marcan correctamente
  • Filtrar por técnica y fuente funciona

T-216: UI de reglas de detección y checklist en pestaña Blue Team

Objetivo: Cuando el Blue Team evalúa un test, mostrarle las reglas de detección que deberían haber saltado, permitiéndole marcar cuáles detectaron y cuáles no.

Archivos a crear:

  • frontend/src/components/test-detail/DetectionRuleChecklist.tsx
  • backend/app/models/test_detection_result.py

Modelo TestDetectionResult:

Campo Tipo Restricciones
id UUID PK, default uuid4
test_id UUID FK → tests.id, not null
detection_rule_id UUID FK → detection_rules.id, not null
triggered Boolean nullable (null = not evaluated)
notes Text nullable
evaluated_by UUID FK → users.id, nullable
evaluated_at DateTime nullable

Componente DetectionRuleChecklist:

  • Lista de reglas de detección asociadas al test/template
  • Cada regla con: título, severidad (badge color), fuente (Sigma/Elastic), contenido expandible
  • Checkbox: "Triggered" / "Not triggered" / "Not applicable"
  • Campo de notas por regla
  • Resumen: X/Y reglas detectaron (con porcentaje)

Validación:

  • El checklist muestra las reglas de detección correctas
  • Se puede marcar cada regla como triggered/not triggered/N.A.
  • Las notas se guardan correctamente
  • El resumen X/Y se calcula
  • Los resultados se persisten en la BD

FASE 26 — Campañas de Tests (Attack Chains)

T-217: Modelo Campaign

Objetivo: Crear campañas que agrupen múltiples tests en una secuencia que simula una cadena de ataque completa (kill chain).

Archivo a crear: backend/app/models/campaign.py

Campos de Campaign:

Campo Tipo Restricciones
id UUID PK, default uuid4
name String not null
description Text nullable
type String not null (custom, apt_emulation, kill_chain, compliance)
threat_actor_id UUID FK → threat_actors.id, nullable
status String default "draft" (draft, active, completed, archived)
created_by UUID FK → users.id, nullable
scheduled_at DateTime nullable
completed_at DateTime nullable
target_platform String nullable
tags JSONB nullable, default []
created_at DateTime default utcnow

Campos de CampaignTest (tests de la campaña, con orden):

Campo Type Restricciones
id UUID PK, default uuid4
campaign_id UUID FK → campaigns.id, not null
test_id UUID FK → tests.id, not null
order_index Integer not null (posición en la cadena)
depends_on UUID FK → campaign_tests.id, nullable (test previo)
phase String nullable (initial_access, execution, persistence, etc.)

⚠️ Prevención de dependencias circulares: El campo depends_on es una FK self-referencial que podría crear ciclos (A depende de B que depende de A). Implementar validación:

def validate_no_circular_dependency(db, campaign_id, test_id, depends_on_id):
    """Recorrer la cadena de depends_on y verificar que no se forma un ciclo."""
    visited = set()
    current = depends_on_id
    while current is not None:
        if current in visited or current == test_id:
            raise HTTPException(400, "Circular dependency detected")
        visited.add(current)
        parent = db.query(CampaignTest).filter_by(id=current).first()
        current = parent.depends_on if parent else None

Generar migración.

Validación:

  • Las tablas se crean correctamente
  • Una campaña puede contener múltiples tests ordenados
  • Los tests pueden tener dependencias
  • Intentar crear una dependencia circular falla con error descriptivo
  • El campo phase acepta las fases del kill chain

T-218: Endpoints y lógica de Campañas

Archivos a crear:

  • backend/app/routers/campaigns.py
  • backend/app/services/campaign_service.py

Endpoints:

Método Ruta Auth Descripción
GET /campaigns autenticado Listar campañas con filtros
POST /campaigns red_tech, admin Crear campaña
GET /campaigns/{id} autenticado Detalle con tests y progreso
PATCH /campaigns/{id} creador, admin Actualizar campaña
POST /campaigns/{id}/tests red_tech, admin Añadir test a campaña
DELETE /campaigns/{id}/tests/{test_id} creador, admin Quitar test de campaña
POST /campaigns/{id}/activate red_tech, admin Activar campaña
POST /campaigns/{id}/complete red_lead, admin Marcar como completada
GET /campaigns/{id}/progress autenticado Progreso: tests por estado
POST /campaigns/from-threat-actor/{actor_id} red_tech, admin Auto-generar campaña desde gaps

Servicio de generación automática:

generate_campaign_from_threat_actor(db, actor_id, user):

  1. Obtener técnicas del actor no cubiertas (via /threat-actors/{id}/gaps)
  2. Para cada técnica sin test validado, buscar el mejor template disponible (priorizar por severity)
  3. Crear test desde template
  4. Crear campaña con los tests ordenados por kill chain (tactic order: reconnaissance → initial_access → execution → ... → exfiltration)
  5. Retornar la campaña con sus tests

Validación:

  • CRUD de campañas funciona
  • Se pueden añadir/quitar tests con orden
  • Activar campaña cambia status y notifica
  • El progreso se calcula correctamente
  • La generación automática desde threat actor crea una campaña coherente
  • Los tests se ordenan por kill chain

T-219: UI de Campañas — Listado

Archivo a crear: frontend/src/pages/CampaignsPage.tsx

Contenido:

  • Grid de cards con: nombre, tipo (badge), threat actor (si aplica), status, progreso %, nº tests
  • Filtros: tipo, status, threat actor
  • Botón "New Campaign" y "Generate from Threat Actor"

Ruta: /campaigns — añadir al router y sidebar.

Validación:

  • El listado muestra campañas con filtros
  • Cada card muestra la información correcta
  • Los botones de creación funcionan
  • La ruta aparece en el sidebar

T-220: UI de Campañas — Detalle con Kill Chain Timeline

Archivos a crear:

  • frontend/src/pages/CampaignDetailPage.tsx
  • frontend/src/components/CampaignTimeline.tsx

CampaignDetailPage:

  • Header: nombre, descripción, status, threat actor linkado, dates
  • Kill Chain Timeline: visualización horizontal mostrando los tests agrupados por fase (Initial Access → Execution → Persistence → ...)
    • Cada nodo es un test con color según su estado
    • Flechas de dependencia entre tests
    • Click en un nodo abre el detalle del test
  • Progress Panel: barra de progreso + contadores por estado
  • Tests Table: tabla con todos los tests de la campaña, reordenable

Validación:

  • El timeline visual renderiza correctamente los tests por fase
  • El progreso se actualiza al cambiar estado de tests
  • Se pueden añadir/quitar tests desde la UI
  • Click en nodos navega al detalle del test

FASE 27 — Heatmap ATT&CK Avanzado (estilo Navigator)

T-221: Backend de layers para heatmap

Objetivo: Crear un sistema de "layers" (capas) que permitan visualizar la matriz ATT&CK con diferentes datos superpuestos, similar al MITRE ATT&CK Navigator.

Archivo a crear: backend/app/routers/heatmap.py

Endpoints:

Método Ruta Auth Descripción
GET /heatmap/coverage autenticado Capa de cobertura (status de cada técnica)
GET /heatmap/threat-actor/{actor_id} autenticado Capa de técnicas usadas por un actor
GET /heatmap/detection-rules autenticado Capa de cobertura de reglas de detección
GET /heatmap/campaign/{campaign_id} autenticado Capa de progreso de una campaña
GET /heatmap/export-navigator autenticado Exportar como JSON del ATT&CK Navigator

Formato de respuesta:

Todas las capas retornan el mismo formato base, compatible con ATT&CK Navigator:

{
  "name": "Aegis Coverage",
  "versions": {"attack": "15", "navigator": "5.0", "layer": "4.5"},
  "domain": "enterprise-attack",
  "description": "Coverage layer generated by Aegis",
  "filters": {
    "platforms": ["windows", "linux", "macos"]
  },
  "gradient": {
    "colors": ["#ff6666", "#ffff66", "#66ff66"],
    "minValue": 0,
    "maxValue": 100
  },
  "techniques": [
    {
      "techniqueID": "T1059.001",
      "tactic": "execution",
      "color": "#00ff00",
      "score": 100,
      "comment": "Validated - 3 tests passed, 5 detection rules active",
      "enabled": true,
      "metadata": [
        {"name": "tests_count", "value": "3"},
        {"name": "detection_rules", "value": "5"},
        {"name": "last_validated", "value": "2026-01-15"}
      ]
    }
  ]
}

Lógica por endpoint:

  • /heatmap/coverage: score basado en status_global (validated=100, partial=60, in_progress=30, not_covered=10, not_evaluated=0). Color derivado del score.
  • /heatmap/threat-actor/{id}: técnicas del actor con color de cobertura, técnicas no del actor con enabled=false.
  • /heatmap/detection-rules: score basado en ratio de reglas de detección disponibles vs evaluadas para cada técnica.
  • /heatmap/campaign/{id}: solo técnicas de la campaña, color basado en estado del test asociado.
  • /heatmap/export-navigator: acepta query param layer (coverage, threat-actor, detection-rules, campaign) y layer_id (actor_id o campaign_id si aplica). Retorna fichero JSON descargable con header Content-Disposition: attachment.

Query params comunes para filtrar todas las capas:

  • platforms: filtrar por plataforma (windows, linux, macos)
  • tactics: filtrar por táctica
  • min_score: score mínimo para incluir

Validación:

  • /heatmap/coverage retorna datos para todas las técnicas con scores correctos
  • /heatmap/threat-actor/{id} resalta solo las técnicas del actor
  • /heatmap/detection-rules calcula correctamente la cobertura de reglas
  • /heatmap/campaign/{id} muestra solo técnicas de la campaña
  • /heatmap/export-navigator genera un JSON descargable
  • El JSON exportado se puede importar en ATT&CK Navigator real (verificar manualmente en https://mitre-attack.github.io/attack-navigator/)
  • Los filtros de platform y tactic funcionan en todas las capas

T-222: Frontend de heatmap — Componente base

Objetivo: Crear el componente de heatmap reutilizable con renderizado eficiente de la matriz ATT&CK.

Archivos a crear:

  • frontend/src/components/heatmap/AdvancedHeatmap.tsx
  • frontend/src/components/heatmap/HeatmapCell.tsx
  • frontend/src/components/heatmap/HeatmapTooltip.tsx
  • frontend/src/components/heatmap/HeatmapLegend.tsx

⚠️ Rendimiento con 3000+ técnicas: No renderizar toda la matriz al DOM. Usar virtualización:

  1. Opción preferida: Usar react-window (o @tanstack/react-virtual) para virtualizar tanto filas como columnas
  2. Las columnas son las tácticas (14 columnas en Enterprise ATT&CK — se renderizan todas)
  3. Las filas son las técnicas dentro de cada táctica (esto es lo que necesita virtualización — algunas tácticas tienen 100+ técnicas)
  4. Implementar un layout CSS Grid donde cada columna de táctica tiene scroll virtual independiente

Añadir dependencia: @tanstack/react-virtual o react-window a package.json

Componente AdvancedHeatmap:

Props:

interface HeatmapProps {
  techniques: HeatmapTechnique[];  // datos de la capa activa
  onCellClick: (techniqueId: string) => void;
  colorScale: (score: number) => string;  // función de color
}

Funcionalidades:

  • Grid con columnas = tácticas (14 columnas de Enterprise ATT&CK)
  • Dentro de cada columna, lista virtualizada de técnicas
  • Cada celda muestra: mitre_id + nombre truncado, coloreada por score
  • Header de columna con nombre de la táctica y conteo de técnicas
  • Zoom: slider que controla el tamaño de las celdas (compact/normal/expanded)
  • Scroll horizontal para ver todas las tácticas

Componente HeatmapCell:

  • Renderiza una celda individual
  • Color de fondo según score/status
  • Borde especial si review_required = true
  • Indicadores opcionales (iconos pequeños): 🔴 sin tests, ⚠️ review requerida, validado

Componente HeatmapTooltip:

Al hacer hover sobre una celda:

┌─────────────────────────────────┐
│ T1059.001 - PowerShell          │
│ Status: Validated ✅             │
│ Score: 85/100                   │
│ Tests: 3 validated              │
│ Detection Rules: 12 available   │
│ Last validated: 2026-01-15      │
│ Threat Actors: APT29, APT32     │
└─────────────────────────────────┘

Componente HeatmapLegend:

  • Muestra gradiente de colores con labels
  • Se adapta según la capa activa
  • Para coverage: rojo → amarillo → verde
  • Para threat actor: gris (no aplica) → rojo (no cubierto) → verde (cubierto)

Validación:

  • El heatmap renderiza todas las técnicas agrupadas por táctica
  • Los colores corresponden al status/score correcto
  • Los tooltips muestran información completa
  • Zoom in/out cambia el tamaño de las celdas
  • Scroll horizontal funciona para ver todas las tácticas
  • Con 3000+ técnicas no hay lag visible (virtualización funciona)
  • Click en celda ejecuta el callback correctamente

T-223: Frontend de heatmap — Selector de capas, filtros y export

Archivos a crear/modificar:

  • frontend/src/components/heatmap/HeatmapLayerSelector.tsx
  • frontend/src/components/heatmap/HeatmapFilters.tsx
  • frontend/src/pages/MatrixPage.tsx — rediseñar la página existente de técnicas

Componente HeatmapLayerSelector:

  • Radio buttons o tabs para seleccionar la capa activa:
    • 🛡️ Coverage (default)
    • 👤 Threat Actor (al seleccionar, aparece un dropdown para elegir el actor)
    • 🔍 Detection Rules
    • 📋 Campaign (al seleccionar, aparece un dropdown para elegir la campaña)
  • Al cambiar de capa, se hace fetch al endpoint correspondiente de /heatmap/

Componente HeatmapFilters:

  • Filtros inline (horizontal, encima de la matriz):
    • Plataforma: checkboxes (Windows, Linux, macOS)
    • Táctica: multi-select
    • Status: multi-select
    • Score mínimo: slider (0-100)

Página MatrixPage rediseñada:

Layout:

┌──────────────────────────────────────────────────────────┐
│  [Layer Selector]    [Filters]    [Export ▼]  [Zoom ⊕⊖] │
├──────────────────────────────────────────────────────────┤
│                                                          │
│  AdvancedHeatmap                                         │
│  ┌──────┬──────┬──────┬──────┬───────────────────────┐  │
│  │Recon │Init  │Exec  │Pers  │ ...más tácticas       │  │
│  │      │Access│      │      │                        │  │
│  │ T1595│ T1190│ T1059│ T1547│                        │  │
│  │ T1592│ T1133│ T1203│ T1053│                        │  │
│  │ ...  │ ...  │ ...  │ ...  │                        │  │
│  └──────┴──────┴──────┴──────┴───────────────────────┘  │
│                                                          │
├──────────────────────────────────────────────────────────┤
│  HeatmapLegend                                           │
└──────────────────────────────────────────────────────────┘

Botón Export:

Dropdown con opciones:

  • "Export Navigator JSON" → descarga el fichero JSON
  • "Copy Navigator URL" → copia URL que abre el layer en ATT&CK Navigator web

Validación:

  • Seleccionar capas diferentes cambia la visualización
  • Seleccionar "Threat Actor" muestra dropdown de actores y cambia el heatmap
  • Los filtros reducen las técnicas mostradas en tiempo real
  • Export genera un JSON descargable
  • El JSON exportado se puede importar en ATT&CK Navigator real
  • La leyenda se actualiza según la capa activa
  • El zoom funciona con el slider

FASE 28 — Scoring y Métricas Avanzadas

T-224: Sistema de scoring de cobertura

Objetivo: Implementar un sistema de puntuación granular de 0-100 por técnica, táctica, actor y organización.

Archivos a crear:

  • backend/app/services/scoring_service.py
  • backend/app/models/scoring_config.py (opcional, para pesos en BD)

⚠️ Pesos configurables — NO hardcodear:

Los pesos del scoring deben ser configurables. Dos opciones:

Opción A — Variables de entorno / config.py (más simple):

# config.py
class Settings(BaseSettings):
    # ... existentes ...
    SCORING_WEIGHT_TESTS: int = 40
    SCORING_WEIGHT_DETECTION_RULES: int = 20
    SCORING_WEIGHT_D3FEND: int = 15
    SCORING_WEIGHT_FRESHNESS: int = 15
    SCORING_WEIGHT_PLATFORM_DIVERSITY: int = 10

Opción B — Tabla en BD (más flexible, permite cambiar sin restart):

class ScoringConfig(Base):
    __tablename__ = "scoring_configs"
    id = Column(UUID, primary_key=True, default=uuid4)
    key = Column(String, unique=True, not null)     # ej: "weight_tests"
    value = Column(Integer, not null)                # ej: 40
    description = Column(String, nullable=True)
    updated_at = Column(DateTime, default=utcnow)

Usar Opción A para el MVP. Migrar a Opción B si se necesita cambiar pesos frecuentemente.

Lógica de scoring por técnica:

def calculate_technique_score(technique, db) -> dict:
    """
    Retorna:
    {
        "total_score": 75,
        "breakdown": {
            "tests_validated": {"score": 35, "max": 40, "detail": "3/4 tests detected"},
            "detection_rules": {"score": 15, "max": 20, "detail": "8/12 rules triggered"},
            "d3fend_coverage": {"score": 10, "max": 15, "detail": "2/3 countermeasures"},
            "freshness": {"score": 10, "max": 15, "detail": "Last test 45 days ago"},
            "platform_diversity": {"score": 5, "max": 10, "detail": "2/3 platforms covered"}
        }
    }
    """
    weights = settings  # o cargar de BD

    # Tests validados con detección
    validated_tests = [t for t in technique.tests if t.state == "validated"]
    detected = [t for t in validated_tests if t.detection_result == "detected"]
    if validated_tests:
        test_score = (len(detected) / len(validated_tests)) * weights.SCORING_WEIGHT_TESTS
    else:
        test_score = 0

    # Reglas de detección: ratio de reglas triggered vs total
    # (requiere datos de TestDetectionResult)

    # D3FEND: ratio de contramedidas verificadas
    # (requiere datos de DefensiveTechniqueMapping)

    # Frescura: tests más recientes = más puntos
    # < 90 días = 100%, 90-180 = 50%, > 180 = 0%

    # Diversidad de plataformas
    # Contar plataformas únicas cubiertas vs totales disponibles

    total = min(test_score + detection_score + d3fend_score + freshness_score + diversity_score, 100)
    return {"total_score": round(total, 1), "breakdown": {...}}

Lógica de scoring por threat actor:

def calculate_actor_coverage_score(actor, db) -> dict:
    """
    Promedio ponderado de scores de las técnicas del actor.
    Retorna score global + desglose por técnica.
    """
    techniques = [mapping.technique for mapping in actor.technique_mappings]
    scores = [calculate_technique_score(t, db)["total_score"] for t in techniques]
    return {
        "total_score": round(sum(scores) / len(scores), 1) if scores else 0,
        "techniques_count": len(techniques),
        "techniques_covered": len([s for s in scores if s > 50]),
        "techniques_detail": [...]
    }

Lógica de scoring global:

def calculate_organization_score(db) -> dict:
    """
    Score global de la organización:
    {
        "overall_score": 62.5,
        "total_coverage": 58.0,        # promedio de todas las técnicas evaluadas
        "critical_coverage": 71.0,      # técnicas con severity >= high
        "detection_maturity": 45.0,     # basado en reglas de detección
        "response_readiness": 30.0,     # basado en remediaciones completadas
        "techniques_evaluated": 450,
        "techniques_total": 650
    }
    """

Endpoints:

Método Ruta Auth Descripción
GET /scores/technique/{mitre_id} autenticado Score detallado con breakdown
GET /scores/tactic/{tactic} autenticado Score promedio por táctica
GET /scores/threat-actor/{id} autenticado Score de cobertura contra actor
GET /scores/organization autenticado Score global de la organización
GET /scores/history autenticado Historial de scores (query: period=30d/90d/1y)
GET /scores/config admin Ver pesos actuales
PATCH /scores/config admin Modificar pesos

Validación:

  • El score de técnica se calcula correctamente (0-100) con breakdown detallado
  • Cambiar los pesos via config cambia los scores resultantes
  • El score de threat actor refleja la cobertura real (verificar con datos de demo)
  • El score de organización agrega correctamente
  • El historial muestra la evolución temporal con puntos de datos
  • GET /scores/config retorna los pesos actuales

T-225: Métricas operativas (MTTD, MTTR, Detection Efficacy)

Objetivo: Implementar métricas operativas del equipo de seguridad.

Archivo a crear: backend/app/services/operational_metrics_service.py

Métricas a calcular:

  1. MTTD (Mean Time to Detect):

    • Calcular para cada test validado: tiempo entre que el test entra en red_executing y entra en blue_evaluating
    • Usar timestamps del audit_log para extraer las fechas de cada transición
    • Retornar media, mediana, min, max
  2. MTTR (Mean Time to Respond/Remediate):

    • Para tests con remediation_status = completed: tiempo entre detection_result seteado y remediation_status = completed
    • Solo calculable para tests que tienen remediación documentada
    • Retornar media, mediana, min, max
  3. Detection Efficacy:

    • detected_count / total_validated_tests * 100
    • Desglosar: detected, partially_detected, not_detected
  4. Alert Fidelity:

    • Ratio de TestDetectionResult.triggered = True vs total evaluados
    • Solo calculable si hay datos de evaluación de reglas
  5. Coverage Velocity:

    • Contar técnicas que cambiaron a validated o partial por semana
    • Query: GROUP BY date_trunc('week', last_review_date) ORDER BY week
  6. Validation Throughput:

    • Tests que pasaron a validated o rejected por semana
    • Query: GROUP BY date_trunc('week', validated_at/rejected_at)
  7. Rejection Rate:

    • rejected_count / (validated_count + rejected_count) * 100
    • Desglosar por red_lead y blue_lead

Endpoints:

Método Ruta Auth Descripción
GET /metrics/operational autenticado Todas las métricas operativas actuales
GET /metrics/operational/trend autenticado Tendencia temporal. Query: `period=30d
GET /metrics/operational/by-team autenticado Métricas desglosadas por equipo (red vs blue)

Formato de respuesta de /metrics/operational:

{
  "mttd": {"mean_hours": 12.5, "median_hours": 8.0, "min_hours": 1.2, "max_hours": 72.0, "sample_size": 45},
  "mttr": {"mean_hours": 48.0, "median_hours": 36.0, "sample_size": 20},
  "detection_efficacy": {"percentage": 72.5, "detected": 29, "partially": 8, "not_detected": 3, "total": 40},
  "alert_fidelity": {"percentage": 65.0, "triggered": 130, "not_triggered": 70, "total_evaluated": 200},
  "coverage_velocity": {"techniques_per_week": 5.2, "trend": "improving"},
  "validation_throughput": {"tests_per_week": 8.3, "trend": "stable"},
  "rejection_rate": {"percentage": 15.0, "by_red_lead": 10.0, "by_blue_lead": 20.0}
}

Validación:

  • MTTD se calcula correctamente a partir de timestamps del audit_log
  • MTTR incluye solo tests con remediación completada
  • Detection Efficacy es un porcentaje correcto con desglose
  • Las tendencias muestran evolución temporal (array de puntos por semana)
  • El desglose por equipo separa métricas red vs blue correctamente
  • Si no hay datos suficientes para una métrica, retorna null en lugar de error

T-226: Dashboard ejecutivo con scores y métricas

Objetivo: Crear una vista de dashboard ejecutivo pensada para presentar a dirección.

Archivo a crear: frontend/src/pages/ExecutiveDashboardPage.tsx

Dependencia a añadir: recharts en package.json (para gráficos)

Secciones del dashboard:

  1. Score Card Principal:

    • Gauge circular (tipo velocímetro) con el score global de la organización
    • Color: rojo (0-30), naranja (30-50), amarillo (50-70), verde (70-100)
    • Texto central: score numérico
    • Debajo: desglose en sub-scores (coverage, detection maturity, response readiness)
  2. Trend Chart:

    • Gráfico de línea (recharts LineChart) mostrando evolución del score en los últimos 90 días
    • Línea principal: score global
    • Líneas secundarias opcionales: sub-scores
    • Tooltip con fecha y valor
  3. Top 5 Threat Actors más relevantes:

    • Cards horizontales con: nombre, bandera de país, % cobertura con barra de progreso
    • Ordenados por relevancia (sectores target que coinciden con la organización)
  4. Operational KPIs:

    • 4 cards en fila: MTTD, MTTR, Detection Efficacy, Validation Throughput
    • Cada card con: valor actual, tendencia (↑ mejorando / ↓ empeorando / → estable), sparkline mini
  5. Coverage por táctica:

    • Barras horizontales (recharts BarChart horizontal) con % de cobertura por táctica
    • Color de barra según porcentaje
  6. Critical Gaps:

    • Tabla con las top 10 técnicas de alta severidad sin cobertura
    • Columnas: MITRE ID, nombre, táctica, nº threat actors que la usan, templates disponibles
    • Click en fila navega al detalle de la técnica
  7. Team Performance:

    • Dos columnas: Red Team | Blue Team
    • Cada columna con: tests completados esta semana, tiempo medio de respuesta, rejection rate

Ruta: /executive-dashboard — accesible para roles admin, red_lead, blue_lead.

Validación:

  • El dashboard carga con datos reales del backend
  • El gauge del score global funciona y se colorea correctamente
  • El gráfico de tendencia renderiza con datos históricos
  • Los KPIs muestran valores correctos con tendencias
  • Los critical gaps enlazan al detalle de la técnica
  • Solo roles de liderazgo y admin pueden acceder
  • El dashboard es responsive (se adapta a pantallas pequeñas)

FASE 29 — Compliance y Reportes Avanzados

T-227: Modelo de mapeo a frameworks de compliance

Objetivo: Mapear técnicas ATT&CK y controles de seguridad a frameworks de compliance.

Archivo a crear: backend/app/models/compliance.py

Campos de ComplianceFramework:

Campo Tipo Restricciones
id UUID PK, default uuid4
name String unique, not null (ej: "NIST 800-53")
version String nullable
description Text nullable
url String nullable
is_active Boolean default True
created_at DateTime default utcnow

Campos de ComplianceControl:

Campo Tipo Restricciones
id UUID PK, default uuid4
framework_id UUID FK → compliance_frameworks.id, not null
control_id String not null (ej: "AC-2", "PR.AC-1")
title String not null
description Text nullable
category String nullable

Campos de ComplianceControlMapping (mapeo a técnicas ATT&CK):

Campo Tipo Restricciones
id UUID PK, default uuid4
compliance_control_id UUID FK → compliance_controls.id, not null
technique_id UUID FK → techniques.id, not null

Índices:

Index('ix_compliance_controls_framework', ComplianceControl.framework_id)
Index('ix_compliance_mappings_control', ComplianceControlMapping.compliance_control_id)
Index('ix_compliance_mappings_technique', ComplianceControlMapping.technique_id)
UniqueConstraint('compliance_control_id', 'technique_id', name='uq_control_technique')

⚠️ Fuente de datos para mappings NIST → ATT&CK: Los mappings oficiales están en el repositorio del Center for Threat-Informed Defense de MITRE: https://github.com/center-for-threat-informed-defense/attack_to_nist_mapping

El formato es un STIX bundle JSON con objetos relationship que conectan controles NIST a técnicas ATT&CK. El servicio de importación debe:

  1. Descargar ZIP del repositorio
  2. Parsear el JSON de mappings
  3. Crear ComplianceFramework, ComplianceControls, y ComplianceControlMappings

Servicio de importación backend/app/services/compliance_import_service.py:

def import_nist_800_53_mappings(db):
    """
    1. Descargar ZIP de https://github.com/center-for-threat-informed-defense/attack_to_nist_mapping
    2. Parsear el STIX bundle JSON
    3. Crear framework 'NIST 800-53 Rev 5'
    4. Para cada control: crear ComplianceControl
    5. Para cada relationship: crear ComplianceControlMapping vinculando control → técnica
    """

Generar migración.

Validación:

  • Las tablas se crean correctamente con índices
  • El import de NIST 800-53 crea framework, controles y mappings
  • Se puede consultar qué controles aplican a una técnica
  • Se puede consultar qué técnicas cubren un control
  • No se permite duplicar la misma relación control-técnica (unique constraint)
  • Re-ejecutar la importación no duplica datos

T-228: Endpoints y generación de reportes de compliance

Archivo a crear: backend/app/routers/compliance.py

Endpoints:

Método Ruta Auth Descripción
GET /compliance/frameworks autenticado Listar frameworks disponibles
GET /compliance/frameworks/{id}/status autenticado Estado de cada control
GET /compliance/frameworks/{id}/report autenticado Reporte completo de compliance
GET /compliance/frameworks/{id}/report/csv autenticado Export CSV del reporte
GET /compliance/frameworks/{id}/gaps autenticado Controles con técnicas no cubiertas

Lógica del status de cada control:

Para cada control del framework:

  1. Obtener las técnicas ATT&CK mapeadas via ComplianceControlMapping
  2. Para cada técnica, obtener su status_global y su score (de scoring_service)
  3. Clasificar el control:
    • covered (verde): todas las técnicas mapeadas tienen score >= 70
    • partially_covered (amarillo): al menos una técnica tiene score >= 30 pero alguna tiene score < 70
    • not_covered (rojo): todas las técnicas tienen score < 30
    • not_evaluated (gris): ninguna técnica tiene tests
  4. Calcular score del control: promedio de scores de sus técnicas

Formato de respuesta de /frameworks/{id}/status:

{
  "framework": {"id": "...", "name": "NIST 800-53 Rev 5"},
  "summary": {
    "total_controls": 150,
    "covered": 85,
    "partially_covered": 35,
    "not_covered": 20,
    "not_evaluated": 10,
    "compliance_percentage": 56.7
  },
  "controls": [
    {
      "control_id": "AC-2",
      "title": "Account Management",
      "category": "Access Control",
      "status": "partially_covered",
      "score": 55.0,
      "techniques_count": 5,
      "techniques_covered": 3,
      "techniques": [
        {"mitre_id": "T1078", "name": "Valid Accounts", "score": 80.0, "status": "validated"},
        {"mitre_id": "T1136", "name": "Create Account", "score": 30.0, "status": "in_progress"}
      ]
    }
  ]
}

Formato CSV export:

control_id,title,category,status,score,techniques_total,techniques_covered,technique_ids
AC-2,Account Management,Access Control,partially_covered,55.0,5,3,"T1078,T1136,T1098,T1087,T1069"

Lógica de /gaps:

Retornar solo controles con status not_covered o partially_covered, incluyendo para cada uno:

  • Las técnicas que faltan por cubrir
  • Templates disponibles para esas técnicas
  • Número de threat actors que usan esas técnicas (para priorizar)

Validación:

  • El estado de cada control se calcula correctamente según la lógica de scoring
  • El reporte incluye todos los controles del framework
  • El compliance_percentage es correcto: (covered + partially_covered*0.5) / total * 100
  • El export CSV se descarga y abre correctamente en Excel
  • Los gaps listan controles con técnicas no cubiertas
  • Las técnicas dentro de cada control están ordenadas por score (ascendente = prioridad)

T-229: UI de Compliance

Archivos a crear:

  • frontend/src/pages/CompliancePage.tsx
  • frontend/src/components/compliance/ComplianceGauge.tsx
  • frontend/src/components/compliance/ControlsTable.tsx

CompliancePage:

Layout:

┌──────────────────────────────────────────────────────────────┐
│  [Framework Selector ▼ NIST 800-53]     [Export CSV] [Export PDF] │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐       │
│  │ 56.7%    │ │  85      │ │  35      │ │  20      │       │
│  │ Overall  │ │ Covered  │ │ Partial  │ │ Not Cov  │       │
│  └──────────┘ └──────────┘ └──────────┘ └──────────┘       │
│                                                              │
│  ┌───────────────────────────────────────────────────────┐  │
│  │ Filtros: [Status ▼] [Category ▼] [Search...]         │  │
│  ├───────────────────────────────────────────────────────┤  │
│  │ Control │ Title              │ Status  │ Score │ Tech │  │
│  │ AC-2    │ Account Management │ ●Partial│ 55.0  │ 3/5  │  │
│  │ AC-3    │ Access Enforcement │ ●Covered│ 82.0  │ 4/4  │  │
│  │ AC-4    │ Information Flow   │ ●NotCov │ 12.0  │ 0/3  │  │
│  │ ...     │ ...                │ ...     │ ...   │ ...  │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                              │
│  Expandir un control muestra sus técnicas con detalle       │
└──────────────────────────────────────────────────────────────┘

Interacciones:

  • Selector de framework: dropdown con frameworks disponibles
  • Cards superiores: resumen con conteos y porcentaje global (gauge circular)
  • Tabla de controles: expandible — click en un control muestra sus técnicas con status y score
  • Filtros: status (covered/partial/not_covered), categoría, búsqueda por ID o título
  • Export: CSV y JSON (el PDF se puede implementar en una fase futura)
  • Click en una técnica dentro de un control navega al detalle de la técnica

Ruta: /compliance — añadir al sidebar.

Validación:

  • La página muestra frameworks con métricas correctas
  • El selector de framework cambia los datos
  • La tabla de controles se filtra correctamente
  • Expandir un control muestra sus técnicas con status
  • El CSV se descarga y es correcto
  • Los datos de compliance son consistentes con los scores
  • La ruta aparece en el sidebar

FASE 30 — Comparación Temporal y Re-testing

T-230: Snapshots de cobertura

Objetivo: Crear snapshots periódicos del estado de cobertura para comparar en el tiempo.

Archivo a crear: backend/app/models/coverage_snapshot.py

⚠️ Evitar JSONB gigante: Almacenar el estado de 3000+ técnicas como JSONB en una sola fila generaría registros de varios MB. Con snapshots semanales automáticos, crece muy rápido. En su lugar, normalizar el almacenamiento:

Campos de CoverageSnapshot:

Campo Tipo Restricciones
id UUID PK, default uuid4
name String nullable (ej: "Pre-remediación Q1")
organization_score Float not null
total_techniques Integer not null
validated_count Integer not null
partial_count Integer not null
not_covered_count Integer not null
in_progress_count Integer not null
not_evaluated_count Integer not null
created_by UUID FK → users.id, nullable
created_at DateTime default utcnow

Campos de SnapshotTechniqueState (tabla normalizada, una fila por técnica por snapshot):

Campo Tipo Restricciones
id UUID PK, default uuid4
snapshot_id UUID FK → coverage_snapshots.id, not null, ON DELETE CASCADE
technique_id UUID FK → techniques.id, not null
mitre_id String not null (desnormalizado para queries rápidos)
status String not null
score Float nullable

Índices:

Index('ix_snapshot_technique_states_snapshot', SnapshotTechniqueState.snapshot_id)
Index('ix_snapshot_technique_states_technique', SnapshotTechniqueState.technique_id)

Generar migración.

Servicio backend/app/services/snapshot_service.py:

def create_snapshot(db, name=None, user_id=None) -> CoverageSnapshot:
    """
    1. Obtener todas las técnicas con su status y score
    2. Crear CoverageSnapshot con conteos agregados
    3. Crear SnapshotTechniqueState para cada técnica
    4. Retornar el snapshot
    """

def compare_snapshots(db, snapshot_a_id, snapshot_b_id) -> dict:
    """
    Retorna:
    {
        "snapshot_a": {...},
        "snapshot_b": {...},
        "score_delta": +5.2,
        "improved": [{"mitre_id": "T1059", "old_status": "not_covered", "new_status": "validated", "old_score": 0, "new_score": 85}],
        "worsened": [...],
        "unchanged_count": 580,
        "summary": {"improved_count": 12, "worsened_count": 3, "new_count": 5}
    }
    """

def cleanup_old_snapshots(db, keep_last=52):
    """Mantener solo los últimos 52 snapshots (1 año de semanales). Eliminar los más antiguos."""

Job: Programar job APScheduler semanal (domingos a las 00:00) que ejecute create_snapshot(db, name="Auto-weekly"). Incluir cleanup_old_snapshots al final del job.

Endpoints:

Método Ruta Auth Descripción
GET /snapshots autenticado Listar snapshots (paginado)
POST /snapshots red_lead, blue_lead, admin Crear snapshot manual con nombre
GET /snapshots/{id} autenticado Detalle de un snapshot
GET /snapshots/compare autenticado Comparar dos: ?a={id}&b={id}
DELETE /snapshots/{id} admin Eliminar snapshot

Validación:

  • Crear snapshot captura el estado actual correctamente (conteos + detalle por técnica)
  • Comparar dos snapshots muestra técnicas que mejoraron, empeoraron y sin cambio
  • El job semanal crea snapshots automáticamente
  • cleanup_old_snapshots elimina snapshots antiguos respetando el mínimo
  • El snapshot normalizado no genera registros gigantes (verificar tamaño con datos de demo)

T-231: UI de comparación temporal

Archivo a crear: frontend/src/pages/ComparisonPage.tsx

Contenido:

Layout:

┌──────────────────────────────────────────────────────────────┐
│  Snapshot A: [Dropdown ▼]          Snapshot B: [Dropdown ▼]  │
├──────────────────────────┬───────────────────────────────────┤
│  Score: 58.0             │  Score: 63.2  (+5.2 ↑)          │
│  Validated: 120          │  Validated: 132 (+12)            │
│  Partial: 85             │  Partial: 88 (+3)                │
│  Not covered: 45         │  Not covered: 38 (-7)            │
├──────────────────────────┴───────────────────────────────────┤
│                                                              │
│  [Improved (12)] [Worsened (3)] [Unchanged (585)]           │
│                                                              │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ MITRE ID │ Name              │ Before  │ After   │ Δ │   │
│  │ T1059    │ Command Line      │ ●NotCov │ ●Valid  │ ↑ │   │
│  │ T1078    │ Valid Accounts    │ ●Partial│ ●Valid  │ ↑ │   │
│  │ T1053    │ Scheduled Tasks   │ ●Valid  │ ●Partial│ ↓ │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  [Mini Heatmap Diff] (opcional: verde=mejoró, rojo=empeoró)│
└──────────────────────────────────────────────────────────────┘

Interacciones:

  • Dos dropdowns para seleccionar snapshots (ordenados por fecha, más reciente primero)
  • Cards side-by-side con scores y conteos, mostrando deltas
  • Tabs para filtrar la tabla: Improved, Worsened, Unchanged
  • Tabla con técnicas que cambiaron, mostrando estado antes/después
  • Click en técnica navega al detalle
  • Mini heatmap diff opcional (si hay menos de 200 técnicas con cambios)

Ruta: /comparison — accesible desde dashboard ejecutivo y sidebar.

Validación:

  • Se pueden seleccionar dos snapshots
  • La comparación muestra las diferencias correctamente
  • Los deltas se calculan y muestran con flechas ↑↓
  • Los tabs Improved/Worsened/Unchanged filtran la tabla
  • Las métricas son correctas

T-232: Sistema de re-testing automático

Objetivo: Cuando un test se marca con remediación completada, crear automáticamente un re-test para verificar que la remediación fue efectiva.

Archivos a modificar:

  • backend/app/models/test.py — añadir campos de re-test
  • backend/app/services/test_workflow_service.py — lógica de re-test

Nuevos campos en Test:

Campo Tipo Restricciones
retest_of UUID FK → tests.id, nullable
retest_count Integer default 0

⚠️ Prevención de loop infinito de re-tests:

Añadir constante configurable:

# config.py
MAX_RETEST_COUNT: int = 3  # máximo de retests automáticos por test original

Lógica:

def handle_remediation_completed(db, test, user):
    """
    Se llama cuando remediation_status cambia a 'completed'.
    1. Verificar que retest_count < MAX_RETEST_COUNT
    2. Si no se alcanzó el límite:
       - Crear nuevo test con mismos datos base (technique, platform, procedure, tool)
       - Setear retest_of = test.id (o el test original si este ya es un retest)
       - Setear retest_count = test.retest_count + 1
       - Estado: draft
       - Notificar al creador del test original y al red_tech
    3. Si se alcanzó el límite:
       - No crear retest
       - Crear notificación: "Max retests reached for test X — manual review required"
       - Log de auditoría
    """
    original_test_id = test.retest_of or test.id
    if test.retest_count >= settings.MAX_RETEST_COUNT:
        create_notification(db, test.created_by, "max_retests_reached", ...)
        return None

    retest = Test(
        technique_id=test.technique_id,
        name=f"[Retest #{test.retest_count + 1}] {test.name}",
        description=test.description,
        platform=test.platform,
        procedure_text=test.procedure_text,
        tool_used=test.tool_used,
        state=TestState.draft,
        created_by=test.created_by,
        retest_of=original_test_id,
        retest_count=test.retest_count + 1,
    )
    db.add(retest)
    db.commit()
    # Notificar
    return retest

Endpoints nuevos:

Método Ruta Auth Descripción
GET /tests/{id}/retest-chain autenticado Ver cadena de retests (original + todos los retests)

UI: En el detalle del test, mostrar:

  • Si es un retest: link al test original
  • Si tiene retests: lista de retests con su estado
  • Indicador visual: "Retest 2/3" con barra de progreso

Generar migración.

Validación:

  • Completar remediación crea automáticamente un retest
  • El retest tiene retest_of apuntando al test original (no al intermedio)
  • El retest tiene los mismos datos base que el original
  • Se genera notificación del retest
  • Al alcanzar MAX_RETEST_COUNT, NO se crea retest y se notifica
  • La cadena de retests se puede consultar via endpoint
  • En la UI se muestra la cadena de retests con links

FASE 31 — Scheduling y Automatización

T-233: Sistema de scheduling de campañas

Objetivo: Permitir programar campañas para ejecución periódica.

Archivos a modificar:

  • backend/app/models/campaign.py — nuevos campos de scheduling
  • backend/app/services/campaign_scheduler_service.py — nuevo

Nuevos campos en Campaign:

Campo Tipo Restricciones
is_recurring Boolean default False
recurrence_pattern String nullable (weekly, monthly, quarterly)
next_run_at DateTime nullable
last_run_at DateTime nullable

Generar migración.

Servicio backend/app/services/campaign_scheduler_service.py:

def check_and_run_recurring_campaigns(db):
    """
    Job diario. Para cada campaña donde is_recurring=True y next_run_at <= now:
    1. Clonar la campaña:
       - Crear nueva campaña con mismos datos base + suffix " (Run {date})"
       - Para cada CampaignTest de la campaña original:
         - Crear nuevo Test con mismos datos base, state=draft
         - Crear nuevo CampaignTest vinculando al nuevo test
    2. Activar la nueva campaña (status=active)
    3. Actualizar la campaña original: last_run_at=now, next_run_at=calcular_siguiente(recurrence_pattern)
    4. Notificar a los equipos
    5. Log de auditoría
    """

def calculate_next_run(current_date, pattern):
    """
    weekly: +7 días
    monthly: +30 días
    quarterly: +90 días
    """

Job: Programar job APScheduler diario que ejecute check_and_run_recurring_campaigns.

Endpoints:

Método Ruta Auth Descripción
PATCH /campaigns/{id}/schedule creador, admin Configurar recurrencia
GET /campaigns/{id}/history autenticado Historial de ejecuciones (campañas hijas)

Formato de PATCH /campaigns/{id}/schedule:

{
  "is_recurring": true,
  "recurrence_pattern": "monthly",
  "next_run_at": "2026-03-01T00:00:00Z"
}

Validación:

  • Se puede configurar una campaña como recurrente con patrón y fecha
  • El job diario detecta campañas que deben ejecutarse
  • La clonación crea nueva campaña con tests nuevos en draft
  • Los tests clonados tienen los mismos datos base pero IDs nuevos
  • last_run_at y next_run_at se actualizan correctamente
  • El historial lista todas las ejecuciones pasadas (campañas hijas)
  • Las notificaciones se generan al crear nueva ejecución

T-234: UI de scheduling

Archivo a modificar: frontend/src/pages/CampaignDetailPage.tsx

Nuevas funcionalidades:

  • Toggle "Recurring Campaign": switch que activa/desactiva recurrencia
  • Al activar, aparece:
    • Selector de frecuencia: Weekly / Monthly / Quarterly
    • Date picker para "Next run at"
  • Indicador de próxima ejecución: badge en el header "Next run: March 1, 2026"
  • Tab "Execution History": tabla con ejecuciones pasadas
    • Columnas: fecha, nombre, nº tests, progreso %, score obtenido, link
    • Click en una ejecución navega al detalle de esa campaña

Validación:

  • El toggle de recurrencia funciona (guarda via PATCH)
  • Se puede seleccionar la frecuencia
  • El date picker funciona para next_run_at
  • La próxima ejecución se muestra en el header
  • El historial lista ejecuciones pasadas con datos correctos
  • Desactivar recurrencia limpia next_run_at

FASE 32 — Tests Automatizados V3

T-235: Tests de importación de fuentes

Archivo a crear: backend/tests/test_data_sources.py

⚠️ Nota sobre tests de importación externa: Los tests que dependen de descargar repositorios de GitHub son lentos e inestables (dependen de red). Implementar dos niveles:

  1. Tests unitarios (rápidos, sin red): mockear las descargas, testear solo la lógica de parsing
  2. Tests de integración (lentos, con red): marcados con @pytest.mark.integration, excluidos por defecto
import pytest

class TestDataSourcesParsing:
    """Tests unitarios — sin acceso a red, usando fixtures de YAML/TOML de ejemplo"""

    def test_sigma_yaml_parsing():
        """Parsear un YAML de Sigma de ejemplo y verificar extracción de campos"""

    def test_lolbas_yaml_parsing():
        """Parsear un YAML de LOLBAS y verificar extracción de MitreID y commands"""

    def test_caldera_yaml_parsing():
        """Parsear un YAML de CALDERA ability y verificar campos"""

    def test_elastic_toml_parsing():
        """Parsear un TOML de Elastic y verificar extracción de KQL y threat mappings"""

    def test_stix_threat_actor_parsing():
        """Parsear un bundle STIX de ejemplo y verificar extracción de intrusion-sets y relationships"""

    def test_d3fend_api_response_parsing():
        """Parsear una respuesta mock de la API D3FEND"""

    def test_no_duplicates_on_reimport():
        """Verificar que la lógica de deduplicación funciona con datos mock"""


@pytest.mark.integration
class TestDataSourcesIntegration:
    """Tests de integración — requieren acceso a red. Ejecutar con: pytest -m integration"""

    def test_sigma_full_import():
        """Importar desde GitHub real y verificar volumen"""

    def test_lolbas_full_import():
        """Importar LOLBAS completo"""

    def test_caldera_full_import():
        """Importar CALDERA completo"""

    def test_elastic_full_import():
        """Importar Elastic rules completo"""

Crear fixtures: backend/tests/fixtures/ con archivos de ejemplo:

  • sample_sigma_rule.yml
  • sample_lolbas_entry.yml
  • sample_caldera_ability.yml
  • sample_elastic_rule.toml
  • sample_stix_bundle.json (con 2-3 intrusion-sets y relationships)

Validación:

  • pytest tests/test_data_sources.py (sin flag integration) pasa rápido (<10s)
  • pytest tests/test_data_sources.py -m integration pasa (puede tardar minutos)
  • Cada parsing se verifica independientemente con fixtures locales

T-236: Tests de scoring, métricas y compliance

Archivo a crear: backend/tests/test_scoring_and_compliance.py

Tests:

class TestScoring:
    def test_technique_score_all_detected():
        """Técnica con todos los tests detected → score alto"""

    def test_technique_score_no_tests():
        """Técnica sin tests → score 0"""

    def test_technique_score_partial_detection():
        """Técnica con detección parcial → score intermedio"""

    def test_technique_score_freshness_penalty():
        """Tests > 180 días → penalización en freshness"""

    def test_scoring_weights_configurable():
        """Cambiar pesos cambia el score resultante"""

    def test_threat_actor_coverage_score():
        """Score de cobertura contra un actor con datos conocidos"""

    def test_organization_score_aggregation():
        """Score global agrega correctamente los scores de técnicas"""


class TestOperationalMetrics:
    def test_mttd_calculation():
        """MTTD se calcula desde timestamps del audit_log"""

    def test_mttr_calculation():
        """MTTR incluye tiempo de remediación"""

    def test_detection_efficacy():
        """Detection efficacy con datos de prueba conocidos"""

    def test_metrics_with_no_data():
        """Métricas retornan null cuando no hay datos suficientes"""


class TestCompliance:
    def test_control_fully_covered():
        """Control con todas las técnicas validated → covered"""

    def test_control_partially_covered():
        """Control con técnicas mixtas → partially_covered"""

    def test_control_not_covered():
        """Control con todas las técnicas sin tests → not_covered"""

    def test_compliance_percentage():
        """Porcentaje global de compliance calculado correctamente"""

    def test_compliance_gaps():
        """Gaps retorna solo controles no cubiertos con sus técnicas"""

Validación:

  • pytest tests/test_scoring_and_compliance.py pasa todos los tests
  • Los cálculos son correctos con datos conocidos
  • Los edge cases (sin datos, datos parciales) se manejan sin errores

T-237: Tests de campañas, snapshots y re-testing

Archivo a crear: backend/tests/test_campaigns_and_snapshots.py

Tests:

class TestCampaigns:
    def test_create_campaign_with_tests():
        """CRUD básico de campaña con tests ordenados"""

    def test_campaign_progress_calculation():
        """Progreso se calcula según estado de tests"""

    def test_generate_from_threat_actor():
        """Generación automática de campaña desde actor cubre los gaps"""

    def test_circular_dependency_prevention():
        """Intentar crear dependencia circular en campaign_tests falla"""

    def test_campaign_cloning():
        """Clonación de campaña recurrente crea tests nuevos con datos correctos"""

    def test_campaign_scheduling_next_run():
        """next_run_at se calcula correctamente para weekly/monthly/quarterly"""


class TestSnapshots:
    def test_create_snapshot():
        """Snapshot captura estado actual correctamente"""

    def test_compare_snapshots_improvements():
        """Comparación detecta técnicas que mejoraron"""

    def test_compare_snapshots_regressions():
        """Comparación detecta técnicas que empeoraron"""

    def test_snapshot_cleanup():
        """Cleanup mantiene solo los últimos N snapshots"""

    def test_snapshot_normalized_storage():
        """Verificar que el almacenamiento normalizado funciona correctamente"""


class TestRetesting:
    def test_retest_created_on_remediation():
        """Completar remediación crea retest automáticamente"""

    def test_retest_points_to_original():
        """Retest de un retest apunta al test original, no al intermedio"""

    def test_retest_max_limit():
        """Al alcanzar MAX_RETEST_COUNT no se crea retest"""

    def test_retest_chain_query():
        """Endpoint /tests/{id}/retest-chain retorna cadena completa"""

    def test_retest_has_correct_data():
        """Retest tiene mismos datos base que el original"""

Validación:

  • pytest tests/test_campaigns_and_snapshots.py pasa todos los tests
  • El flujo completo de campañas funciona
  • Los snapshots y comparaciones son correctos
  • El re-testing respeta el límite máximo

FASE 33 — Pulido Final V3

T-238: Actualizar navegación completa

Objetivo: Integrar todas las nuevas páginas en la navegación.

Archivos a modificar:

  • frontend/src/App.tsx
  • frontend/src/components/Sidebar.tsx

Sidebar actualizado:

📊 Dashboard
📊 Executive Dashboard          (leads + admin)
🔲 ATT&CK Matrix                (heatmap avanzado)
🧪 Tests
  ├─ All Tests
  ├─ My Pending Tasks
  └─ Test Catalog
📋 Campaigns
👤 Threat Actors
📜 Compliance
📈 Comparison                    (leads + admin)
📄 Reports
⚙️ System                       (admin)
  ├─ Data Sources
  ├─ MITRE Sync
  ├─ Users
  └─ Audit Log

Nuevas rutas:

/executive-dashboard   → ExecutiveDashboardPage
/campaigns             → CampaignsPage
/campaigns/:id         → CampaignDetailPage
/threat-actors         → ThreatActorsPage
/threat-actors/:id     → ThreatActorDetailPage
/compliance            → CompliancePage
/comparison            → ComparisonPage
/system/data-sources   → DataSourcesPage

Visibilidad por rol:

Ruta admin red_lead blue_lead red_tech blue_tech
Dashboard
Executive Dashboard
ATT&CK Matrix
Tests (all)
Test Catalog
Campaigns
Threat Actors
Compliance
Comparison
Reports
System / Data Sources

Validación:

  • Todas las rutas nuevas funcionan y cargan la página correcta
  • El sidebar muestra items según el rol del usuario logueado
  • La navegación es consistente (breadcrumbs o back links donde aplique)
  • No hay rutas rotas o 404
  • Un usuario red_tech no ve Executive Dashboard ni Comparison en el sidebar
  • Un admin ve todo

T-239: Optimización de rendimiento

Objetivo: Asegurar que la plataforma rinde bien con volúmenes grandes de datos (3000+ técnicas, 5000+ templates, 10000+ detection rules).

Optimizaciones backend:

  1. Índices adicionales — Crear migración con índices que falten tras analizar queries lentas:
   # Verificar con EXPLAIN ANALYZE las queries más frecuentes
   # Candidatos probables:
   Index('ix_detection_rules_technique_source', DetectionRule.mitre_technique_id, DetectionRule.source)
   Index('ix_snapshot_technique_states_snapshot_technique', SnapshotTechniqueState.snapshot_id, SnapshotTechniqueState.technique_id)
   Index('ix_campaign_tests_campaign', CampaignTest.campaign_id)
  1. Paginación cursor-based en listados grandes:

    • Test templates (5000+)
    • Detection rules (10000+)
    • Audit logs
    • Reemplazar offset-based por cursor-based donde el volumen supere 1000 registros habituales
  2. Caché de métricas y scores:

    • Los scores de organización y métricas operativas son costosos de calcular
    • Implementar caché in-memory simple con TTL (diccionario con timestamp):
   _score_cache = {}
   CACHE_TTL = 300  # 5 minutos

   def get_organization_score_cached(db):
       now = time.time()
       if "org_score" in _score_cache and now - _score_cache["org_score"]["ts"] < CACHE_TTL:
           return _score_cache["org_score"]["data"]
       result = calculate_organization_score(db)
       _score_cache["org_score"] = {"data": result, "ts": now}
       return result
  • Invalidar caché cuando se valida un test o cambia un score
  1. Queries optimizadas:
    • Usar selectinload para relaciones que siempre se necesitan (test.evidences, technique.tests)
    • Usar subqueryload para relaciones grandes
    • Evitar N+1 queries en endpoints de listado

Optimizaciones frontend:

  1. Virtualización de tablas grandes:

    • Añadir @tanstack/react-virtual (ya añadido en T-222 para el heatmap)
    • Aplicar también a: tabla de test templates, tabla de detection rules, tabla de audit logs
    • Umbral: virtualizar tablas que puedan superar 100 filas
  2. Lazy loading de páginas:

   // App.tsx
   const ExecutiveDashboard = React.lazy(() => import('./pages/ExecutiveDashboardPage'));
   const CompliancePage = React.lazy(() => import('./pages/CompliancePage'));
   // etc. para todas las páginas de V3
  • Envolver en <Suspense fallback={<LoadingSpinner />}>
  1. Memoización:

    • React.memo para HeatmapCell (se renderiza 3000+ veces)
    • useMemo para cálculos de colores y filtros en el heatmap
    • useCallback para handlers en componentes que se renderizan muchas veces
  2. Debounce en buscadores:

    • Todos los campos de búsqueda con debounce de 300ms
    • Usar hook custom useDebounce(value, delay)

Validación:

  • El heatmap con 3000+ técnicas renderiza sin lag perceptible (<1s para render inicial)
  • Las tablas con 5000+ filas scrollean suavemente (60fps)
  • Los endpoints de listado responden en < 500ms con volúmenes grandes
  • El dashboard ejecutivo carga en < 3 segundos
  • EXPLAIN ANALYZE de las queries principales muestra uso de índices
  • El caché de scores funciona (segunda petición es instantánea)
  • Lazy loading funciona (verificar en Network tab que los chunks se cargan bajo demanda)

T-240: Documentación completa V3

Archivos a crear/modificar:

  • README.md — actualizar con todas las funcionalidades V3
  • docs/API.md — documentar todos los endpoints nuevos
  • docs/ARCHITECTURE.md — nuevo
  • docs/DATA_SOURCES.md — nuevo
  • docs/SCORING.md — nuevo

README actualizado:

Secciones a añadir:

  • Descripción completa de todas las funcionalidades V3
  • Diagrama de arquitectura simplificado (texto ASCII)
  • Guía de inicio rápido actualizada
  • Cómo importar datos de todas las fuentes (Data Sources)
  • Cómo configurar campañas y threat actors
  • Cómo generar reportes de compliance
  • Cómo usar el heatmap avanzado
  • Cómo configurar pesos de scoring
  • Variables de entorno nuevas

docs/ARCHITECTURE.md:

  • Diagrama de la base de datos completa (todas las tablas y relaciones)
  • Diagrama de flujo de datos entre servicios
  • Descripción de cada servicio y su responsabilidad
  • Diagrama del pipeline de tests (draft → validated)
  • Diagrama de jobs programados (APScheduler)

docs/DATA_SOURCES.md:

Para cada fuente (Atomic Red Team, Sigma, LOLBAS, GTFOBins, CALDERA, Elastic, D3FEND, MITRE CTI):

  • Descripción de la fuente
  • URL del repositorio
  • Formato de datos (YAML, TOML, STIX, etc.)
  • Cómo se parsea y qué campos se extraen
  • Frecuencia de actualización recomendada
  • Volumen aproximado de datos
  • Troubleshooting: qué hacer si la importación falla (rate limits, formato cambiado, etc.)

docs/SCORING.md:

  • Explicación del sistema de scoring
  • Descripción de cada componente del score con su peso
  • Cómo modificar los pesos
  • Ejemplos de cálculo
  • Cómo se agregan scores por táctica, actor y organización
  • Explicación de cada métrica operativa (MTTD, MTTR, etc.)

Validación:

  • Un nuevo desarrollador puede entender la arquitectura leyendo ARCHITECTURE.md
  • DATA_SOURCES.md cubre todas las fuentes con instrucciones claras
  • SCORING.md explica el sistema de scoring con ejemplos
  • El README refleja todas las funcionalidades V3
  • Swagger UI en /docs muestra todos los endpoints
  • Siguiendo el README desde cero se puede levantar todo el proyecto con datos importados

Resumen de Fases V3

Fase Tareas Descripción
21 T-200 a T-202 Seed de demo, modelo de fuentes y detection rules
22 T-203 a T-207 Importación de fuentes de tests y panel de gestión
23 T-208 a T-212 Perfiles de amenaza (Threat Actor Profiles)
24 T-213 a T-214 MITRE D3FEND: contramedidas defensivas
25 T-215 a T-216 Reglas de detección sugeridas por test
26 T-217 a T-220 Campañas de tests (attack chains / kill chain)
27 T-221 a T-223 Heatmap ATT&CK avanzado (estilo Navigator)
28 T-224 a T-226 Scoring y métricas avanzadas (MTTD, MTTR, etc.)
29 T-227 a T-229 Compliance y reportes (NIST, DORA, NIS2, ISO 27001)
30 T-230 a T-232 Comparación temporal y re-testing automático
31 T-233 a T-234 Scheduling y automatización de campañas
32 T-235 a T-237 Tests automatizados V3
33 T-238 a T-240 Pulido final y documentación V3

Total V3: 41 tareas = 41 commits mínimo Cada tarea es autocontenida y verificable antes de hacer commit.


Resumen General del Proyecto Aegis

Bloque Tareas Commits Descripción
MVP T-001036 36 Plataforma base funcional
V2 T-100135 36 Flujo Red/Blue, templates, notificaciones
V3 T-200240 41 Enterprise: fuentes, scoring, compliance
Total 113 tareas 113 commits Plataforma completa

Análisis Competitivo: Qué Copiamos de Cada Plataforma

De Validato

  • Step-by-step remediation (V2: T-130)
  • Mapeo a MITRE D3FEND (V3: T-213)
  • Re-testing post-remediación (V3: T-232)
  • Validación continua con campañas recurrentes (V3: T-233)
  • Resultados mapeados a frameworks de compliance (V3: T-227)

De Cymulate

  • Full kill chain campaigns (V3: T-217)
  • Reglas de detección sugeridas por test (V3: T-215)
  • Múltiples fuentes de tests — 100,000+ scenarios (V3: T-201206)
  • Daily threat updates con sync automático (V3: T-207)
  • Detection heatmap avanzado (V3: T-221)
  • Generación de campañas desde threat actors (V3: T-218)

De Picus Security

  • Detection Rule Validation (V3: T-216)
  • Catálogo masivo de tests de múltiples fuentes (V3: T-201206)
  • Filtros de threat actors por sector/región (V3: T-210)
  • Emulación de grupos APT específicos (V3: T-208)
  • Constructor de campañas desde APT profiles (V3: T-218)
  • Multi-framework compliance: NIST, DORA, ISO (V3: T-227)

De AttackIQ

  • Templates pre-construidos por escenario (V2: T-103)
  • MITRE ATT&CK Navigator integration con export (V3: T-221)
  • Campañas recurrentes programadas (V3: T-233)
  • Purple team collaboration Red/Blue (V2: core feature)
  • Detection pipeline validation (V3: T-216)
  • Contextual risk prioritization con scoring (V3: T-224)

Fuentes Open-Source Únicas de Aegis

  • Atomic Red Team: 1,500+ tests atómicos (V2: T-108)
  • SigmaHQ: 3,000+ reglas de detección (V3: T-203)
  • LOLBAS + GTFOBins: 750+ técnicas living-off-the-land (V3: T-204)
  • MITRE CALDERA: 400+ abilities ejecutables (V3: T-205)
  • Elastic Detection Rules: 1,000+ reglas KQL (V3: T-206)
  • MITRE D3FEND: 200+ contramedidas defensivas (V3: T-213)
  • MITRE CTI: 140+ threat actor profiles (V3: T-209)

Lo que Aegis NO tiene (y las plataformas enterprise sí)

Estas funcionalidades requieren infraestructura de agentes/endpoints y quedan fuera del scope de Aegis como plataforma de gestión:

  1. Ejecución automática de ataques en endpoints (requiere agentes instalados)
  2. Integración directa con SIEMs (Splunk, Elastic, QRadar) para verificar alertas en tiempo real
  3. Integración con EDR (CrowdStrike, SentinelOne) para verificar detección automática
  4. Sandbox de ejecución segura (entorno aislado para ejecutar payloads)
  5. API de ejecución remota (ejecutar tests automáticamente en hosts)

Aegis se posiciona como plataforma de gestión y tracking del proceso de validación, no como herramienta de ejecución automática. Los equipos ejecutan manualmente (o con sus propias herramientas) y documentan resultados en Aegis.