Files
budget/src/Budget.Infrastructure/Data/Migrations/20260507024323_AddHardeningIndexes.cs
T
Spencer Twaddle ac3dcc2f31 Security & resource hardening: eliminate CPU/disk attack surface
Addresses production CPU spike incident. Key changes:

- Guard OTel exporter behind OTEL_EXPORTER_OTLP_ENDPOINT env var; filter
  tracing to /api paths only — unconditional export was primary suspect
- Remove /healthz endpoint entirely (unauthenticated, hit DB on every call)
- Replace KnownUserMiddleware with POST /api/users/me called once on login
  from TokenSync — eliminates unconditional DB write on every request
- Add DB indexes: (BudgetId, IsDeleted) on Incomes/Outgos, OwnerUserId on
  Budgets, SharedWithUserId and (IsPending, SharedWithEmail) on BudgetShares
- Move UseRateLimiter() before UseStaticFiles() so all requests are throttled
- Replace full-array reorder with move-by-position (id + newIndex) — bounded
  input, fewer DB writes, better API design
- Lock ForwardedHeaders to 172.20.0.0/16 subnet; fixes KnownNetworks
  deprecation warning (0 warnings in build now)
- Add AsNoTracking() to all read-only queries in Summary/Incomes/OutgosController
- FrequencyCalculator returns 0 for unknown enum values instead of throwing
- Thread.Sleep → await Task.Delay in OIDC startup loop
- AllowedHosts locked to budget.stwaddle.com

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 22:17:18 -05:00

82 lines
2.6 KiB
C#

using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Budget.Infrastructure.Data.Migrations
{
/// <inheritdoc />
public partial class AddHardeningIndexes : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_Outgos_BudgetId",
table: "Outgos");
migrationBuilder.DropIndex(
name: "IX_Incomes_BudgetId",
table: "Incomes");
migrationBuilder.CreateIndex(
name: "IX_Outgos_BudgetId_IsDeleted",
table: "Outgos",
columns: new[] { "BudgetId", "IsDeleted" });
migrationBuilder.CreateIndex(
name: "IX_Incomes_BudgetId_IsDeleted",
table: "Incomes",
columns: new[] { "BudgetId", "IsDeleted" });
migrationBuilder.CreateIndex(
name: "IX_BudgetShares_IsPending_SharedWithEmail",
table: "BudgetShares",
columns: new[] { "IsPending", "SharedWithEmail" });
migrationBuilder.CreateIndex(
name: "IX_BudgetShares_SharedWithUserId",
table: "BudgetShares",
column: "SharedWithUserId");
migrationBuilder.CreateIndex(
name: "IX_Budgets_OwnerUserId",
table: "Budgets",
column: "OwnerUserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_Outgos_BudgetId_IsDeleted",
table: "Outgos");
migrationBuilder.DropIndex(
name: "IX_Incomes_BudgetId_IsDeleted",
table: "Incomes");
migrationBuilder.DropIndex(
name: "IX_BudgetShares_IsPending_SharedWithEmail",
table: "BudgetShares");
migrationBuilder.DropIndex(
name: "IX_BudgetShares_SharedWithUserId",
table: "BudgetShares");
migrationBuilder.DropIndex(
name: "IX_Budgets_OwnerUserId",
table: "Budgets");
migrationBuilder.CreateIndex(
name: "IX_Outgos_BudgetId",
table: "Outgos",
column: "BudgetId");
migrationBuilder.CreateIndex(
name: "IX_Incomes_BudgetId",
table: "Incomes",
column: "BudgetId");
}
}
}