searchTerm recibido de la query string se concatena directamente en la consulta SQL mediante interpolación de cadena ($"..."). Un atacante puede inyectar SQL arbitrario para extraer, modificar o eliminar datos de pacientes.SqlParameter o migrar a EF Core con LINQ donde la parametrización es automática. Nunca incluir input del usuario en el texto de la consulta.var query = $"SELECT * FROM Patients WHERE Name LIKE '%{searchTerm}%' OR DNI = '{searchTerm}'"; var cmd = new SqlCommand(query, conn); var results = await cmd.ExecuteReaderAsync();
var query = @"SELECT * FROM Patients WHERE Name LIKE @search OR DNI = @dni"; var cmd = new SqlCommand(query, conn); cmd.Parameters.AddWithValue("@search", $"%{searchTerm}%"); cmd.Parameters.AddWithValue("@dni", searchTerm); var results = await cmd.ExecuteReaderAsync();
outputPath proviene directamente del body de la petición HTTP y se usa sin validar en File.WriteAllBytesAsync. Un atacante puede escribir archivos fuera del directorio permitido usando secuencias ../../../ o rutas absolutas.Path.GetFullPath y verificar que la ruta resultante empieza con el directorio base permitido. Idealmente, no recibir rutas del cliente: generar el nombre del archivo en el servidor.public async Task ExportAsync( string outputPath, ReportData data) { var pdf = GeneratePdf(data); // ⚠️ sin validar outputPath await File.WriteAllBytesAsync( outputPath, pdf); }
private const string _allowedBase = "/var/app/exports"; public async Task ExportAsync( string fileName, ReportData data) { var fullPath = Path.GetFullPath( Path.Combine(_allowedBase, fileName)); if (!fullPath.StartsWith(_allowedBase)) throw new UnauthorizedAccessException(); await File.WriteAllBytesAsync(fullPath, pdf); }
BinaryFormatter está obsoleto en .NET 5+ y deshabilitado por defecto en .NET 7+. Deserializar un archivo binario del usuario con él permite ejecución remota de código (RCE). Además, hay un aviso en el compilador que se ignoró con #pragma warning disable.BinaryFormatter. Para CSV, usar CsvHelper o StreamReader con parseo manual. Para datos binarios, usar System.Text.Json o MessagePack.#pragma warning disable SYSLIB0011 var formatter = new BinaryFormatter(); using var stream = File.OpenRead(filePath); var plan = (NutritionPlan)formatter .Deserialize(stream); // ⚠️ RCE #pragma warning restore SYSLIB0011
using var reader = new StreamReader(filePath); using var csv = new CsvReader(reader, CultureInfo.InvariantCulture); var records = csv .GetRecords<NutritionPlanDto>() .ToList();
GetPatientsAsync().Result bloquea el hilo del pool de threads. En ASP.NET Core con SynchronizationContext activo puede causar deadlock; en todos los casos desperdicia el hilo durante la espera de I/O.async Task<IActionResult> y usar await.public IActionResult Search(string q) { var results = _service .GetPatientsAsync(q).Result; // ⚠️ return Ok(results); }
public async Task<IActionResult> Search( string q, CancellationToken ct) { var results = await _service .GetPatientsAsync(q, ct); return Ok(results); }
ImportAsync y ExportAsync no aceptan CancellationToken. Si el cliente cancela la petición HTTP, las operaciones de archivo y base de datos seguirán ejecutándose, consumiendo recursos innecesariamente.CancellationToken ct = default como último parámetro y propagarlo a todas las llamadas async internas (ReadToEndAsync(ct), ExecuteReaderAsync(ct)...).patient!.Name, result!.Data y record!.DNI con el operador ! de supresión de nullable en tres puntos. Esto silencia las advertencias del compilador sin resolver el problema subyacente; si cualquiera de esos objetos es null en runtime, se producirá un NullReferenceException.if (patient is null) return NotFound(); o usar el patrón patient?.Name ?? string.Empty según corresponda.catch (Exception) { return null; } alrededor del loop de importación CSV. Si falla la lectura de una línea, se devuelve null sin registrar nada. El dietista nunca sabe que la importación fue parcial.CsvHelperException, FormatException), loguear con contexto (_logger.LogError(ex, "Fila {Row}", rowIndex)) y relanzar o devolver un resultado de error tipado.patient.NutritionPlans dentro de un foreach, disparando una query adicional por paciente. Con 100 resultados → 101 queries a base de datos..Include(p => p.NutritionPlans) en la query EF Core original. Añadir .AsNoTracking() ya que es una consulta de solo lectura.htmlContent += row dentro de un loop de hasta 1.000 filas de datos nutricionales. Cada concatenación crea un nuevo objeto string en el heap → O(n²) en memoria y tiempo.var sb = new StringBuilder(); sb.Append(row); y sb.ToString() al final del loop."ReportSettings:OutputPath" y "ReportSettings:MaxSizeMB" están duplicadas como literales en 3 sitios. Un typo en una de ellas es silencioso en compilación.ReportSettings con string OutputPath y registrarla con services.Configure<ReportSettings>(config.GetSection("ReportSettings")). Inyectar IOptions<ReportSettings>.sealed. El compilador puede optimizar mejor los métodos virtuales en clases sealed.sealed: public sealed class NutritionPlanImporter.NutritionPlanDto es una clase con setters públicos. Al ser un DTO de solo lectura (solo se lee del CSV), debería ser inmutable.public record NutritionPlanDto(string Name, decimal Calories, ...).