From 6e1df4ddc698f095b6ca4da29cf489810ef67716 Mon Sep 17 00:00:00 2001 From: Afonso Coutinho Date: Wed, 3 Jun 2026 06:23:34 +0100 Subject: [PATCH] fix: POST /api/contacts/add crashes on JSON null name/email (None.strip()) (#1544) --- routes/contacts_routes.py | 4 +-- tests/test_contacts_add_null_name.py | 42 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 tests/test_contacts_add_null_name.py diff --git a/routes/contacts_routes.py b/routes/contacts_routes.py index 8db5463..e045a3a 100644 --- a/routes/contacts_routes.py +++ b/routes/contacts_routes.py @@ -676,8 +676,8 @@ def setup_contacts_routes(): @router.post("/add") async def add_contact(data: dict, _admin: str = Depends(require_admin)): """Add a new contact.""" - name = data.get("name", "").strip() - email = data.get("email", "").strip() + name = (data.get("name") or "").strip() + email = (data.get("email") or "").strip() if not email: return {"success": False, "error": "Email required"} # Check if already exists diff --git a/tests/test_contacts_add_null_name.py b/tests/test_contacts_add_null_name.py new file mode 100644 index 0000000..8341c3e --- /dev/null +++ b/tests/test_contacts_add_null_name.py @@ -0,0 +1,42 @@ +"""Regression: POST /api/contacts/add must not crash when name/email is JSON null. + +The handler did `data.get("name", "").strip()`. dict.get returns the default +only when the key is ABSENT; a body like {"name": null, "email": "x@y.com"} +gives name=None, so None.strip() raised AttributeError -> 500. Now guarded with +`(data.get("name") or "")`. +""" +import asyncio + +import pytest + +import routes.contacts_routes as cr + + +def _add_handler(): + router = cr.setup_contacts_routes() + for r in router.routes: + if getattr(r, "path", "").endswith("/add") and "POST" in getattr(r, "methods", set()): + return r.endpoint + raise AssertionError("add_contact route not found") + + +@pytest.fixture +def _stub_store(monkeypatch): + created = [] + monkeypatch.setattr(cr, "_fetch_contacts", lambda *a, **k: []) + monkeypatch.setattr(cr, "_create_contact", lambda name, email: created.append((name, email)) or True) + return created + + +def test_null_name_does_not_crash(_stub_store): + handler = _add_handler() + result = asyncio.run(handler({"name": None, "email": "x@y.com"}, _admin="admin")) + assert result["success"] is True + # name fell back to the email local-part instead of crashing. + assert _stub_store == [("x", "x@y.com")] + + +def test_null_email_is_rejected_cleanly(_stub_store): + handler = _add_handler() + result = asyncio.run(handler({"name": "Bob", "email": None}, _admin="admin")) + assert result == {"success": False, "error": "Email required"}