Discovered while running pre-merge verification on PR #284. Full context in .claude/notes/2026-05-20-signup-flow-adhoc-findings.md — Finding 15.
signup_submit does not check session.get('user'). A logged-in user can POST /signup/submit with an arbitrary new email and create a fresh pending member row that is not linked to their session in any way.
Reproduction (on dev)
- Dev-login as Ada Lovelace (id=1) — confirm
/dashboard returns 200 with Ada's info.
- POST
/signup/submit from the same session, with a NEW email and a unique username.
- Result: HTTP 302 →
/signup/payment?email=…. New member row created in the DB (id=62 in our repro).
- Ada's session is untouched.
/dashboard still renders her id=1 data.
POSTing with the SAME email as the logged-in user correctly routes to /signup with the "already exists" flash — i.e., the email-collision check catches the same-email case. The gap is only for new emails.
Implications
Suggested fix shape
Add a session-already-authenticated short-circuit at the top of both signup_check_email and signup_submit:
if session.get('user'):
flash('You are already signed in.', 'info')
return redirect(url_for('member_dashboard'))
One-line guard per endpoint. Should NOT touch signup_start (GET /signup) since the Finding 5 fix in PR #284 already preserves the logged-in user's session on visit; consider whether to redirect them to dashboard or render the signup page as today.
Context: PR #284
Discovered while running pre-merge verification on PR #284. Full context in
.claude/notes/2026-05-20-signup-flow-adhoc-findings.md— Finding 15.signup_submitdoes not checksession.get('user'). A logged-in user can POST/signup/submitwith an arbitrary new email and create a fresh pending member row that is not linked to their session in any way.Reproduction (on dev)
/dashboardreturns 200 with Ada's info./signup/submitfrom the same session, with a NEW email and a unique username./signup/payment?email=…. New member row created in the DB (id=62 in our repro)./dashboardstill renders her id=1 data.POSTing with the SAME email as the logged-in user correctly routes to
/signupwith the "already exists" flash — i.e., the email-collision check catches the same-email case. The gap is only for new emails.Implications
/signup/submit. Each one fills the DB with orphan rows that admin tooling must manually clean up.Suggested fix shape
Add a session-already-authenticated short-circuit at the top of both
signup_check_emailandsignup_submit:One-line guard per endpoint. Should NOT touch
signup_start(GET /signup) since the Finding 5 fix in PR #284 already preserves the logged-in user's session on visit; consider whether to redirect them to dashboard or render the signup page as today.Context: PR #284