fix: reject empty credential fields with 400 instead of 500

Add an explicit non-empty check before encrypting user-submitted
email/password fields in the create and update user routes, so a
request that bypasses the HTML `required` attribute gets a clean
400 instead of an unhandled ValueError from CredentialCipher.encrypt
propagating as a 500. Applies to all four credential fields on
create, and to the two email fields on update (the password-blank-
means-keep-existing behavior on update is unchanged).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Bastian Wagner
2026-08-15 09:50:05 +02:00
parent b39842fe2c
commit d2ac079870
2 changed files with 128 additions and 6 deletions

View File

@@ -234,3 +234,111 @@ def test_dashboard_lists_created_user_without_secrets(client: TestClient) -> Non
assert "Max" in response.text
assert "mw-secret" not in response.text
assert "garmin-secret" not in response.text
def _create_payload(**overrides: str) -> dict:
payload = {
"name": "Max",
"mywhoosh_email": "max@example.com",
"mywhoosh_password": "mw-secret",
"garmin_email": "max-garmin@example.com",
"garmin_password": "garmin-secret",
"enabled": "on",
}
payload.update(overrides)
return payload
def test_create_user_rejects_empty_mywhoosh_email(client: TestClient) -> None:
login(client)
page = client.get("/users/new")
csrf = extract_csrf(page.text)
response = client.post(
"/users",
data={"csrf_token": csrf, **_create_payload(mywhoosh_email="")},
)
assert response.status_code == 400
def test_create_user_rejects_empty_mywhoosh_password(client: TestClient) -> None:
login(client)
page = client.get("/users/new")
csrf = extract_csrf(page.text)
response = client.post(
"/users",
data={"csrf_token": csrf, **_create_payload(mywhoosh_password="")},
)
assert response.status_code == 400
def test_create_user_rejects_empty_garmin_email(client: TestClient) -> None:
login(client)
page = client.get("/users/new")
csrf = extract_csrf(page.text)
response = client.post(
"/users",
data={"csrf_token": csrf, **_create_payload(garmin_email="")},
)
assert response.status_code == 400
def test_create_user_rejects_empty_garmin_password(client: TestClient) -> None:
login(client)
page = client.get("/users/new")
csrf = extract_csrf(page.text)
response = client.post(
"/users",
data={"csrf_token": csrf, **_create_payload(garmin_password="")},
)
assert response.status_code == 400
def test_create_user_rejects_whitespace_only_email(client: TestClient) -> None:
login(client)
page = client.get("/users/new")
csrf = extract_csrf(page.text)
response = client.post(
"/users",
data={"csrf_token": csrf, **_create_payload(mywhoosh_email=" ")},
)
assert response.status_code == 400
def test_update_user_rejects_empty_mywhoosh_email(client: TestClient) -> None:
login(client)
user_id = create_user_via_http(client)
edit_page = client.get(f"/users/{user_id}/edit")
csrf = extract_csrf(edit_page.text)
response = client.post(
f"/users/{user_id}",
data={
"csrf_token": csrf,
"name": "Max",
"mywhoosh_email": "",
"mywhoosh_password": "",
"garmin_email": "max-garmin@example.com",
"garmin_password": "",
"enabled": "on",
},
)
assert response.status_code == 400
def test_update_user_rejects_empty_garmin_email(client: TestClient) -> None:
login(client)
user_id = create_user_via_http(client)
edit_page = client.get(f"/users/{user_id}/edit")
csrf = extract_csrf(edit_page.text)
response = client.post(
f"/users/{user_id}",
data={
"csrf_token": csrf,
"name": "Max",
"mywhoosh_email": "max@example.com",
"mywhoosh_password": "",
"garmin_email": "",
"garmin_password": "",
"enabled": "on",
},
)
assert response.status_code == 400