Not exploitable today - the only custom auth message data is a
hardcoded resend link - but |raw on a translated exception message is
a latent XSS pattern if a future change ever threads user input
through the auth exception's message data. Twig's default
autoescaping is sufficient here.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New /profile route (nav link between Admin and Logout) with two
independent forms, both requiring the current password before
applying a change:
- Change password: standard current/new/repeat flow, same password
strength rules as registration.
- Change email: re-checks the new address isn't already taken (avoids
a 500 from the unique constraint), then marks the account
unverified and re-sends the confirmation email via the existing
EmailVerifier/verify-email flow, same as registration. The current
session stays logged in, but the user needs to verify the new
address before their next login (UserChecker already blocks
unverified accounts).
Registration now requires agreeing to the Terms and Conditions and lets
users opt in to hear about future projects. The opt-in is persisted on
the user via a new marketing_opt_in column (migration included).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
confirmation_email.html.twig and reset_password/email.html.twig were
plain unstyled HTML with no shared structure. Extract a table-based
layout (templates/emails/layout.html.twig) with header/logo, content
block, and footer, so future transactional emails can extend it
instead of starting from scratch.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>