Saltar a contenido

📋 AUDITORÍA DE PLATAFORMA NEXUS - PROBLEMAS A REVISAR

Fecha: 2026-01-30 Versión: 1.0 Estado: Pendiente de revisión


Índice

  1. Resumen Ejecutivo
  2. Problemas Críticos de Seguridad
  3. Problemas de Calidad de Código
  4. Problemas de Base de Datos
  5. Problemas de Arquitectura
  6. Plan de Remediación
  7. Métricas y Deuda Técnica

Resumen Ejecutivo

Métricas Generales

Métrica Valor
Proyectos .NET 8 principales + 15 scripts
Entidades DB 45+
Servicios 46
Controllers API 38
Componentes Blazor 50+
Líneas de código estimadas 50,000+

Stack Tecnológico

  • .NET 9.0 (C# 12)
  • Blazor Server + MudBlazor 7.15.0
  • PostgreSQL 16 + EF Core 9.0
  • RabbitMQ 3 para eventos
  • SignalR para real-time
  • Sentry para error tracking

Matriz de Riesgos

Categoría Crítico Alto Medio Bajo
Seguridad 4 4 5 4
Calidad Código 2 5 6 3
Base de Datos 0 2 4 2
Arquitectura 0 1 3 2
TOTAL 6 12 18 11

🔴 PROBLEMAS CRÍTICOS DE SEGURIDAD

SEC-001: Secretos Expuestos en Código Fuente

Severidad: CRÍTICA Archivo: Orchestrator/src/Orchestrator.Api/appsettings.json Estado: ⏳ Pendiente

Descripción: Se encontraron múltiples secretos hardcodeados directamente en archivos de configuración que están en el repositorio:

// EXPUESTOS EN REPOSITORIO:
{
  "ConnectionStrings": {
    "NexusDb": "Host=localhost;...Password=6e43b64b5fe04d69bfd46086..."
  },
  "RabbitMq": {
    "ConnectionString": "amqp://admin:admin@localhost:5672/"
  },
  "Encryption": {
    "Key": "0221b2e45b9e4e03b064c41626620928"
  },
  "Claude": {
    "ApiKey": "sk-ant-api03-..."
  },
  "ClickUp": {
    "ApiToken": "pk_93835201_...",
    "WebhookSecret": "..."
  },
  "Smtp": {
    "Password": "..."
  }
}

Impacto: Compromiso total de credenciales si el repositorio es accedido por terceros.

Remediación: 1. Rotar TODOS los secretos inmediatamente 2. Mover a variables de entorno o Azure Key Vault 3. Agregar appsettings.json a .gitignore (mantener solo appsettings.example.json)


SEC-002: Hash de Contraseña Débil

Severidad: CRÍTICA Archivo: Orchestrator/src/Orchestrator.Admin/Program.cs:284-296 Estado: ⏳ Pendiente

Descripción:

static string HashPassword(string password)
{
    using var sha256 = SHA256.Create();
    var saltedPassword = $"NexusSalt2024_{password}_SecureHash";
    var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(saltedPassword));
    return Convert.ToBase64String(bytes);
}

Problemas: - Salt fijo y predecible ("NexusSalt2024") - Sin iteraciones - vulnerable a ataques de fuerza bruta - SHA256 no es apropiado para contraseñas

Remediación:

// Usar BCrypt.Net-Next
using BCrypt.Net;

string HashPassword(string password) => BCrypt.HashPassword(password, workFactor: 12);
bool VerifyPassword(string password, string hash) => BCrypt.Verify(password, hash);


SEC-003: Usuario Admin Hardcodeado

Severidad: CRÍTICA Archivo: Orchestrator/src/Orchestrator.Admin/Program.cs:119-159 Estado: ⏳ Pendiente

Descripción:

var adminEmail = "ramac21@gmail.com";
var password = "Nexus2024!";  // ← CONTRASEÑA EN CÓDIGO

Remediación: 1. Eliminar código de creación automática de admin 2. Crear script de setup inicial separado 3. Forzar cambio de contraseña en primer login


SEC-004: Claves de Encriptación Débiles

Severidad: CRÍTICA Archivo: Orchestrator/src/Orchestrator.Api/Program.cs:112-115 Estado: ⏳ Pendiente

Descripción:

var encryptionKey = builder.Configuration["Encryption:Key"]
    ?? "nexus-dev-key-change-in-production-2026";  // Fallback débil

Remediación: - Eliminar fallback - Fallar en startup si no hay key configurada - Usar derivación de clave (PBKDF2)


SEC-005: Autorización Débil (X-User-Id Spoofeable)

Severidad: ALTA Archivo: Múltiples Controllers Estado: ⏳ Pendiente

Descripción:

