From fb7f340038429f42fe4b87a2b957c19ec31dbd7f Mon Sep 17 00:00:00 2001 From: Kitos Date: Mon, 9 Feb 2026 13:52:04 +0100 Subject: [PATCH] feat(phase-18): add in-app notification system (T-128, T-129) --- AegisTestPlan_v2.md | 1431 ++++++ AegisTestPlan_v3.md | 1475 ++++++ aegiscompleteplan.md | 3989 +++++++++++++++++ .../versions/b006_add_notifications_table.py | 46 + backend/app/jobs/mitre_sync_job.py | 24 +- backend/app/main.py | 2 + backend/app/models/__init__.py | 3 +- backend/app/models/notification.py | 39 + backend/app/routers/notifications.py | 103 + backend/app/schemas/notification.py | 28 + backend/app/services/notification_service.py | 179 + backend/app/services/test_workflow_service.py | 15 + frontend/src/api/notifications.ts | 51 + frontend/src/components/Layout.tsx | 2 + frontend/src/components/NotificationBell.tsx | 53 + .../src/components/NotificationDropdown.tsx | 139 + 16 files changed, 7577 insertions(+), 2 deletions(-) create mode 100644 AegisTestPlan_v2.md create mode 100644 AegisTestPlan_v3.md create mode 100644 aegiscompleteplan.md create mode 100644 backend/alembic/versions/b006_add_notifications_table.py create mode 100644 backend/app/models/notification.py create mode 100644 backend/app/routers/notifications.py create mode 100644 backend/app/schemas/notification.py create mode 100644 backend/app/services/notification_service.py create mode 100644 frontend/src/api/notifications.ts create mode 100644 frontend/src/components/NotificationBell.tsx create mode 100644 frontend/src/components/NotificationDropdown.tsx diff --git a/AegisTestPlan_v2.md b/AegisTestPlan_v2.md new file mode 100644 index 0000000..f6752d4 --- /dev/null +++ b/AegisTestPlan_v2.md @@ -0,0 +1,1431 @@ +# Aegis v2 — Plan de Tareas: Sistema de Tests de Validación Red Team / Blue Team + +> **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 extiende el MVP de Aegis (36 tareas completadas) para implementar +> un sistema completo de validación de tests de seguridad inspirado en [Validato](https://validato.io/). +> La idea central: cada TTP de MITRE ATT&CK tiene tests que son ejecutados por el Red Team +> y validados/detectados por el Blue Team. Cada test tiene pestañas separadas para evidencias +> de ataque (Red Team) y detección (Blue Team), con un flujo de validación por managers de +> ambos equipos que actualiza progresivamente el estado del test y de la TTP. + +--- + +## 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 + +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`:** + +```python +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. + +**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 +- [ ] `alembic downgrade -1` revierte sin errores + +--- + +### 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 campo `team` + +**Nuevo enum:** + +```python +class TeamSide(str, enum.Enum): + red = "red" + blue = "blue" +``` + +**Nuevo campo en Evidence:** + +| Campo | Tipo | Restricciones | +|-----------|-------------------|----------------------------------| +| team | Enum(TeamSide) | not null, default "red" | +| notes | Text | nullable (notas sobre la evidencia) | + +**Generar migración.** El default `red` asegura que las evidencias existentes se asignen correctamente. + +**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` + +**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:** + +```python +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 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 + +--- + +### 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 | + +**Actualizar** `models/__init__.py` para importar TestTemplate. + +**Generar migración.** + +**Validación:** + +- [ ] `alembic upgrade head` crea la tabla `test_templates` +- [ ] 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 + +--- + +### 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.) +- `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 +- [ ] `TestTemplateCreate` valida correctamente los campos requeridos +- [ ] `EvidenceOut` incluye `team` y `notes` + +--- + +## FASE 11 — Lógica de Negocio del Flujo Red/Blue + +### T-105: 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:** + +```python +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 +- `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 + +**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ó +- [ ] Cada transición genera un log de auditoría + +--- + +### T-106: 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:** + +```python +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-107: 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` + +**Lógica:** + +1. Descargar/parsear el índice de Atomic Red Team desde GitHub (`https://github.com/redcanaryco/atomic-red-team`) +2. El repositorio contiene ficheros YAML organizados por técnica MITRE (`atomics/T1059.001/T1059.001.yaml`) +3. Para cada test atómico: + - Extraer `name`, `description`, `supported_platforms`, `executor` (tipo y command) + - Mapear a la técnica MITRE correspondiente + - Crear un `TestTemplate` con `source = "atomic_red_team"` +4. No duplicar templates que ya existen (comparar por `atomic_test_id`) +5. Log de auditoría con resumen + +**Nota:** En el MVP, se puede hacer una importación simplificada usando la API de GitHub para obtener los YAML directamente, o descargar un JSON resumen pre-generado. + +**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 50+ templates + +--- + +## FASE 12 — Endpoints API Red/Blue + +### T-108: 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 (procedure, tool, etc.) | +| PATCH | /tests/{id}/blue | blue_tech, admin | Blue Team actualiza su parte (detection, summary) | +| 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 | + +**Detalle del endpoint GET /tests/{id}:** + +La respuesta debe incluir: +```json +{ + "id": "...", + "name": "...", + "state": "blue_evaluating", + "red_evidences": [...], // evidencias con team=red + "blue_evidences": [...], // evidencias con team=blue + "red_summary": "...", + "blue_summary": "...", + "attack_success": true, + "detection_result": "detected", + "red_validation_status": "approved", + "blue_validation_status": "pending", + "timeline": [...] // historial de cambios +} +``` + +**Validación:** + +- [ ] `POST /tests` crea un test en estado `draft` +- [ ] `POST /tests/from-template` crea un test con datos pre-rellenados del template +- [ ] `PATCH /tests/{id}/red` solo funciona si el test está en `red_executing` +- [ ] `PATCH /tests/{id}/blue` solo funciona si el test está 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 +- [ ] `POST /tests/{id}/validate-blue` solo accesible por blue_lead +- [ ] 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-109: 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:** + +- Red Team solo puede subir evidencias `team=red` cuando el test está en `red_executing` +- Blue Team solo puede subir evidencias `team=blue` cuando el test está en `blue_evaluating` +- Admin puede subir en cualquier momento + +**Validación:** + +- [ ] Un `red_tech` puede subir evidencia con `team=red` en estado `red_executing` +- [ ] Un `red_tech` NO puede subir evidencia con `team=blue` +- [ ] Un `blue_tech` puede subir evidencia con `team=blue` en estado `blue_evaluating` +- [ ] Un `blue_tech` NO puede subir evidencia con `team=red` +- [ ] `GET /tests/{id}/evidence?team=red` filtra correctamente +- [ ] `DELETE /evidence/{id}` solo permite borrar en estados editables +- [ ] Admin puede subir cualquier tipo de evidencia en cualquier momento + +--- + +### T-110: 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 + +**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-111: 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-112: 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:** + +```typescript +// Actualizar TestState +export type TestState = + | "draft" + | "red_executing" + | "blue_evaluating" + | "in_review" + | "validated" + | "rejected"; + +// Nuevo tipo TeamSide +export type TeamSide = "red" | "blue"; + +// 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 { + // ... campos existentes ... + 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; +} +``` + +**Validación:** + +- [ ] TypeScript compila sin errores +- [ ] Todos los tipos nuevos están exportados +- [ ] Los tipos coinciden con los schemas del backend + +--- + +### T-113: 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:** + +```typescript +export const createTestFromTemplate = (templateId: string, techniqueId: string) => ... +export const updateTestRed = (testId: string, data: RedUpdateData) => ... +export const updateTestBlue = (testId: string, data: BlueUpdateData) => ... +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:** + +```typescript +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:** + +```typescript +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-114: 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 `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 +- [ ] Los indicadores de validación dual se actualizan correctamente + +--- + +### T-115: 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 `red_executing`) +- Herramienta utilizada +- Indicador de éxito del ataque (switch: sí/no) +- Resumen del Red Team (textarea) +- Lista de evidencias Red con upload (solo en `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-116: 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-117: 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-118: 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-119: 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-120: 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-121: 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-122: 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-123: 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-124: 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:** + +```python +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""" +``` + +**Validación:** + +- [ ] `pytest tests/test_workflow.py` ejecuta todos los tests +- [ ] Todos los tests pasan (verde) +- [ ] Cobertura del flujo completo + +--- + +### T-125: Tests de TestTemplates + +**Objetivo:** Tests automatizados para el CRUD de templates y la instanciación. + +**Archivo a crear:** `backend/tests/test_templates.py` + +**Tests:** + +```python +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-126: 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:** + +```python +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-127: 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 | + +**Generar migración.** + +**Servicio** `backend/app/services/notification_service.py`: + +```python +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 +``` + +**Disparar notificaciones automáticamente:** +- 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 + +**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 + +--- + +### T-128: 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| +| 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 | + +**Frontend:** + +- `NotificationBell`: icono de campana en el header con badge de conteo +- `NotificationDropdown`: dropdown con lista de notificaciones +- Click en notificación navega a la entidad correspondiente y marca como leída +- Polling cada 30 segundos para actualizar conteo (o usar react-query con refetchInterval) + +**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 + +--- + +## FASE 19 — Mejoras de Remediación y Reportes (inspirado en Validato) + +### T-129: 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-130: 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 y Documentación + +### T-131: 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-132: Error handling y edge cases + +**Objetivo:** Asegurar que todos los nuevos flujos manejan errores correctamente. + +**Verificaciones:** + +**Backend:** +- Todos los endpoints nuevos tienen manejo de 404, 400, 403 +- Las transiciones de estado inválidas retornan errores descriptivos +- 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 +- [ ] 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-133: Backend tests finales de integración + +**Objetivo:** Suite final de tests que verifica el sistema completo end-to-end. + +**Archivo a crear:** `backend/tests/test_integration.py` + +**Tests:** + +```python +class TestIntegration: + def test_full_e2e_flow(): + """ + 1. Admin importa Atomic Red Team templates + 2. Red Tech crea test desde template + 3. Red Tech sube evidencias y submite + 4. Blue Tech evalúa y sube evidencias + 5. Blue Tech submite para review + 6. Red Lead y Blue Lead validan + 7. 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.py` pasa todos los tests +- [ ] El flujo E2E completo funciona sin errores +- [ ] Las métricas son consistentes tras todas las operaciones + +--- + +### T-134: Actualizar documentación + +**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 +- Cómo importar tests de Atomic Red Team +- Cómo usar el catálogo de templates +- Explicación del ciclo de vida de un test +- Diagrama 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 + +| Fase | Tareas | Descripción | +|------|------------------|-------------------------------------------------------| +| 10 | T-100 a T-104 | Evolución del modelo de datos para Red/Blue Team | +| 11 | T-105 a T-107 | Lógica de negocio del flujo Red/Blue | +| 12 | T-108 a T-111 | Endpoints API Red/Blue | +| 13 | T-112 a T-113 | Frontend: tipos y API clients | +| 14 | T-114 a T-117 | Frontend: página de test con pestañas Red/Blue | +| 15 | T-118 a T-120 | Frontend: catálogo de tests y templates | +| 16 | T-121 a T-123 | Frontend: vistas de gestión y dashboard mejorado | +| 17 | T-124 a T-126 | Backend tests automatizados | +| 18 | T-127 a T-128 | Notificaciones in-app | +| 19 | T-129 a T-130 | Remediación y reportes | +| 20 | T-131 a T-134 | Pulido final y documentación | + +> **Total: 35 tareas = 35 commits mínimo** +> Cada tarea es autocontenida y verificable antes de hacer commit. + +--- + +## Inspiración de Validato + +Las siguientes ideas están inspiradas en la plataforma [Validato](https://validato.io/) y adaptadas al contexto de Aegis: + +1. **Tests mapeados a MITRE ATT&CK**: Cada test está directamente vinculado a una técnica, igual que Validato mapea simulaciones a TTPs. +2. **Catálogo de tests predefinidos**: Similar a cómo Validato ofrece escenarios de validación pre-configurados, Aegis usa Atomic Red Team como fuente de tests base. +3. **Validación de Protection + Detection**: Validato evalúa si los controles protegen Y detectan. En Aegis, Red Team valida la ejecución del ataque y Blue Team valida la detección. +4. **Resultados mapeados a frameworks**: Los resultados se mapean de vuelta a MITRE ATT&CK para actualizar la cobertura. +5. **Remediación paso a paso**: Inspirado en cómo Validato proporciona remediation steps, cada test puede incluir pasos de remediación sugeridos. +6. **Validación continua**: El pipeline draft → red → blue → review → validated permite re-ejecutar tests continuamente para medir mejoras. +7. **Reportes de cobertura**: Exportación de reportes para demostrar compliance (similar a los reportes de Validato para DORA, NIS2, GLBA). +8. **Priorización por severidad**: Los templates incluyen severidad para priorizar qué tests ejecutar primero. diff --git a/AegisTestPlan_v3.md b/AegisTestPlan_v3.md new file mode 100644 index 0000000..1688084 --- /dev/null +++ b/AegisTestPlan_v3.md @@ -0,0 +1,1475 @@ +# Aegis v3 — Plan de Tareas: Plataforma Avanzada de Validación de Seguridad + +> **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 plan v2 (T-100 a T-134). +> El v2 establece el flujo base Red Team / Blue Team con pestañas, validación dual, +> templates de Atomic Red Team y notificaciones. El v3 eleva Aegis a una plataforma +> de validación de seguridad de nivel enterprise, incorporando funcionalidades inspiradas +> en [Validato](https://validato.io/), [Cymulate](https://cymulate.com/), +> [Picus Security](https://www.picussecurity.com/), [AttackIQ](https://www.attackiq.com/), +> y fuentes de datos open-source como Sigma Rules, MITRE D3FEND, LOLBAS, GTFOBins, +> MITRE CALDERA y la Adversary Emulation Library. + +--- + +## 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 (Investigación) + +Tras investigar extensamente, estas son **todas las fuentes open-source** de las que Aegis puede obtener tests mapeados a MITRE ATT&CK: + +### 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](https://github.com/redcanaryco/atomic-red-team) | +| **MITRE CALDERA** | Abilities (acciones) ejecutables por agente | YAML | 400+ | [GitHub](https://github.com/mitre/caldera) | +| **Adversary Emulation Library** | Planes completos de emulación de APTs (APT29, FIN6, etc.) | YAML/JSON/PDF | 15+ planes | [GitHub](https://github.com/center-for-threat-informed-defense/adversary_emulation_library) | +| **LOLBAS** | Binarios legítimos de Windows que pueden ser abusados | YAML/JSON | 400+ | [GitHub](https://github.com/LOLBAS-Project/LOLBAS) | +| **GTFOBins** | Binarios legítimos de Unix/Linux que pueden ser abusados | Markdown/JSON | 350+ | [GitHub](https://gtfobins.github.io/) | +| **MITRE ATT&CK Procedures** | Procedimientos documentados en la propia framework | STIX/JSON | 1,000+ | [TAXII Server](https://cti-taxii.mitre.org/) | + +### Fuentes para Blue Team (Reglas de Detección) + +| Fuente | Descripción | Formato | Reglas aprox. | URL | +|--------|-------------|---------|---------------|-----| +| **SigmaHQ** | Reglas de detección genéricas para SIEM, mapeadas a ATT&CK | YAML | 3,000+ | [GitHub](https://github.com/SigmaHQ/sigma) | +| **Elastic Detection Rules** | Reglas de detección de Elastic SIEM (KQL) | TOML | 1,000+ | [GitHub](https://github.com/elastic/detection-rules) | +| **MITRE D3FEND** | Framework de contramedidas defensivas mapeado a ATT&CK | OWL/JSON | 200+ técnicas | [d3fend.mitre.org](https://d3fend.mitre.org/) | +| **Splunk Security Content** | Reglas de detección para Splunk | YAML | 1,500+ | [GitHub](https://github.com/splunk/security_content) | + +### 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](https://github.com/mitre/cti) | +| **MISP** | Plataforma de compartición de threat intelligence | JSON | [misp-project.org](https://www.misp-project.org/) | +| **MITRE ATT&CK Groups** | Perfiles de 140+ grupos APT con sus TTPs | STIX | [attack.mitre.org/groups](https://attack.mitre.org/groups/) | + +--- + +## FASE 21 — Fuentes de Tests Múltiples: Importación y Unificación + +### T-200: 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. + +**Validación:** + +- [ ] `alembic upgrade head` crea la tabla `data_sources` +- [ ] El seed crea las fuentes iniciales (atomic_red_team, sigma, lolbas, gtfobins, caldera, d3fend, elastic_rules, mitre_cti) +- [ ] Cada fuente tiene tipo, URL y configuración correctos +- [ ] Se pueden activar/desactivar fuentes individualmente + +--- + +### T-201: Servicio de importación de Sigma Rules + +**Objetivo:** Importar reglas de detección Sigma del repositorio SigmaHQ y almacenarlas como templates de detección asociados a técnicas MITRE. + +**Archivo a crear:** `backend/app/services/sigma_import_service.py` + +**Modelo a crear:** `backend/app/models/detection_rule.py` + +**Campos de DetectionRule:** + +| 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 | + +**Lógica de importación:** + +1. Clonar/descargar el repo SigmaHQ (o usar GitHub API para ficheros YAML) +2. Para cada regla `.yml`: + - Parsear YAML (título, descripción, logsource, detection, tags) + - Extraer tags de ATT&CK: `attack.t1059.001` → `T1059.001` + - Extraer severidad del campo `level` + - Almacenar como `DetectionRule` con `source = "sigma"` +3. No duplicar reglas existentes (comparar por `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 +- [ ] El contenido YAML de la regla se almacena completo +- [ ] Ejecutar dos veces no duplica +- [ ] Se importan al menos 500+ reglas +- [ ] Las severidades se mapean correctamente + +--- + +### T-202: 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` + +**Lógica para LOLBAS:** + +1. Descargar JSONs desde la API de LOLBAS (`lolbas-project.github.io/api/`) +2. Cada entrada contiene: Name, Description, Commands, Paths, MitreAttackTechniques +3. Por cada binario y cada técnica MITRE asociada: + - Crear TestTemplate con `source = "lolbas"`, platform = "windows" + - El `attack_procedure` incluye los commands documentados + - El `tool_suggested` es el nombre del binario +4. No duplicar + +**Lógica para GTFOBins:** + +1. Parsear datos desde la API/JSON de GTFOBins +2. Cada entrada contiene: nombre del binario, funciones (shell, file-upload, file-download, etc.) +3. Por cada binario y función: + - Crear TestTemplate con `source = "gtfobins"`, platform = "linux" + - El `attack_procedure` incluye los ejemplos de comandos + +**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 + +--- + +### T-203: 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` + +**Lógica:** + +1. Descargar abilities desde el repositorio de CALDERA en GitHub +2. Cada ability es un YAML con: id, name, description, tactic, technique.attack_id, platforms, executors +3. Por cada ability: + - Crear TestTemplate con `source = "caldera"` + - Mapear a técnica MITRE + - Incluir los executors como `attack_procedure` + - Incluir las platforms soportadas + +**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 + +--- + +### T-204: 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` + +**Lógica:** + +1. Descargar reglas TOML desde el repo `elastic/detection-rules` +2. Cada regla TOML contiene: name, description, query (KQL), threat (con ATT&CK mapping), severity +3. Por cada regla: + - Parsear TOML + - Extraer mappings de ATT&CK del campo `threat` + - Crear DetectionRule con `source = "elastic"`, `rule_format = "kql"` + - Almacenar el query KQL completo + +**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 + +--- + +### T-205: 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 | + +**Frontend — Panel de fuentes:** + +- Tabla con todas las fuentes: nombre, tipo, estado, última sync, stats +- Toggle para activar/desactivar +- Botón de sync individual con progreso +- Botón de "Sync All" con progreso +- 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 +- [ ] "Sync All" ejecuta todas las fuentes activas secuencialmente +- [ ] Las estadísticas se actualizan tras cada sync +- [ ] Solo admin puede acceder + +--- + +## FASE 22 — Perfiles de Amenaza (Threat Actor Profiles) + +### T-206: Modelo ThreatActor + +**Objetivo:** Crear un modelo para almacenar perfiles de grupos de amenaza (APTs) con sus TTPs asociadas, permitiendo priorizar qué tests ejecutar según las amenazas relevantes para la organización. + +**Archivo a crear:** `backend/app/models/threat_actor.py` + +**Campos:** + +| 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 [] (nombres alternativos) | +| description | Text | nullable | +| country | String | nullable (país de origen atribuido) | +| target_sectors | JSONB | nullable, default [] (sectores objetivo) | +| target_regions | JSONB | nullable, default [] (regiones geográficas) | +| motivation | String | nullable (espionage, financial, destruction, etc.)| +| sophistication | String | nullable (low, medium, high, advanced) | +| first_seen | String | nullable (año/fecha de primera actividad) | +| last_seen | String | nullable | +| references | JSONB | nullable, default [] (URLs de referencia) | +| 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 (cómo el grupo usa esta técnica) | +| first_seen_using | String | nullable | + +**Generar migración.** + +**Validación:** + +- [ ] Las tablas se crean correctamente +- [ ] Se puede asociar un threat actor a múltiples técnicas +- [ ] Una técnica puede estar asociada a múltiples threat actors +- [ ] Los campos JSONB aceptan arrays + +--- + +### T-207: 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` + +**Lógica:** + +1. Descargar datos STIX desde `https://github.com/mitre/cti` (enterprise-attack) +2. Filtrar objetos de tipo `intrusion-set` (grupos APT) +3. Para cada grupo: + - Extraer name, description, aliases, external_references + - Extraer el MITRE ID de `external_references` +4. Buscar relaciones `uses` entre el grupo y `attack-pattern` (técnicas) +5. Crear `ThreatActor` y sus `ThreatActorTechnique` asociados +6. No duplicar en re-ejecuciones + +**Validación:** + +- [ ] Se importan 140+ threat actors +- [ ] Cada actor tiene sus técnicas asociadas +- [ ] Las relaciones actor-técnica son correctas (verificar con datos de MITRE) +- [ ] Los aliases y descriptions se importan +- [ ] Re-ejecutar no duplica + +--- + +### T-208: Endpoints y UI de Threat Actors + +**Archivos a crear:** + +- `backend/app/routers/threat_actors.py` +- `frontend/src/api/threat-actors.ts` +- `frontend/src/pages/ThreatActorsPage.tsx` +- `frontend/src/pages/ThreatActorDetailPage.tsx` + +**Endpoints:** + +| Método | Ruta | Auth | Descripción | +|--------|-----------------------------------------|-------------|----------------------------------------| +| GET | /threat-actors | autenticado | Listar con filtros | +| GET | /threat-actors/{id} | autenticado | Detalle con técnicas y cobertura | +| 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 | +| POST | /threat-actors/{id}/generate-campaign | red_tech, admin | Generar campaña de tests para cubrir gaps | + +**Filtros del listado:** +- `country`, `target_sectors`, `motivation`, `sophistication` +- `search` (busca en name, aliases, description) + +**Página ThreatActorsPage:** + +- Grid de cards con: nombre, país (bandera), sectores, motivación, nº técnicas, cobertura % +- Filtros laterales por sector, región, motivación +- Buscador + +**Página ThreatActorDetailPage:** + +- Header con perfil del grupo (nombre, aliases, descripción, country, motivación) +- **Heatmap de técnicas**: mini-matriz ATT&CK mostrando solo las técnicas de este actor, coloreadas por estado de cobertura +- **Coverage gap analysis**: lista de técnicas NO cubiertas +- **Botón "Generate Test Campaign"**: crea tests para cubrir las gaps usando templates disponibles +- Lista de tests existentes vinculados a técnicas de este actor + +**Validación:** + +- [ ] El listado muestra threat actors con filtros funcionales +- [ ] El detalle muestra el perfil completo con heatmap +- [ ] El coverage calcula correctamente qué % de las técnicas del actor están cubiertas +- [ ] El gap analysis identifica técnicas sin tests validados +- [ ] "Generate Campaign" crea tests desde templates disponibles +- [ ] La ruta se añade al sidebar: "Threat Actors" + +--- + +## FASE 23 — MITRE D3FEND: Contramedidas Defensivas + +### T-209: Modelo y importación de D3FEND + +**Objetivo:** Integrar el framework MITRE D3FEND para mapear cada técnica ATT&CK a las contramedidas defensivas recomendadas, dando al Blue Team una guía de qué mecanismos de defensa validar. + +**Archivo a crear:** `backend/app/models/defensive_technique.py` + +**Campos:** + +| 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 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 +- [ ] El endpoint de técnica detalle incluye las contramedidas recomendadas + +--- + +### T-210: 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 +- Indicador de si hay reglas de detección asociadas en el catálogo + +**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) +- Checklist de "¿Se validó esta contramedida?" (checkbox que el blue_tech marca) + +**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 +- [ ] El checklist se guarda correctamente + +--- + +## FASE 24 — Reglas de Detección Sugeridas por Test + +### T-211: Asociar DetectionRules a tests y templates + +**Objetivo:** Vincular reglas de detección (Sigma, Elastic, etc.) a los tests y templates, de manera que el Blue Team sepa exactamente 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 (si es la regla principal esperada)| + +**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 + +**Endpoint nuevo:** + +``` +GET /test-templates/{id}/detection-rules → reglas de detección sugeridas para este template +GET /detection-rules?technique={mitre_id} → reglas para una técnica +``` + +**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 funciona + +--- + +### T-212: UI de reglas de detección en la 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. + +**Archivo a crear:** `frontend/src/components/test-detail/DetectionRuleChecklist.tsx` + +**Contenido:** + +- Lista de reglas de detección asociadas al test/template +- Cada regla con: título, severidad (badge color), fuente (Sigma/Elastic), contenido expandible +- Checkbox: "This rule triggered" / "This rule did NOT trigger" / "Not applicable" +- Campo de notas por regla +- Resumen: X/Y reglas detectaron (con porcentaje) + +**Datos a guardar en el backend:** + +Crear 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 | + +**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 25 — Campañas de Tests (Attack Chains) + +### T-213: Modelo Campaign + +**Objetivo:** Crear campañas que agrupen múltiples tests en una secuencia que simula una cadena de ataque completa (kill chain), inspirado en cómo Cymulate y AttackIQ ejecutan full kill chain assessments. + +**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 (ejecución programada) | +| 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.) | + +**Generar migración.** + +**Validación:** + +- [ ] Las tablas se crean correctamente +- [ ] Una campaña puede contener múltiples tests ordenados +- [ ] Los tests de una campaña pueden tener dependencias entre sí +- [ ] El campo `phase` permite etiquetar cada test con la fase del kill chain + +--- + +### T-214: 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 (inicia ejecución) | +| 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 del actor | + +**Servicio de generación automática:** + +`generate_campaign_from_threat_actor(db, actor_id, user)`: +1. Obtener técnicas del actor no cubiertas +2. Para cada técnica sin test validado, buscar el mejor template disponible +3. Crear test desde template +4. Crear campaña con los tests ordenados por kill chain (tactic) +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-215: UI de Campañas + +**Archivos a crear:** + +- `frontend/src/pages/CampaignsPage.tsx` +- `frontend/src/pages/CampaignDetailPage.tsx` +- `frontend/src/components/CampaignTimeline.tsx` + +**CampaignsPage:** + +- 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" + +**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 + +**Ruta:** `/campaigns` y `/campaigns/:id` — añadir al sidebar. + +**Validación:** + +- [ ] El listado muestra campañas con filtros +- [ ] 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 +- [ ] El detalle es interactivo (click en nodos navega) + +--- + +## FASE 26 — Heatmap ATT&CK Avanzado (estilo Navigator) + +### T-216: 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/comparison | autenticado | Comparar dos snapshots temporales | +| GET | /heatmap/export-navigator | autenticado | Exportar como JSON del ATT&CK Navigator | + +**Formato de respuesta (compatible con ATT&CK Navigator layers):** + +```json +{ + "name": "Aegis Coverage", + "versions": {"attack": "15", "navigator": "5.0", "layer": "4.5"}, + "domain": "enterprise-attack", + "techniques": [ + { + "techniqueID": "T1059.001", + "tactic": "execution", + "color": "#00ff00", + "score": 100, + "comment": "Validated - 3 tests passed", + "metadata": [...] + } + ] +} +``` + +**Validación:** + +- [ ] `/heatmap/coverage` retorna datos para todas las técnicas +- [ ] `/heatmap/threat-actor/{id}` resalta solo las técnicas del actor +- [ ] `/heatmap/export-navigator` genera un JSON importable en ATT&CK Navigator real +- [ ] `/heatmap/comparison` acepta dos fechas y muestra diferencias +- [ ] Los colores se calculan correctamente según el estado + +--- + +### T-217: Frontend de heatmap avanzado + +**Objetivo:** Rediseñar la página de matriz ATT&CK con un heatmap interactivo que soporte capas superpuestas. + +**Archivos a crear/modificar:** + +- `frontend/src/pages/TechniquesPage.tsx` — rediseñar +- `frontend/src/components/AdvancedHeatmap.tsx` +- `frontend/src/components/HeatmapLayerSelector.tsx` +- `frontend/src/components/HeatmapLegend.tsx` + +**Funcionalidades:** + +1. **Selector de capas**: dropdown/checkboxes para seleccionar qué capas mostrar + - Coverage (default) + - Threat Actor (selector de actor) + - Detection Rules (cobertura de reglas) + - Campaign progress +2. **Capas superpuestas**: poder ver coverage + threat actor simultáneamente (gradientes) +3. **Zoom y scroll**: la matriz es grande — zoom in/out, scrollable +4. **Tooltips**: hover sobre una técnica muestra resumen (status, nº tests, reglas, actors) +5. **Filtros**: por táctica, plataforma, status +6. **Export**: botón para exportar layer compatible con ATT&CK Navigator +7. **Leyenda**: código de colores dinámico según capas activas + +**Validación:** + +- [ ] El heatmap renderiza todas las técnicas agrupadas por táctica +- [ ] Seleccionar capas diferentes cambia la visualización +- [ ] La superposición de capas funciona visualmente +- [ ] Los tooltips muestran información útil +- [ ] Export genera un JSON descargable compatible con ATT&CK Navigator +- [ ] Zoom y scroll funcionan suavemente + +--- + +## FASE 27 — Scoring y Métricas Avanzadas + +### T-218: Sistema de scoring de cobertura + +**Objetivo:** Implementar un sistema de puntuación granular que vaya más allá de estados binarios, calculando scores numéricos de cobertura por técnica, táctica, actor y organización. + +**Archivo a crear:** `backend/app/services/scoring_service.py` + +**Lógica de scoring por técnica:** + +```python +def calculate_technique_score(technique) -> float: + """ + Score de 0 a 100 basado en: + - Tests validados con detección (40 pts) + - Reglas de detección validadas (20 pts) + - Contramedidas D3FEND cubiertas (15 pts) + - Frescura: tests recientes vs antiguos (15 pts) + - Diversidad de plataformas cubiertas (10 pts) + """ +``` + +**Lógica de scoring por threat actor:** + +```python +def calculate_actor_coverage_score(actor) -> float: + """ + Promedio ponderado de scores de las técnicas del actor, + ponderado por frecuencia de uso de cada técnica. + """ +``` + +**Lógica de scoring global:** + +```python +def calculate_organization_score() -> dict: + """ + Score global de la organización: + - Total coverage score (promedio de técnicas evaluadas) + - Critical technique score (técnicas de alta severidad) + - Detection maturity score (basado en reglas de detección) + - Response readiness score (basado en remediaciones completadas) + """ +``` + +**Endpoints:** + +| Método | Ruta | Auth | Descripción | +|--------|-----------------------------------|-------------|---------------------------------------| +| GET | /scores/technique/{mitre_id} | autenticado | Score detallado de una técnica | +| 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 en el tiempo | + +**Validación:** + +- [ ] El score de técnica se calcula correctamente (0-100) +- [ ] El score de threat actor refleja la cobertura real +- [ ] El score de organización agrega correctamente +- [ ] El historial muestra la evolución temporal +- [ ] Los pesos son configurables + +--- + +### T-219: Métricas operativas (MTTD, MTTR, Detection Efficacy) + +**Objetivo:** Implementar métricas operativas del equipo de seguridad inspiradas en las mejores prácticas de purple teaming. + +**Archivo a crear:** `backend/app/services/operational_metrics_service.py` + +**Métricas a calcular:** + +1. **MTTD (Mean Time to Detect)**: Tiempo medio entre que Red Team ejecuta un ataque (`red_executing → blue_evaluating`) y Blue Team completa evaluación +2. **MTTR (Mean Time to Respond/Remediate)**: Tiempo medio entre detección y remediación completada +3. **Detection Efficacy**: % de tests donde el resultado es `detected` vs total de tests validados +4. **Alert Fidelity**: Ratio de reglas de detección que realmente se activaron vs total evaluadas +5. **Coverage Velocity**: Tasa de nuevas técnicas cubiertas por semana/mes +6. **Validation Throughput**: Tests completados (validados + rechazados) por semana/mes +7. **Rejection Rate**: % de tests rechazados (indica calidad del trabajo) + +**Endpoints:** + +| Método | Ruta | Auth | Descripción | +|--------|-----------------------------------|-------------|---------------------------------------| +| GET | /metrics/operational | autenticado | Todas las métricas operativas | +| GET | /metrics/operational/trend | autenticado | Tendencia temporal (últimos 30/90 días)| +| GET | /metrics/operational/by-team | autenticado | Métricas desglosadas por equipo | + +**Validación:** + +- [ ] MTTD se calcula correctamente a partir de timestamps +- [ ] MTTR incluye el tiempo de remediación +- [ ] Detection Efficacy es un porcentaje correcto +- [ ] Las tendencias muestran evolución temporal +- [ ] El desglose por equipo funciona + +--- + +### T-220: Dashboard ejecutivo con scores y métricas + +**Objetivo:** Crear una vista de dashboard ejecutivo con los scores, métricas operativas y tendencias, pensada para presentar a dirección. + +**Archivo a crear:** `frontend/src/pages/ExecutiveDashboardPage.tsx` + +**Secciones:** + +1. **Score Card**: Score global de la organización con gauge (tipo velocímetro) +2. **Trend Chart**: Gráfico de línea mostrando evolución del score en los últimos 90 días +3. **Top 5 Threat Actors**: Actors más relevantes con su % de cobertura +4. **Operational KPIs**: Cards con MTTD, MTTR, Detection Efficacy, Throughput +5. **Coverage por táctica**: Barras horizontales con % de cobertura por táctica +6. **Critical Gaps**: Top 10 técnicas de alta severidad sin cobertura +7. **Team Performance**: Comparativa Red vs Blue (tests completados, tiempos) + +**Ruta:** `/executive-dashboard` — accesible para roles `admin`, `red_lead`, `blue_lead`. + +**Validación:** + +- [ ] El dashboard carga con datos reales +- [ ] El gauge del score global funciona y se colorea correctamente +- [ ] Los gráficos de tendencia se renderizan +- [ ] Los KPIs muestran valores correctos +- [ ] Los critical gaps enlazan al detalle de la técnica +- [ ] Solo roles de liderazgo pueden acceder + +--- + +## FASE 28 — Compliance y Reportes Avanzados + +### T-221: Modelo de mapeo a frameworks de compliance + +**Objetivo:** Mapear técnicas ATT&CK y controles de seguridad a frameworks de compliance (NIST 800-53, DORA, NIS2, ISO 27001), inspirado en cómo Cymulate y Picus generan reportes 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 | + +**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 | + +**Seed de datos:** NIST 800-53 → ATT&CK mappings están disponibles en D3FEND y en el MITRE CTI. + +**Generar migración.** + +**Validación:** + +- [ ] Las tablas se crean correctamente +- [ ] Se puede mapear un control de compliance a múltiples técnicas +- [ ] Los frameworks NIST 800-53 y DORA se seedean con sus controles +- [ ] Los mappings a técnicas ATT&CK son correctos + +--- + +### T-222: 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 (cubierto/no cubierto) | +| GET | /compliance/frameworks/{id}/report | autenticado | Reporte completo de compliance | +| GET | /compliance/frameworks/{id}/report/pdf | autenticado | Export PDF del reporte | +| GET | /compliance/frameworks/{id}/gaps | autenticado | Controles con técnicas no cubiertas | + +**Lógica del reporte:** + +Para cada control del framework: +1. Obtener las técnicas ATT&CK mapeadas +2. Verificar el estado de cobertura de cada técnica +3. El control está "cubierto" si todas sus técnicas tienen score > umbral +4. El control está "parcialmente cubierto" si algunas técnicas están cubiertas +5. El control está "no cubierto" si ninguna técnica está cubierta +6. Calcular % de compliance global + +**Validación:** + +- [ ] El estado de cada control se calcula correctamente +- [ ] El reporte incluye todos los controles del framework +- [ ] El export PDF se genera correctamente +- [ ] Los gaps listan controles con técnicas no cubiertas +- [ ] El % global de compliance es correcto + +--- + +### T-223: UI de Compliance + +**Archivos a crear:** + +- `frontend/src/pages/CompliancePage.tsx` +- `frontend/src/pages/ComplianceReportPage.tsx` + +**CompliancePage:** + +- Selector de framework (NIST 800-53, DORA, NIS2, ISO 27001) +- Dashboard de compliance: gauge de % global, distribución cubierto/parcial/no cubierto +- Tabla de controles con: ID, título, estado (badge color), % cobertura, nº técnicas +- Filtros: estado, categoría +- Botón de export (PDF, CSV) + +**ComplianceReportPage:** + +- Reporte formateado listo para presentar a auditoría +- Header con framework, fecha, organización +- Resumen ejecutivo con métricas +- Tabla detallada control por control con evidencias de cobertura +- Sección de gaps y plan de remediación recomendado + +**Ruta:** `/compliance` — añadir al sidebar. + +**Validación:** + +- [ ] La página muestra frameworks con métricas correctas +- [ ] La tabla de controles se filtra correctamente +- [ ] El reporte PDF se descarga y es legible +- [ ] Los datos de compliance son consistentes con los scores +- [ ] La ruta aparece en el sidebar + +--- + +## FASE 29 — Comparación Temporal y Re-testing + +### T-224: Snapshots de cobertura + +**Objetivo:** Crear snapshots periódicos del estado de cobertura para poder comparar en el tiempo y medir progreso. + +**Archivo a crear:** `backend/app/models/coverage_snapshot.py` + +**Campos:** + +| Campo | Tipo | Restricciones | +|--------------------|----------|-------------------------------------------------| +| id | UUID | PK, default uuid4 | +| name | String | nullable (ej: "Pre-remediación Q1") | +| snapshot_data | JSONB | not null (estado completo de todas las técnicas)| +| 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 | +| created_by | UUID | FK → users.id, nullable | +| created_at | DateTime | default utcnow | + +**Servicio** `backend/app/services/snapshot_service.py`: + +- `create_snapshot(db, name, user)` — captura estado actual +- `compare_snapshots(db, snapshot_a_id, snapshot_b_id)` — retorna diff +- Auto-crear snapshot semanal (job APScheduler) + +**Endpoints:** + +| Método | Ruta | Auth | Descripción | +|--------|-----------------------------------|-------------|---------------------------------------| +| GET | /snapshots | autenticado | Listar snapshots | +| POST | /snapshots | red_lead, blue_lead, admin | Crear snapshot manual | +| GET | /snapshots/{id} | autenticado | Detalle de un snapshot | +| GET | /snapshots/compare | autenticado | Comparar dos snapshots (query params) | + +**Validación:** + +- [ ] Crear snapshot captura el estado actual correctamente +- [ ] Comparar dos snapshots muestra técnicas que cambiaron +- [ ] El job semanal crea snapshots automáticamente +- [ ] La comparación incluye: técnicas mejoradas, empeoradas, sin cambio + +--- + +### T-225: UI de comparación temporal + +**Archivo a crear:** `frontend/src/pages/ComparisonPage.tsx` + +**Contenido:** + +- Selector de dos snapshots (date pickers o dropdown) +- **Side-by-side**: scores globales de cada snapshot +- **Diff table**: técnicas que cambiaron de estado (mejoraron/empeoraron) +- **Heatmap diff**: mini-matriz con colores verde (mejoró), rojo (empeoró), gris (sin cambio) +- **Métricas delta**: cuántas técnicas se mejoraron, % de progreso + +**Ruta:** `/comparison` — accesible desde el dashboard ejecutivo. + +**Validación:** + +- [ ] Se pueden seleccionar dos snapshots +- [ ] La comparación muestra las diferencias correctamente +- [ ] El heatmap diff se renderiza +- [ ] Las métricas delta son correctas + +--- + +### T-226: 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, inspirado en cómo Validato permite re-testing inmediato. + +**Archivos a modificar:** + +- `backend/app/services/test_workflow_service.py` +- `backend/app/models/test.py` — añadir campo `retest_of` (FK a test original) + +**Nuevo campo en Test:** + +| Campo | Tipo | Restricciones | +|-------------|--------|----------------------------------| +| retest_of | UUID | FK → tests.id, nullable | +| retest_count| Integer| default 0 | + +**Lógica:** + +1. Cuando `remediation_status` cambia a `completed`: + - Auto-crear un nuevo test con los mismos datos del original + - Marcar como `retest_of = original_test_id` + - Incrementar `retest_count` + - Estado: `draft` — listo para que Red Team lo ejecute de nuevo +2. Notificar al creador del test original y al red_tech asignado +3. En la UI del test, mostrar link al test original y al retest + +**Validación:** + +- [ ] Completar remediación crea automáticamente un retest +- [ ] El retest tiene `retest_of` apuntando al original +- [ ] El retest tiene los mismos datos base que el original +- [ ] Se genera notificación del retest +- [ ] En la UI se muestra la cadena de retests + +--- + +## FASE 30 — Scheduling y Automatización + +### T-227: Sistema de scheduling de campañas + +**Objetivo:** Permitir programar campañas para ejecución periódica, de manera que los tests se puedan re-ejecutar automáticamente. + +**Archivos a crear/modificar:** + +- `backend/app/models/campaign.py` — nuevos campos de scheduling +- `backend/app/services/campaign_scheduler_service.py` + +**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 | + +**Servicio de scheduling:** + +- Job APScheduler que revisa campañas recurrentes diariamente +- Si `next_run_at <= now` y `is_recurring = True`: + - Clonar los tests de la campaña con nuevos IDs + - Crear nueva instancia de campaña con los tests clonados + - Actualizar `last_run_at` y calcular `next_run_at` +- Notificar a los equipos de la nueva ejecución + +**Endpoints:** + +``` +PATCH /campaigns/{id}/schedule — configurar recurrencia +GET /campaigns/{id}/history — historial de ejecuciones +``` + +**Validación:** + +- [ ] Se puede configurar una campaña como recurrente +- [ ] El job crea instancias nuevas según el patrón +- [ ] Los tests se clonan correctamente +- [ ] El historial muestra todas las ejecuciones +- [ ] Las notificaciones se generan + +--- + +### T-228: UI de scheduling + +**Modificar:** `frontend/src/pages/CampaignDetailPage.tsx` + +**Nuevas funcionalidades:** + +- Toggle "Recurring Campaign" con selector de frecuencia +- Indicador de próxima ejecución programada +- Tab "Execution History" con tabla de ejecuciones pasadas +- Cada ejecución con: fecha, nº tests, progreso, score obtenido + +**Validación:** + +- [ ] El toggle de recurrencia funciona +- [ ] Se puede seleccionar la frecuencia +- [ ] La próxima ejecución se muestra +- [ ] El historial lista ejecuciones pasadas + +--- + +## FASE 31 — Tests Automatizados V3 + +### T-229: Tests de importación de fuentes + +**Archivo a crear:** `backend/tests/test_data_sources.py` + +**Tests:** + +```python +class TestDataSources: + def test_sigma_import(): + """Verificar importación de reglas Sigma""" + + def test_lolbas_import(): + """Verificar importación de LOLBAS""" + + def test_caldera_import(): + """Verificar importación de CALDERA abilities""" + + def test_elastic_rules_import(): + """Verificar importación de Elastic rules""" + + def test_d3fend_import(): + """Verificar importación de D3FEND""" + + def test_threat_actor_import(): + """Verificar importación de threat actors""" + + def test_no_duplicates_on_reimport(): + """Verificar que ninguna fuente duplica al re-importar""" +``` + +**Validación:** + +- [ ] Todos los tests pasan +- [ ] Cada importación se verifica independientemente + +--- + +### T-230: Tests de scoring y métricas + +**Archivo a crear:** `backend/tests/test_scoring.py` + +**Tests:** + +```python +class TestScoring: + def test_technique_score_calculation(): + """Score de técnica con diferentes combinaciones""" + + def test_threat_actor_coverage(): + """Cobertura contra un threat actor""" + + def test_organization_score(): + """Score global de la organización""" + + def test_mttd_calculation(): + """MTTD se calcula desde timestamps""" + + def test_detection_efficacy(): + """Detection efficacy con datos de prueba""" + + def test_compliance_status(): + """Estado de compliance con datos de prueba""" +``` + +**Validación:** + +- [ ] Todos los tests pasan +- [ ] Los cálculos son correctos con datos conocidos + +--- + +### T-231: Tests de campañas y threat actors + +**Archivo a crear:** `backend/tests/test_campaigns.py` + +**Tests:** + +```python +class TestCampaigns: + def test_create_campaign(): + """CRUD básico de campaña""" + + def test_campaign_progress(): + """Progreso se calcula según estado de tests""" + + def test_generate_from_threat_actor(): + """Generación automática de campaña desde actor""" + + def test_campaign_scheduling(): + """Recurrencia de campañas""" + + def test_campaign_cloning(): + """Clonación de tests al re-ejecutar""" +``` + +**Validación:** + +- [ ] Todos los tests pasan +- [ ] El flujo completo de campañas funciona + +--- + +## FASE 32 — Pulido Final V3 + +### T-232: 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 +📄 Reports +⚙️ System (admin) + ├─ Data Sources + ├─ MITRE Sync + ├─ Users + └─ Audit Log +``` + +**Validación:** + +- [ ] Todas las rutas funcionan +- [ ] El sidebar muestra items según rol +- [ ] La navegación es consistente +- [ ] No hay rutas rotas + +--- + +### T-233: 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:** + +- Índices en BD para campos de filtro frecuente (`mitre_technique_id`, `source`, `state`, `tactic`) +- Paginación cursor-based en listados grandes +- Caché de métricas y scores (redis o in-memory con TTL) +- Queries optimizadas con `selectinload` / `subqueryload` donde sea necesario + +**Optimizaciones frontend:** + +- Virtualización de tablas/listas grandes (react-window o tanstack-virtual) +- Lazy loading de páginas (React.lazy + Suspense) +- Memoización de componentes pesados (heatmap, charts) +- Debounce en buscadores + +**Validación:** + +- [ ] El heatmap con 3000+ técnicas renderiza sin lag +- [ ] Las tablas con 5000+ filas scrollean suavemente +- [ ] Los endpoints responden en < 500ms con volúmenes grandes +- [ ] El dashboard ejecutivo carga en < 3 segundos + +--- + +### T-234: Documentación completa V3 + +**Archivos a modificar:** + +- `README.md` +- `docs/API.md` +- Nuevo: `docs/ARCHITECTURE.md` +- Nuevo: `docs/DATA_SOURCES.md` + +**README actualizado:** + +- Descripción completa de todas las funcionalidades V3 +- Diagrama de arquitectura +- Guía de inicio rápido +- Cómo importar datos de todas las fuentes +- Cómo configurar campañas y threat actors +- Cómo generar reportes de compliance + +**ARCHITECTURE.md:** + +- Diagrama de la base de datos completa +- Flujo de datos entre servicios +- Descripción de cada servicio y su responsabilidad + +**DATA_SOURCES.md:** + +- Lista de todas las fuentes de datos soportadas +- Cómo configurar cada fuente +- Frecuencia de actualización recomendada +- Troubleshooting de importaciones + +**Validación:** + +- [ ] Un nuevo desarrollador puede entender la arquitectura leyendo ARCHITECTURE.md +- [ ] DATA_SOURCES.md cubre todas las fuentes con instrucciones claras +- [ ] El README refleja todas las funcionalidades V3 +- [ ] Swagger UI muestra todos los endpoints + +--- + +## Resumen de Fases V3 + +| Fase | Tareas | Descripción | +|------|------------------|-------------------------------------------------------| +| 21 | T-200 a T-205 | Fuentes de tests múltiples: importación y unificación | +| 22 | T-206 a T-208 | Perfiles de amenaza (Threat Actor Profiles) | +| 23 | T-209 a T-210 | MITRE D3FEND: contramedidas defensivas | +| 24 | T-211 a T-212 | Reglas de detección sugeridas por test | +| 25 | T-213 a T-215 | Campañas de tests (attack chains / kill chain) | +| 26 | T-216 a T-217 | Heatmap ATT&CK avanzado (estilo Navigator) | +| 27 | T-218 a T-220 | Scoring y métricas avanzadas (MTTD, MTTR, etc.) | +| 28 | T-221 a T-223 | Compliance y reportes (NIST, DORA, NIS2, ISO 27001) | +| 29 | T-224 a T-226 | Comparación temporal y re-testing automático | +| 30 | T-227 a T-228 | Scheduling y automatización de campañas | +| 31 | T-229 a T-231 | Tests automatizados V3 | +| 32 | T-232 a T-234 | Pulido final y documentación V3 | + +> **Total: 35 tareas = 35 commits mínimo** +> Cada tarea es autocontenida y verificable antes de hacer commit. + +--- + +## Análisis Competitivo: Qué Copiamos de Cada Plataforma + +### De [Validato](https://validato.io/) +- **Step-by-step remediation**: Campo de remediación con pasos concretos (V2: T-129) +- **Mapeo a MITRE SHIELD/D3FEND**: Contramedidas defensivas (V3: T-209) +- **Re-testing post-remediación**: Verificar que la fix funciona (V3: T-226) +- **Validación continua**: Campañas recurrentes (V3: T-227) +- **Resultados mapeados a frameworks**: Compliance reporting (V3: T-221) + +### De [Cymulate](https://cymulate.com/) +- **Full kill chain campaigns**: Cadenas de tests simulando ataques completos (V3: T-213) +- **Auto-generated Sigma/EDR rules**: Reglas de detección sugeridas por test (V3: T-211) +- **Vendor-specific remediation**: Guías de remediación específicas por herramienta (V3: T-129 mejorado) +- **100,000+ attack scenarios**: Múltiples fuentes de tests (V3: T-200-204) +- **Daily threat updates**: Sync automático de fuentes (V3: T-205) +- **Detection heatmap**: Heatmap de cobertura de detección (V3: T-216) +- **AI Template Creator**: Generación de campañas desde threat actors (V3: T-214) + +### De [Picus Security](https://www.picussecurity.com/) +- **Detection Rule Validation**: Validar si las reglas SIEM detectan realmente (V3: T-212) +- **Threat library 10,000+**: Catálogo masivo de tests de múltiples fuentes (V3: T-200-204) +- **Filter by geography/industry**: Filtros de threat actors por sector/región (V3: T-208) +- **150+ APT scenarios**: Emulación de grupos específicos (V3: T-206) +- **Campaign builder**: Constructor de campañas desde APT profiles (V3: T-214) +- **Multi-framework compliance**: NIST, DORA, HIPAA, ISO (V3: T-221) + +### De [AttackIQ](https://www.attackiq.com/) +- **Assessment templates**: Templates pre-construidos por escenario (V2: T-103, V3: expandido) +- **MITRE ATT&CK Navigator integration**: Export de layers (V3: T-216) +- **Continuous testing**: Campañas recurrentes programadas (V3: T-227) +- **Purple team collaboration**: Flujo Red/Blue con feedback en tiempo real (V2: core feature) +- **Detection pipeline validation**: Verificar toda la cadena de detección (V3: T-212) +- **Contextual risk prioritization**: Scoring basado en criticidad real (V3: T-218) + +### Fuentes Open-Source Únicas de Aegis +- **Atomic Red Team**: 1,500+ tests atómicos (V2: T-107) +- **SigmaHQ**: 3,000+ reglas de detección (V3: T-201) +- **LOLBAS + GTFOBins**: 750+ técnicas living-off-the-land (V3: T-202) +- **MITRE CALDERA**: 400+ abilities ejecutables (V3: T-203) +- **Elastic Detection Rules**: 1,000+ reglas KQL (V3: T-204) +- **MITRE D3FEND**: 200+ contramedidas defensivas (V3: T-209) +- **Adversary Emulation Library**: 15+ planes de emulación APT (V3: T-203) + +### Lo que Aegis NO tiene (y las plataformas enterprise sí): +> Estas son funcionalidades que 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. diff --git a/aegiscompleteplan.md b/aegiscompleteplan.md new file mode 100644 index 0000000..7ac25ef --- /dev/null +++ b/aegiscompleteplan.md @@ -0,0 +1,3989 @@ +# 🛡️ 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`:** +```python +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: +```python +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:** +```python +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_by` → `legacy_validated_by` y `validated_at` → `legacy_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: +```python +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:** +```python +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:** +```python +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:** +```python +# 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:** +```python +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:** +```python +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: +```json +{ + "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:** +```typescript +// 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; +} +``` + +**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:** +```typescript +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:** +```typescript +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:** +```typescript +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:** +```python +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:** +```python +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:** +```python +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:** +```python +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`: +```python +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: +```json + {"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:** +```python +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](https://github.com/redcanaryco/atomic-red-team) | +| **MITRE CALDERA** | Abilities (acciones) ejecutables por agente | YAML | 400+ | [GitHub](https://github.com/mitre/caldera) | +| **Adversary Emulation Library** | Planes completos de emulación de APTs | YAML/JSON/PDF | 15+ planes | [GitHub](https://github.com/center-for-threat-informed-defense/adversary_emulation_library) | +| **LOLBAS** | Binarios legítimos de Windows abusables | YAML/JSON | 400+ | [GitHub](https://github.com/LOLBAS-Project/LOLBAS) | +| **GTFOBins** | Binarios legítimos de Unix/Linux abusables | Markdown/JSON | 350+ | [GitHub](https://gtfobins.github.io/) | +| **MITRE ATT&CK Procedures** | Procedimientos documentados en la propia framework | STIX/JSON | 1,000+ | [TAXII Server](https://cti-taxii.mitre.org/) | + +### 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](https://github.com/SigmaHQ/sigma) | +| **Elastic Detection Rules** | Reglas de detección de Elastic SIEM | TOML | 1,000+ | [GitHub](https://github.com/elastic/detection-rules) | +| **MITRE D3FEND** | Framework de contramedidas defensivas | OWL/JSON | 200+ técnicas | [d3fend.mitre.org](https://d3fend.mitre.org/) | +| **Splunk Security Content** | Reglas de detección para Splunk | YAML | 1,500+ | [GitHub](https://github.com/splunk/security_content) | + +### 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](https://github.com/mitre/cti) | +| **MITRE ATT&CK Groups** | Perfiles de 140+ grupos APT con sus TTPs | STIX | [attack.mitre.org/groups](https://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:** +```python +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.001` → `T1059.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: +```python +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:** +```python +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: +```python +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: +```json +{ + "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: +```typescript +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): +```python +# 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): +```python +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:** +```python +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:** +```python +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:** +```python +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|90d|1y` | +| GET | /metrics/operational/by-team | autenticado | Métricas desglosadas por equipo (red vs blue) | + +**Formato de respuesta de `/metrics/operational`:** +```json +{ + "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:** +```python +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`: +```python +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`:** +```json +{ + "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:** +```python +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`: +```python +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: +```python +# config.py +MAX_RETEST_COUNT: int = 3 # máximo de retests automáticos por test original +``` + +**Lógica:** +```python +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`: +```python +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:** +```json +{ + "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 +```python +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:** +```python +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:** +```python +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: +```python + # 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) +``` + +2. **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 + +3. **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): +```python + _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 + +4. **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:** +```typescript + // 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 `}>` + +3. **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 + +4. **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-001–036 | 36 | Plataforma base funcional | +| V2 | T-100–135 | 36 | Flujo Red/Blue, templates, notificaciones | +| V3 | T-200–240 | 41 | Enterprise: fuentes, scoring, compliance | +| **Total** | **113 tareas** | **113 commits** | **Plataforma completa** | + +--- + +## Análisis Competitivo: Qué Copiamos de Cada Plataforma + +### De [Validato](https://validato.io/) +- 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](https://cymulate.com/) +- 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-201–206) +- 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](https://www.picussecurity.com/) +- Detection Rule Validation (V3: T-216) +- Catálogo masivo de tests de múltiples fuentes (V3: T-201–206) +- 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](https://www.attackiq.com/) +- 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. \ No newline at end of file diff --git a/backend/alembic/versions/b006_add_notifications_table.py b/backend/alembic/versions/b006_add_notifications_table.py new file mode 100644 index 0000000..0e1eba6 --- /dev/null +++ b/backend/alembic/versions/b006_add_notifications_table.py @@ -0,0 +1,46 @@ +"""add_notifications_table + +Revision ID: b006notifications +Revises: b005v2indexes +Create Date: 2026-02-09 11:00:00.000000 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects.postgresql import UUID + + +# revision identifiers, used by Alembic. +revision: str = 'b006notifications' +down_revision: Union[str, Sequence[str], None] = 'b005v2indexes' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Create notifications table.""" + op.create_table( + 'notifications', + sa.Column('id', UUID(as_uuid=True), primary_key=True, server_default=sa.text('gen_random_uuid()')), + sa.Column('user_id', UUID(as_uuid=True), sa.ForeignKey('users.id'), nullable=False), + sa.Column('type', sa.String(), nullable=False), + sa.Column('title', sa.String(), nullable=False), + sa.Column('message', sa.Text(), nullable=True), + sa.Column('entity_type', sa.String(), nullable=True), + sa.Column('entity_id', UUID(as_uuid=True), nullable=True), + sa.Column('read', sa.Boolean(), server_default='false'), + sa.Column('created_at', sa.DateTime(), server_default=sa.func.now()), + ) + op.create_index('ix_notifications_user_id', 'notifications', ['user_id']) + op.create_index('ix_notifications_read', 'notifications', ['read']) + op.create_index('ix_notifications_created_at', 'notifications', ['created_at']) + + +def downgrade() -> None: + """Drop notifications table.""" + op.drop_index('ix_notifications_created_at', table_name='notifications') + op.drop_index('ix_notifications_read', table_name='notifications') + op.drop_index('ix_notifications_user_id', table_name='notifications') + op.drop_table('notifications') diff --git a/backend/app/jobs/mitre_sync_job.py b/backend/app/jobs/mitre_sync_job.py index bacc91e..b3eec45 100644 --- a/backend/app/jobs/mitre_sync_job.py +++ b/backend/app/jobs/mitre_sync_job.py @@ -17,6 +17,7 @@ from apscheduler.schedulers.background import BackgroundScheduler from app.database import SessionLocal from app.services.mitre_sync_service import sync_mitre from app.services.intel_service import scan_intel +from app.services.notification_service import cleanup_old_notifications logger = logging.getLogger(__name__) @@ -45,6 +46,19 @@ def _run_mitre_sync() -> None: db.close() +def _run_notification_cleanup() -> None: + """Clean up old read notifications.""" + logger.info("Scheduled notification cleanup job starting...") + db = SessionLocal() + try: + deleted = cleanup_old_notifications(db, days=90) + logger.info("Notification cleanup finished — deleted %d old notifications", deleted) + except Exception: + logger.exception("Notification cleanup job failed") + finally: + db.close() + + def _run_intel_scan() -> None: """Execute an intel scan inside its own DB session.""" logger.info("Scheduled intel scan job starting...") @@ -89,5 +103,13 @@ def start_scheduler() -> None: name="Intel scan (every 7d)", replace_existing=True, ) + scheduler.add_job( + _run_notification_cleanup, + trigger="interval", + hours=24, + id="notification_cleanup", + name="Notification cleanup (daily)", + replace_existing=True, + ) scheduler.start() - logger.info("Background scheduler started — mitre_sync (24h), intel_scan (7d)") + logger.info("Background scheduler started — mitre_sync (24h), intel_scan (7d), notification_cleanup (24h)") diff --git a/backend/app/main.py b/backend/app/main.py index a18e3c4..528e2f0 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -16,6 +16,7 @@ from app.routers import system as system_router from app.routers import metrics as metrics_router from app.routers import users as users_router from app.routers import audit as audit_router +from app.routers import notifications as notifications_router from app.storage import ensure_bucket_exists from app.jobs.mitre_sync_job import start_scheduler, scheduler @@ -56,6 +57,7 @@ app.include_router(system_router.router, prefix="/api/v1") app.include_router(metrics_router.router, prefix="/api/v1") app.include_router(users_router.router, prefix="/api/v1") app.include_router(audit_router.router, prefix="/api/v1") +app.include_router(notifications_router.router, prefix="/api/v1") @app.get("/health") diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index 75723ed..b6fafc7 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -6,10 +6,11 @@ from app.models.test_template import TestTemplate from app.models.evidence import Evidence from app.models.intel import IntelItem from app.models.audit import AuditLog +from app.models.notification import Notification from app.models.enums import TechniqueStatus, TestState, TestResult, TeamSide __all__ = [ "User", "Technique", "Test", "TestTemplate", "Evidence", - "IntelItem", "AuditLog", + "IntelItem", "AuditLog", "Notification", "TechniqueStatus", "TestState", "TestResult", "TeamSide", ] diff --git a/backend/app/models/notification.py b/backend/app/models/notification.py new file mode 100644 index 0000000..c7c7961 --- /dev/null +++ b/backend/app/models/notification.py @@ -0,0 +1,39 @@ +"""Notification model — in-app notifications for user actions.""" + +import uuid +from datetime import datetime + +from sqlalchemy import Column, String, Text, Boolean, DateTime, ForeignKey, Index +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import relationship + +from app.database import Base + + +class Notification(Base): + """ + In-app notification for alerting users when they need to act. + + Types include: test_assigned, validation_needed, test_rejected, + test_validated, test_state_changed, etc. + """ + __tablename__ = "notifications" + + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) + user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False) + type = Column(String, nullable=False) + title = Column(String, nullable=False) + message = Column(Text, nullable=True) + entity_type = Column(String, nullable=True) + entity_id = Column(UUID(as_uuid=True), nullable=True) + read = Column(Boolean, default=False) + created_at = Column(DateTime, default=datetime.utcnow) + + # Relationships + user = relationship("User") + + __table_args__ = ( + Index("ix_notifications_user_id", "user_id"), + Index("ix_notifications_read", "read"), + Index("ix_notifications_created_at", "created_at"), + ) diff --git a/backend/app/routers/notifications.py b/backend/app/routers/notifications.py new file mode 100644 index 0000000..5f25cd6 --- /dev/null +++ b/backend/app/routers/notifications.py @@ -0,0 +1,103 @@ +"""Notification endpoints. + +Endpoints +--------- +GET /notifications — list user notifications (paginated) +GET /notifications/unread-count — count of unread notifications +PATCH /notifications/{id}/read — mark one notification as read +POST /notifications/read-all — mark all as read +""" + +import uuid + +from fastapi import APIRouter, Depends, HTTPException, Query, status +from sqlalchemy.orm import Session + +from app.database import get_db +from app.dependencies.auth import get_current_user +from app.models.notification import Notification +from app.models.user import User +from app.schemas.notification import NotificationOut, UnreadCountOut +from app.services.notification_service import ( + mark_as_read, + mark_all_as_read, + get_unread_count, +) + +router = APIRouter(prefix="/notifications", tags=["notifications"]) + + +# --------------------------------------------------------------------------- +# GET /notifications — list (paginated) +# --------------------------------------------------------------------------- + + +@router.get("", response_model=list[NotificationOut]) +def list_notifications( + offset: int = Query(0, ge=0), + limit: int = Query(20, ge=1, le=100), + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +): + """Return paginated notifications for the current user, newest first.""" + notifs = ( + db.query(Notification) + .filter(Notification.user_id == current_user.id) + .order_by(Notification.created_at.desc()) + .offset(offset) + .limit(limit) + .all() + ) + return notifs + + +# --------------------------------------------------------------------------- +# GET /notifications/unread-count +# --------------------------------------------------------------------------- + + +@router.get("/unread-count", response_model=UnreadCountOut) +def unread_count( + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +): + """Return the number of unread notifications for the current user.""" + count = get_unread_count(db, current_user.id) + return UnreadCountOut(unread_count=count) + + +# --------------------------------------------------------------------------- +# PATCH /notifications/{id}/read +# --------------------------------------------------------------------------- + + +@router.patch("/{notification_id}/read", response_model=NotificationOut) +def read_notification( + notification_id: uuid.UUID, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +): + """Mark a single notification as read.""" + success = mark_as_read(db, notification_id, current_user.id) + if not success: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Notification not found", + ) + notif = db.query(Notification).filter(Notification.id == notification_id).first() + return notif + + +# --------------------------------------------------------------------------- +# POST /notifications/read-all +# --------------------------------------------------------------------------- + + +@router.post("/read-all") +def read_all_notifications( + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +): + """Mark all notifications for the current user as read.""" + count = mark_all_as_read(db, current_user.id) + return {"detail": f"Marked {count} notifications as read"} diff --git a/backend/app/schemas/notification.py b/backend/app/schemas/notification.py new file mode 100644 index 0000000..436cb00 --- /dev/null +++ b/backend/app/schemas/notification.py @@ -0,0 +1,28 @@ +"""Pydantic schemas for Notification endpoints.""" + +import uuid +from datetime import datetime + +from pydantic import BaseModel, ConfigDict + + +class NotificationOut(BaseModel): + """Notification returned by the API.""" + + id: uuid.UUID + user_id: uuid.UUID + type: str + title: str + message: str | None = None + entity_type: str | None = None + entity_id: uuid.UUID | None = None + read: bool = False + created_at: datetime | None = None + + model_config = ConfigDict(from_attributes=True) + + +class UnreadCountOut(BaseModel): + """Simple counter response.""" + + unread_count: int diff --git a/backend/app/services/notification_service.py b/backend/app/services/notification_service.py new file mode 100644 index 0000000..df7c83c --- /dev/null +++ b/backend/app/services/notification_service.py @@ -0,0 +1,179 @@ +"""Notification service — create, read, and manage in-app notifications. + +Provides helpers for generating notifications automatically when test +state changes occur, plus CRUD for the notifications API. +""" + +import uuid +from datetime import datetime, timedelta + +from sqlalchemy.orm import Session +from sqlalchemy import func + +from app.models.notification import Notification +from app.models.user import User + + +# --------------------------------------------------------------------------- +# Core CRUD +# --------------------------------------------------------------------------- + + +def create_notification( + db: Session, + user_id: uuid.UUID, + type: str, + title: str, + message: str | None = None, + entity_type: str | None = None, + entity_id: uuid.UUID | None = None, +) -> Notification: + """Create a single notification for a user.""" + notif = Notification( + user_id=user_id, + type=type, + title=title, + message=message, + entity_type=entity_type, + entity_id=entity_id, + ) + db.add(notif) + db.commit() + db.refresh(notif) + return notif + + +def mark_as_read(db: Session, notification_id: uuid.UUID, user_id: uuid.UUID) -> bool: + """Mark a single notification as read. Returns True if updated.""" + notif = ( + db.query(Notification) + .filter(Notification.id == notification_id, Notification.user_id == user_id) + .first() + ) + if notif is None: + return False + notif.read = True + db.commit() + return True + + +def mark_all_as_read(db: Session, user_id: uuid.UUID) -> int: + """Mark all unread notifications for a user as read. Returns count updated.""" + count = ( + db.query(Notification) + .filter(Notification.user_id == user_id, Notification.read == False) # noqa: E712 + .update({"read": True}) + ) + db.commit() + return count + + +def get_unread_count(db: Session, user_id: uuid.UUID) -> int: + """Return the number of unread notifications for a user.""" + return ( + db.query(func.count(Notification.id)) + .filter(Notification.user_id == user_id, Notification.read == False) # noqa: E712 + .scalar() + ) or 0 + + +def cleanup_old_notifications(db: Session, days: int = 90) -> int: + """Delete read notifications older than *days*. Returns count deleted.""" + cutoff = datetime.utcnow() - timedelta(days=days) + count = ( + db.query(Notification) + .filter( + Notification.read == True, # noqa: E712 + Notification.created_at < cutoff, + ) + .delete() + ) + db.commit() + return count + + +# --------------------------------------------------------------------------- +# Automatic notification dispatchers +# --------------------------------------------------------------------------- + + +def notify_test_state_change(db: Session, test, new_state: str) -> None: + """Dispatch notifications based on a test's new state. + + Called by the workflow service after each state transition. + + Rules: + - red_executing -> notify creator (confirmation) + - blue_evaluating -> notify all blue_tech users + - in_review -> notify red_lead and blue_lead users + - rejected -> notify creator + - validated -> notify creator + """ + test_name = test.name + test_id = test.id + creator_id = test.created_by + + if new_state == "red_executing" and creator_id: + create_notification( + db, + user_id=creator_id, + type="test_state_changed", + title="Test execution started", + message=f'Your test "{test_name}" has moved to execution phase.', + entity_type="test", + entity_id=test_id, + ) + + elif new_state == "blue_evaluating": + # Notify all blue_tech users + blue_users = db.query(User).filter(User.role == "blue_tech", User.is_active == True).all() # noqa: E712 + for user in blue_users: + create_notification( + db, + user_id=user.id, + type="test_assigned", + title="New test ready for blue evaluation", + message=f'Test "{test_name}" needs blue team evaluation.', + entity_type="test", + entity_id=test_id, + ) + + elif new_state == "in_review": + # Notify red_lead and blue_lead users + managers = ( + db.query(User) + .filter(User.role.in_(["red_lead", "blue_lead"]), User.is_active == True) # noqa: E712 + .all() + ) + for user in managers: + create_notification( + db, + user_id=user.id, + type="validation_needed", + title="Test ready for validation", + message=f'Test "{test_name}" is awaiting your review.', + entity_type="test", + entity_id=test_id, + ) + + elif new_state == "rejected" and creator_id: + create_notification( + db, + user_id=creator_id, + type="test_rejected", + title="Test rejected", + message=f'Your test "{test_name}" has been rejected. Please review and resubmit.', + entity_type="test", + entity_id=test_id, + ) + + elif new_state == "validated" and creator_id: + create_notification( + db, + user_id=creator_id, + type="test_validated", + title="Test validated", + message=f'Your test "{test_name}" has been validated successfully.', + entity_type="test", + entity_id=test_id, + ) diff --git a/backend/app/services/test_workflow_service.py b/backend/app/services/test_workflow_service.py index dd30666..334d8eb 100644 --- a/backend/app/services/test_workflow_service.py +++ b/backend/app/services/test_workflow_service.py @@ -20,6 +20,7 @@ from app.models.enums import TestState from app.models.test import Test from app.models.user import User from app.services.audit_service import log_action +from app.services.notification_service import notify_test_state_change # --------------------------------------------------------------------------- # Valid transition map @@ -91,6 +92,12 @@ def transition_state( details=details, ) + # Dispatch in-app notifications for the new state + try: + notify_test_state_change(db, test, target_state.value) + except Exception: + pass # Notifications are best-effort — don't block the workflow + return test @@ -250,9 +257,17 @@ def check_dual_validation(db: Session, test: Test) -> Test: if red_status == "rejected" or blue_status == "rejected": test.state = TestState.rejected db.commit() + try: + notify_test_state_change(db, test, "rejected") + except Exception: + pass elif red_status == "approved" and blue_status == "approved": test.state = TestState.validated db.commit() + try: + notify_test_state_change(db, test, "validated") + except Exception: + pass else: # One side hasn't voted yet — stay in_review, just flush db.commit() diff --git a/frontend/src/api/notifications.ts b/frontend/src/api/notifications.ts new file mode 100644 index 0000000..22f14b9 --- /dev/null +++ b/frontend/src/api/notifications.ts @@ -0,0 +1,51 @@ +import client from "./client"; + +// ── Types ─────────────────────────────────────────────────────────── + +export interface NotificationItem { + id: string; + user_id: string; + type: string; + title: string; + message: string | null; + entity_type: string | null; + entity_id: string | null; + read: boolean; + created_at: string | null; +} + +export interface UnreadCount { + unread_count: number; +} + +// ── API ───────────────────────────────────────────────────────────── + +/** Fetch notifications for the current user (paginated). */ +export async function getNotifications( + offset = 0, + limit = 20, +): Promise { + const { data } = await client.get( + `/notifications?offset=${offset}&limit=${limit}`, + ); + return data; +} + +/** Get the unread notification count. */ +export async function getUnreadCount(): Promise { + const { data } = await client.get("/notifications/unread-count"); + return data; +} + +/** Mark a single notification as read. */ +export async function markAsRead(id: string): Promise { + const { data } = await client.patch( + `/notifications/${id}/read`, + ); + return data; +} + +/** Mark all notifications as read. */ +export async function markAllAsRead(): Promise { + await client.post("/notifications/read-all"); +} diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index 3df823a..d54c371 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -2,6 +2,7 @@ import { Outlet } from "react-router-dom"; import { LogOut } from "lucide-react"; import { useAuth } from "../context/AuthContext"; import Sidebar from "./Sidebar"; +import NotificationBell from "./NotificationBell"; export default function Layout() { const { user, logout } = useAuth(); @@ -13,6 +14,7 @@ export default function Layout() {
{/* Header */}
+ {user?.username} + + {open && setOpen(false)} />} +
+ ); +} diff --git a/frontend/src/components/NotificationDropdown.tsx b/frontend/src/components/NotificationDropdown.tsx new file mode 100644 index 0000000..2204a45 --- /dev/null +++ b/frontend/src/components/NotificationDropdown.tsx @@ -0,0 +1,139 @@ +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { useNavigate } from "react-router-dom"; +import { + Loader2, + CheckCheck, + FlaskConical, + AlertTriangle, + CheckCircle, + XCircle, + Bell, +} from "lucide-react"; +import { + getNotifications, + markAsRead, + markAllAsRead, + type NotificationItem, +} from "../api/notifications"; + +const typeIcons: Record = { + test_assigned: , + validation_needed: , + test_rejected: , + test_validated: , + test_state_changed: , +}; + +export default function NotificationDropdown({ onClose }: { onClose: () => void }) { + const navigate = useNavigate(); + const queryClient = useQueryClient(); + + const { data: notifications, isLoading } = useQuery({ + queryKey: ["notifications", "list"], + queryFn: () => getNotifications(0, 20), + }); + + const markReadMutation = useMutation({ + mutationFn: markAsRead, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["notifications"] }); + }, + }); + + const markAllMutation = useMutation({ + mutationFn: markAllAsRead, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["notifications"] }); + }, + }); + + const handleClick = (notif: NotificationItem) => { + if (!notif.read) { + markReadMutation.mutate(notif.id); + } + if (notif.entity_type === "test" && notif.entity_id) { + navigate(`/tests/${notif.entity_id}`); + } else if (notif.entity_type === "technique" && notif.entity_id) { + navigate(`/techniques/${notif.entity_id}`); + } + onClose(); + }; + + const formatTime = (dateStr: string | null) => { + if (!dateStr) return ""; + const d = new Date(dateStr); + const now = new Date(); + const diffMs = now.getTime() - d.getTime(); + const diffMin = Math.floor(diffMs / 60000); + if (diffMin < 1) return "just now"; + if (diffMin < 60) return `${diffMin}m ago`; + const diffH = Math.floor(diffMin / 60); + if (diffH < 24) return `${diffH}h ago`; + const diffD = Math.floor(diffH / 24); + return `${diffD}d ago`; + }; + + return ( +
+ {/* Header */} +
+

Notifications

+ +
+ + {/* List */} +
+ {isLoading ? ( +
+ +
+ ) : notifications && notifications.length > 0 ? ( + notifications.map((notif) => ( + + )) + ) : ( +
+ No notifications yet +
+ )} +
+
+ ); +}