Fix all 45 audited bugs from bug-fixes.md

Verified every finding against the code, then fixed correctness (redirects,
advancement reset, duplicate heats, high-jump attempt entry, placement
ranking with tie handling, delete-dependency 500s), security (secrets out of
config, config-driven admin seed, login lockout, role authorization with
school scoping, heat-time IDOR, forwarded headers, last-admin guards),
schema integrity (nullable+filtered ExistingStudentId, unique indexes for
rounds/heats/bar heights via SchemaIntegrityFixes migration), performance
(N+1 removal in high jump/reports/standings/dashboard, SQL-side student
paging), and hygiene (duplicate notifications, auto-dismiss scope, local
bootstrap-icons, orphaned files, test-data.sql tournament creation).

FluentValidation is now registered; AutoMapper bumped to 14.0.0 (advisory
fully patched only in licence-changed 15.1.1 — documented). 11 new tests;
63/63 passing. Credential rotation and deploy-time DB_CONNECTION_STRING are
required manual follow-ups, documented in bug-fixes.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-11 08:30:53 -04:00
parent 78c526ee35
commit 810f721e48
93 changed files with 3251 additions and 1606 deletions

View File

@@ -1,29 +1,55 @@
@model EventRegistrationCreateDto
@{
ViewData["Title"] = "Register Student";
var students = ViewBag.Students as IEnumerable<StudentDto>;
var telId = ViewBag.TournamentEventLevelId as int?;
var eventName = ViewBag.EventName as string;
var levelName = ViewBag.LevelName as string;
var isEligible = ViewBag.IsEligible as bool?;
var eligibilityReason = ViewBag.EligibilityReason as string;
var selectedStudentId = Model?.StudentId;
}
<h2>Register Student</h2>
<p class="text-muted">@eventName - @levelName</p>
@if (!string.IsNullOrEmpty(eventName))
{
<p class="text-muted">@eventName - @levelName</p>
}
<hr />
<partial name="_Notification" />
<div class="row">
<div class="col-md-6">
<form asp-action="Register" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<input type="hidden" name="TournamentEventLevelId" value="@telId" />
<div class="mb-3">
<label class="form-label">Student</label>
<select name="StudentId" class="form-select">
@* Re-request the page with the chosen student so eligibility is checked before submitting. *@
<select name="StudentId" class="form-select"
onchange="window.location = '@Url.Action("Register", new { tournamentEventLevelId = telId })&studentId=' + this.value">
<option value="">Select Student</option>
@if (students != null) { @foreach (var s in students) { <option value="@s.StudentId">@s.FullName (@s.SchoolName) - @s.Sex</option> } }
@if (students != null)
{
@foreach (var s in students)
{
<option value="@s.StudentId" selected="@(selectedStudentId == s.StudentId)">@s.FullName (@s.SchoolName) - @s.Sex</option>
}
}
</select>
</div>
<button type="submit" class="btn btn-primary">Register</button>
@if (isEligible == true)
{
<div class="alert alert-success py-2">
<i class="bi bi-check-circle"></i> This student is eligible for this event level.
</div>
}
else if (isEligible == false)
{
<div class="alert alert-danger py-2">
<i class="bi bi-x-circle"></i> Not eligible: @eligibilityReason
</div>
}
<button type="submit" class="btn btn-primary" disabled="@(isEligible == false)">Register</button>
<a asp-action="Index" asp-route-tournamentEventLevelId="@telId" class="btn btn-secondary">Cancel</a>
</form>
</div>