private Guid? GetUserId()
{
    var headerUserId = Request.Headers["X-User-Id"].FirstOrDefault();
    if (Guid.TryParse(headerUserId, out var headerGuid))
        return headerGuid;  // Sin validación contra JWT
    // ...
}

Impacto: Un cliente malicioso puede spoofear su UserId y acceder a datos de otros usuarios.

Remediación:

private Guid? GetUserId()
{
    // Priorizar claims del token JWT autenticado
    var claim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    if (Guid.TryParse(claim, out var claimGuid))
        return claimGuid;

    // Solo permitir X-User-Id si viene de servicio interno autenticado
    if (User.IsInRole("InternalService"))
    {
        var headerUserId = Request.Headers["X-User-Id"].FirstOrDefault();
        if (Guid.TryParse(headerUserId, out var headerGuid))
            return headerGuid;
    }

    return null;
}


SEC-006: CORS Muy Permisivo

Severidad: ALTA Archivo: Orchestrator/src/Orchestrator.Mcp.Remote/Program.cs:157 Estado: ⏳ Pendiente

Descripción:

policy.AllowAnyOrigin()
    .AllowAnyMethod()
    .AllowAnyHeader();

Remediación:

policy.WithOrigins(
    "https://admin.nexus.local",
    "https://app.nexus.local"
)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();


SEC-007: Validación de Webhooks Opcional

Severidad: ALTA Archivo: Orchestrator/src/Orchestrator.Api/Controllers/WebhooksController.cs Estado: ⏳ Pendiente

Descripción:

if (!string.IsNullOrEmpty(secret))  // Validación opcional
{
    if (!ValidateClickUpSignature(body, signature, secret))
        return Unauthorized();
}
// Si no hay secret, acepta cualquier webhook

Remediación: Hacer la validación obligatoria, fallar si no hay secret configurado.


SEC-008: XSS Potencial en Blazor (MarkupString)

Severidad: MEDIA Archivos: 9 componentes Blazor Estado: ⏳ Pendiente

Descripción:

return Markdig.Markdown.ToHtml(markdown);  // Sin sanitización
// ...
@((MarkupString)RenderedHtml)  // Renderizado directo

Archivos afectados: - BacklogDetail.razor - CommandDetail.razor - SkillDetail.razor - ConversationArea.razor - ProcedurePreview.razor - MarkdownViewer.razor - PromptEditor.razor - ProjectGitPanel.razor

Remediación:

using Ganss.Xss;

private static readonly HtmlSanitizer _sanitizer = new();

private string RenderMarkdown(string markdown)
{
    var html = Markdig.Markdown.ToHtml(markdown);
    return _sanitizer.Sanitize(html);
}


SEC-009: Tokens con Expiración Muy Larga

Severidad: MEDIA Archivo: Orchestrator/src/Orchestrator.Api/Options/TunnelOptions.cs:29 Estado: ⏳ Pendiente

Descripción:

public int TokenExpirationDays { get; set; } = 365;  // 1 año
// También hay opción de 100 años (efectivamente sin expiración)

Remediación: Reducir a 24-48 horas con refresh tokens.


SEC-010: Falta Rate Limiting

Severidad: MEDIA Archivo: Global Estado: ⏳ Pendiente

Descripción: No hay rate limiting en ningún endpoint, especialmente webhooks.

Remediación:

builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("webhook", opt =>
    {
        opt.PermitLimit = 100;
        opt.Window = TimeSpan.FromMinutes(1);
    });
});


SEC-011: Cookies Sin Secure Flag

Severidad: MEDIA Archivo: Orchestrator/src/Orchestrator.Admin/Program.cs:56-66 Estado: ⏳ Pendiente

Descripción:

