From a3222de8b6a2574288affa146bf02ca131101f80 Mon Sep 17 00:00:00 2001 From: Xusheng Date: Tue, 22 Sep 2026 13:47:30 -0400 Subject: [PATCH] Fix registration password confirmation validation --- app/controllers/register.py | 10 ++++++++-- templates/register/register.html | 8 ++++---- tests/test_controller_edge_cases.py | 13 ++++++++++--- tests/test_routes.py | 22 ++++++++++++++++++++++ 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/app/controllers/register.py b/app/controllers/register.py index 7e99617..413e56b 100644 --- a/app/controllers/register.py +++ b/app/controllers/register.py @@ -52,13 +52,19 @@ def register_post(): name = request.form.get('name', '') email = request.form.get('email', '').lower() password = request.form.get('password', '') + password_verify = request.form.get('password_verify', '') # Validate required fields - if not name or not email or not password: - missing = 'name' if not name else ('email' if not email else 'password') + if not name or not email or not password or not password_verify: + missing = ('name' if not name else 'email' if not email else + 'password' if not password else 'password_verify') flash(f'Field missing: {missing}', FLASH_ERROR) return render_template('register/register.html', name=name, email=email) + if password != password_verify: + flash('Passwords do not match', FLASH_ERROR) + return render_template('register/register.html', name=name, email=email) + # Validate reCAPTCHA if not verify_recaptcha(request): flash('reCAPTCHA invalid!', FLASH_ERROR) diff --git a/templates/register/register.html b/templates/register/register.html index c65140e..88e4dd1 100644 --- a/templates/register/register.html +++ b/templates/register/register.html @@ -14,7 +14,7 @@
- +
@@ -22,7 +22,7 @@
- +
@@ -30,7 +30,7 @@
- +
@@ -38,7 +38,7 @@
- +
{% if RECAPTCHA_SITEKEY %} diff --git a/tests/test_controller_edge_cases.py b/tests/test_controller_edge_cases.py index 5702954..3154c95 100644 --- a/tests/test_controller_edge_cases.py +++ b/tests/test_controller_edge_cases.py @@ -41,9 +41,11 @@ def test_login_database_failure_is_generic(client): def test_registration_rejects_duplicate_email_and_username(client, alice): duplicate_email = client.post('/register', data={ 'name': 'different', 'email': 'alice@example.test', 'password': 'password123', + 'password_verify': 'password123', }) duplicate_name = client.post('/register', data={ 'name': 'alice', 'email': 'different@example.test', 'password': 'password123', + 'password_verify': 'password123', }) assert b'Account already exists' in duplicate_email.data assert b'Account already exists' in duplicate_name.data @@ -56,11 +58,11 @@ def test_registration_cross_field_conflicts(client, db): }) username_is_email = client.post('/register', data={ 'name': 'existing-email@example.test', 'email': 'new@example.test', - 'password': 'password123', + 'password': 'password123', 'password_verify': 'password123', }) email_is_username = client.post('/register', data={ 'name': 'new-user', 'email': 'existing@example.test', - 'password': 'password123', + 'password': 'password123', 'password_verify': 'password123', }) assert b'username is not available' in username_is_email.data assert b'email is not available' in email_is_username.data @@ -69,17 +71,22 @@ def test_registration_cross_field_conflicts(client, db): def test_registration_rejects_invalid_chars_and_recaptcha(client): invalid = client.post('/register', data={ 'name': 'bad name', 'email': 'valid@example.test', 'password': 'password123', + 'password_verify': 'password123', }) with patch('app.controllers.register.verify_recaptcha', return_value=False): captcha = client.post('/register', data={ 'name': 'valid', 'email': 'valid@example.test', 'password': 'password123', + 'password_verify': 'password123', }) assert b'Non allowed chars' in invalid.data assert b'reCAPTCHA invalid' in captcha.data def test_registration_hash_and_create_failures(client): - data = {'name': 'newuser', 'email': 'new@example.test', 'password': 'password123'} + data = { + 'name': 'newuser', 'email': 'new@example.test', + 'password': 'password123', 'password_verify': 'password123', + } with patch('app.controllers.register.hash_string', side_effect=RuntimeError): hashing = client.post('/register', data=data) with patch('app.controllers.register.user_create', side_effect=RuntimeError): diff --git a/tests/test_routes.py b/tests/test_routes.py index 852eabc..e39b553 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -66,6 +66,7 @@ def test_registration_creates_a_user(client, db): 'name': 'charlie', 'email': 'CHARLIE@example.test', 'password': 'charlie-password', + 'password_verify': 'charlie-password', }) assert response.status_code == 302 @@ -75,6 +76,27 @@ def test_registration_creates_a_user(client, db): assert match_string(user['password'], 'charlie-password') is True +def test_registration_requires_matching_password_confirmation(client, db): + registration = { + 'name': 'charlie', + 'email': 'charlie@example.test', + 'password': 'charlie-password', + } + + missing = client.post('/register', data=registration) + assert missing.status_code == 200 + assert b'Field missing: password_verify' in missing.data + assert db.user.find_one({'name': 'charlie'}) is None + + mismatch = client.post('/register', data={ + **registration, + 'password_verify': 'different-password', + }) + assert mismatch.status_code == 200 + assert b'Passwords do not match' in mismatch.data + assert db.user.find_one({'name': 'charlie'}) is None + + def test_logout_clears_only_main_auth(alice_client): with alice_client.session_transaction() as session: session['unrelated'] = 'preserved'