Files
UmbraTrans/web/verify-email.html
umbra audio 3419485aa7 Fix a syntax error I introduced, which put a password in a URL
The commit immediately before this one - the password-toggle fix - deleted a closing
brace and did not replace it. app.js did not parse. The browser reported it in a console
nobody was watching, and every script-driven behaviour on the sign-in page stopped.

The consequence was not a cosmetic one. The login form's submit handler, which is the
only thing calling preventDefault(), never ran, so the browser performed a native form
submission. Neither form declared a method, and a form with no method attribute submits
as GET. Both password fields went into the query string:

    GET /?email=umbra%40bdmurphy.me&password=...

The server logged it in plain text, three times, before it was noticed. It would also
have sat in browser history and in any proxy access log between here and the public
internet.

Two fixes, the second of which is the one that matters:

- The toggle fix is reapplied correctly to the last known-good version of app.js, with
  nothing else in the file touched - the diff is four lines against thirteen and two
  against one.
- **Every form on every page now declares method=post.** The script still intercepts
  and submits via fetch; this decides what happens when the script does not run, which is
  precisely the case that failed. Five more forms carried the same latent defect,
  including password reset and email verification.

test_form_safety.py checks both, and was confirmed to fail when app.js is broken the
same way. It uses esprima rather than a hand-written brace counter, which gave two false
readings during this work - one of them 'balanced' for a file that did not parse at all.
A check that can be wrong about a syntax error is worse than no check.

The account password should be treated as exposed and changed. So should any password
entered into that form while the script was broken.
2026-10-03 20:06:05 -07:00

58 lines
3.1 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Confirm your UmbraTrans email address">
<meta name="referrer" content="same-origin">
<title>Confirm your email address · UmbraTrans</title>
<link rel="icon" type="image/png" href="assets/umbratrans-emblem.png">
<link rel="stylesheet" href="assets/styles.css">
</head>
<body class="auth-page">
<main class="auth-shell">
<section class="auth-brand" aria-labelledby="brand-title">
<a class="brand-lockup" href="index.html" aria-label="UmbraTrans home">
<img class="brand-image" src="assets/umbratrans-emblem.png" alt="" aria-hidden="true">
<span>UmbraTrans</span>
</a>
<div class="brand-copy">
<p class="eyebrow">Translation production workspace</p>
<h1 id="brand-title">One source of truth for every language.</h1>
<p class="brand-lede">A short confirmation keeps account recovery and access notices pointed at an address you control.</p>
</div>
</section>
<section class="auth-card" aria-label="Confirm your email address">
<div class="auth-card-heading">
<span class="status-pill status-pill--info" id="verify-pill">Almost there</span>
<h2 id="verify-title">Confirm your email address</h2>
<p id="verify-intro">Confirming proves you can receive mail at this address, which is what makes password recovery possible later.</p>
</div>
<div id="verify-auto" hidden>
<div class="form-message" role="status" aria-live="polite">Confirming your address…</div>
</div>
<!-- method="post": a form with no method defaults to GET, and a native
fallback submission would put email verification code into the query string and into
the server log. No action: the script submits via fetch; this only
decides what happens when the script does not run. -->
<form id="verify-form" class="stack-form" method="post" novalidate>
<label for="verify-token">Confirmation link or token</label>
<input id="verify-token" name="token" type="text" autocomplete="off" autocapitalize="none" autocorrect="off" spellcheck="false" placeholder="Paste the link, or just the token" required>
<p class="field-hint">The link from your email opens this page automatically. You can also paste just the token.</p>
<div id="verify-message" class="form-message" role="status" aria-live="polite"></div>
<button id="verify-submit" class="button button--primary button--wide" type="submit">Confirm my address <span aria-hidden="true">→</span></button>
</form>
<div class="auth-divider"><span>Already confirmed?</span></div>
<a class="admin-link" href="index.html"><span>Back to sign in</span><span aria-hidden="true">↗</span></a>
<p class="auth-footnote">A confirmation link is single-use and expires. If yours has expired, sign in and request a new one.</p>
</section>
</main>
<script src="assets/api.js" defer></script>
<script src="assets/verify.js" defer></script>
</body>
</html>