.AddCookie(options =>
{
    // Falta: options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});


SEC-012: Falta Security Headers

Severidad: BAJA Archivo: Global Estado: ⏳ Pendiente

Headers faltantes: - X-Content-Type-Options: nosniff - X-Frame-Options: DENY - X-XSS-Protection: 1; mode=block - Strict-Transport-Security - Content-Security-Policy


🟠 PROBLEMAS DE CALIDAD DE CÓDIGO

CODE-001: Memory Leak en CopilotService

Severidad: ALTA Archivo: Shared/Shared.Admin/Services/CopilotService.cs:56 Estado: ⏳ Pendiente

Descripción:

private static readonly ConcurrentDictionary<Guid, SemaphoreSlim> _executionLocks = new();
// Locks NUNCA se disponen - memory leak después de 1000+ ejecuciones

Comparación con DevSessionService (que sí lo hace bien):

private static void ReleaseSessionLock(Guid sessionId)
{
    if (_sessionLocks.TryRemove(sessionId, out var semaphore))
        semaphore?.Dispose();
}

Remediación: Implementar cleanup similar al de DevSessionService.


CODE-002: Código Duplicado Masivo en PowerShellExecutor

Severidad: ALTA Archivo: Orchestrator/src/Orchestrator.Workers/Services/PowerShellExecutor.cs Estado: ⏳ Pendiente

Descripción: - ExecuteProcedureAsync(): 182 líneas (29-217) - ExecuteTaskAsync(): 183 líneas (232-421) - 95% código idéntico

Remediación: Extraer método base común ExecuteScriptAsync().


CODE-003: Clases Demasiado Grandes (God Classes)

Severidad: ALTA Estado: ⏳ Pendiente

Clase Líneas Responsabilidades
CopilotService.cs 1,194 Ciclo de vida, ejecución, evaluación, DTOs, prompts, notificaciones
ClickUpSyncService.cs 977 CRUD tareas, webhooks, batch sync, logging, mapeo estados
SchedulerWorker.cs 648 Scheduling, ejecución, dependencias, notificaciones
ExecutionService.cs 424 Queries, estadísticas, timeline, creación, parsing

Remediación: Dividir en servicios más pequeños y focalizados.


CODE-004: Fire-and-Forget Tasks Sin Control

Severidad: ALTA Archivo: CopilotService.cs Estado: ⏳ Pendiente

Descripción:

_ = Task.Run(() => GeneratePlanAndContinueAsync(execution.Id, ct), ct);  // Línea 134
_ = Task.Run(() => ExecuteNextStepAsync(execution.Id, ct), ct);          // Línea 186
_ = Task.Run(() => ExecuteNextStepAsync(execution.Id, ct), ct);          // Línea 240
_ = Task.Run(() => GeneratePlanAndContinueAsync(execution.Id, ct), ct);  // Línea 342

Problemas: - Excepciones silenciadas - Sin tracking de completitud - Difícil debugging

Remediación: Usar IHostedService o BackgroundTaskQueue.


CODE-005: Falta ConfigureAwait(false) en Librerías

Severidad: ALTA Archivo: Todos los servicios en Shared.Admin Estado: ⏳ Pendiente

Descripción: Ningún uso de .ConfigureAwait(false) en métodos async de librerías.

Impacto: Deadlocks potenciales en contextos con SynchronizationContext.

Remediación: Agregar .ConfigureAwait(false) a todos los await en servicios compartidos.


CODE-006: Magic Strings Sin Constantes

Severidad: MEDIA Estado: ⏳ Pendiente

Ubicación Magic String
CopilotService.cs:89-93 "Completed", "Cancelled", "Error"
ExecutionService.cs:206 "running"
ClickUpSyncService.cs:49 "https://api.clickup.com/api/v2"
SchedulerWorker.cs:22 TimeSpan.FromMinutes(1)
PowerShellExecutor.cs:180 TimeSpan.FromMinutes(30)

Remediación: Crear clase Constants o usar IOptions<T>.


CODE-007: Métodos Muy Largos

Severidad: MEDIA Estado: ⏳ Pendiente

Método Líneas Archivo
GenerateOrchestratorMarkdownAsync 204 OrchestratorController.cs
CheckAndExecuteTasksAsync 109 SchedulerWorker.cs
ExecuteNextStepAsync 99 CopilotService.cs

CODE-008: Bare Catch Blocks

Severidad: MEDIA Archivo: PowerShellExecutor.cs:346 Estado: ⏳ Pendiente

Descripción:

catch { }  // Sin logging ni manejo - MUY MALO


CODE-009: Inconsistencia en Estados (String vs Enum)

Severidad: MEDIA Estado: ⏳ Pendiente

Servicio Tipo de Estado
CopilotService CopilotState enum
ExecutionService String: "running", "completed"
DevSessionService String: "active", "archived"

Remediación: Crear ExecutionStatus enum compartido.


CODE-010: Encriptación con Derivación de Clave Débil

Severidad: MEDIA Archivo: Shared/Shared.Admin/Services/SecretEncryptionService.cs:17-25 Estado: ⏳ Pendiente

Descripción:

using var sha256 = SHA256.Create();
_key = sha256.ComputeHash(Encoding.UTF8.GetBytes(encryptionKey));
// Hash directo, sin PBKDF2 o derivación apropiada

Remediación:

using var pbkdf2 = new Rfc2898DeriveBytes(
    encryptionKey,
    salt: Encoding.UTF8.GetBytes("NexusSecretsSalt"),
    iterations: 100000,
    HashAlgorithmName.SHA256);
_key = pbkdf2.GetBytes(32);


🟡 PROBLEMAS DE BASE DE DATOS

DB-001: Timestamp Sin Zona Horaria

Severidad: MEDIA Entidad: BacklogItem.ConvertedAt Estado: ⏳ Pendiente

Descripción:

ConvertedAt timestamp without time zone  -- Debería ser TIMESTAMPTZ

Impacto: Inconsistencias en reportes multi-región.


DB-002: AIProviderConfig Sin Constraint XOR

Severidad: MEDIA Entidad: AIProviderConfig Estado: ⏳ Pendiente

Descripción: OrganizationId XOR UserId sin constraint en base de datos.

Remediación:

ALTER TABLE ai_provider_configs
ADD CONSTRAINT chk_owner CHECK (
    (organization_id IS NOT NULL AND user_id IS NULL) OR
    (organization_id IS NULL AND user_id IS NOT NULL)
);


DB-003: McpRequestLog Sin Particionamiento

Severidad: MEDIA Entidad: McpRequestLog Estado: ⏳ Pendiente

Descripción: Tabla crece 1M-10M registros/día sin política de retención.

Remediación: Implementar particionamiento por fecha y job de purga.


DB-004: ScheduledTask Muy Compleja

Severidad: MEDIA Entidad: ScheduledTask (73 propiedades) Estado: ⏳ Pendiente

Remediación: Dividir en sub-entidades: - ScheduledTaskConfig - ScheduledTaskRetry - ScheduledTaskSchedule


DB-005: Falta Tabla de Auditoría

Severidad: BAJA Estado: ⏳ Pendiente

Descripción: No hay trazabilidad de cambios en entidades críticas (User, Organization, Project).


Severidad: BAJA Entidad: DocumentContextLink Estado: ⏳ Pendiente


DB-007: SystemMetrics Sin Retención

Severidad: BAJA Entidad: SystemMetrics Estado: ⏳ Pendiente

Descripción: Tabla crece indefinidamente.


🔵 PROBLEMAS DE ARQUITECTURA

ARCH-001: Falta Circuit Breaker para APIs Externas

Severidad: ALTA Estado: ⏳ Pendiente

Descripción: No hay circuit breaker para: - Claude API - ClickUp API - Google Sheets API

Remediación: Implementar Polly con circuit breaker.


ARCH-002: RabbitMQ Connection No Reutilizada

Severidad: MEDIA Estado: ⏳ Pendiente

Descripción: Se crea nueva conexión por cada request.

await using var rabbitConnection = await RabbitMqConnection.CreateAsync(connectionString);

Remediación: Usar connection pooling o singleton.


ARCH-003: Falta Health Checks Completos

Severidad: MEDIA Estado: ⏳ Pendiente

Descripción: Health checks básicos, falta: - PostgreSQL - RabbitMQ - Redis (si se implementa) - APIs externas


ARCH-004: Sin Retry Logic Automático

Severidad: MEDIA Estado: ⏳ Pendiente

Descripción: Fallos de red no se reintentan automáticamente.


🎯 PLAN DE REMEDIACIÓN

Fase 1: Inmediato (1-2 días)

  • [ ] SEC-001: Rotar todos los secretos
  • [ ] SEC-001: Mover secretos a variables de entorno
  • [ ] SEC-002: Implementar bcrypt para contraseñas
  • [ ] SEC-003: Eliminar admin hardcodeado

Fase 2: Corto plazo (1-2 semanas)

  • [ ] SEC-005: Validar X-User-Id contra JWT
  • [ ] SEC-006: Restringir CORS
  • [ ] CODE-001: Corregir memory leak en CopilotService
  • [ ] SEC-010: Agregar rate limiting
  • [ ] SEC-008: Implementar HTML sanitization

Fase 3: Mediano plazo (2-4 semanas)

  • [ ] CODE-002: Refactorizar PowerShellExecutor
  • [ ] CODE-003: Dividir clases grandes
  • [ ] CODE-005: Agregar ConfigureAwait(false)
  • [ ] DB-003: Implementar particionamiento McpRequestLog
  • [ ] SEC-012: Agregar security headers

Fase 4: Largo plazo (1-2 meses)

  • [ ] DB-004: Simplificar ScheduledTask
  • [ ] DB-005: Implementar auditoría
  • [ ] ARCH-001: Agregar circuit breaker
  • [ ] ARCH-002: Connection pooling RabbitMQ

📊 MÉTRICAS Y DEUDA TÉCNICA

Deuda Técnica Estimada

Área Deuda Esfuerzo
Seguridad Alta 2-3 días
Refactoring Código Media-Alta 1-2 semanas
Base de Datos Media 3-5 días
Testing Alta (no analizado) 2-4 semanas

Archivos con Mayor Deuda

  1. CopilotService.cs - 1,194 líneas, múltiples responsabilidades
  2. PowerShellExecutor.cs - Duplicación severa
  3. ClickUpSyncService.cs - 977 líneas
  4. SchedulerWorker.cs - 648 líneas
  5. ExecutionService.cs - Parsing y análisis mezclado

Historial de Cambios

Fecha Versión Cambios
2026-01-30 1.0 Auditoría inicial completa

Documento generado automáticamente por auditoría de código