Follow-up to the SEC-22 stored-XSS fix in #231.
SEC-22 was fixed at the correct layer — output encoding in the frontend (shared quote-safe escapeHtml). This issue tracks the complementary, defence-in-depth input validation on the server. It is deliberately not input HTML-sanitisation: the value must still be stored raw and encoded at each output. See docs/security-output-encoding.md for the rationale (encoding on write would corrupt the CSV export, audit log, API responses, and name sorting/matching, and would not remove the render-time escape).
Scope
Add lightweight validation to the user-entered comp text fields in web/workers/competition-api/src/validators.ts:
registered_pilot_name, team_name, pilot_class, default_pilot_class, comp/task name, driver_contact, glider, phone, emergency_contact_name, emergency_contact_phone.
Proposed rules (Zod refinements):
Constraints
- Length bounds already exist (
z.string().min(1).max(128)) — keep them.
- Any rejection must surface to the user as a clear validation error, not a silent edit of their data.
- Do not HTML-encode on input, and do not remove any frontend output encoding — this is additive only.
Not in scope (separate SEC-05 close-out)
- Consolidating the 7 per-file frontend
escapeHtml copies onto web/frontend/src/escape-html.ts.
- A lint rule forbidding
innerHTML = with interpolated non-constant template literals.
Refs: PR #231, docs/security-review.md (SEC-22, SEC-05), docs/security-output-encoding.md.
Follow-up to the SEC-22 stored-XSS fix in #231.
SEC-22 was fixed at the correct layer — output encoding in the frontend (shared quote-safe
escapeHtml). This issue tracks the complementary, defence-in-depth input validation on the server. It is deliberately not input HTML-sanitisation: the value must still be stored raw and encoded at each output. Seedocs/security-output-encoding.mdfor the rationale (encoding on write would corrupt the CSV export, audit log, API responses, and name sorting/matching, and would not remove the render-time escape).Scope
Add lightweight validation to the user-entered comp text fields in
web/workers/competition-api/src/validators.ts:registered_pilot_name,team_name,pilot_class,default_pilot_class, comp/taskname,driver_contact,glider,phone,emergency_contact_name,emergency_contact_phone.Proposed rules (Zod refinements):
\x00-\x1f\x7f) — never legitimate in these fields.<or>in name-type fields. Real names/teams/classes never contain them, so this shrinks the attack surface without manglingO'BrienorMüller. Do not strip silently, and do not touch",', or&— those are legitimate and must pass through (output encoding still handles them).Constraints
z.string().min(1).max(128)) — keep them.Not in scope (separate SEC-05 close-out)
escapeHtmlcopies ontoweb/frontend/src/escape-html.ts.innerHTML =with interpolated non-constant template literals.Refs: PR #231,
docs/security-review.md(SEC-22, SEC-05),docs/security-output-encoding.md.