📋 AUDITORÍA DE PLATAFORMA NEXUS - PROBLEMAS A REVISAR¶
Fecha: 2026-01-30 Versión: 1.0 Estado: Pendiente de revisión
Índice¶
- Resumen Ejecutivo
- Problemas Críticos de Seguridad
- Problemas de Calidad de Código
- Problemas de Base de Datos
- Problemas de Arquitectura
- Plan de Remediación
- 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:
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:
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:
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:
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:
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).
DB-006: DocumentContextLink Sin Versionado¶
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.
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¶
- CopilotService.cs - 1,194 líneas, múltiples responsabilidades
- PowerShellExecutor.cs - Duplicación severa
- ClickUpSyncService.cs - 977 líneas
- SchedulerWorker.cs - 648 líneas
- 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