mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-17 10:15:27 -04:00
fix(contacts): tolerate non-string body in /api/contacts/import (#3638)
import_vcf built `text = data.get("vcf") or data.get("text") or ""`, so a
non-string JSON value (a number, list, etc.) stayed in place and the following
`text.strip()` raised AttributeError, returning HTTP 500. Coerce vcf/text/csv
with str() so non-string input degrades to the existing structured "no data"
response, matching the file's convention elsewhere.
Adds tests/test_contacts_import_nonstring.py covering non-string vcf, non-string
csv, and an empty body.
This commit is contained in:
committed by
GitHub
parent
4e210d3337
commit
96975f8dd9
@@ -729,8 +729,11 @@ def setup_contacts_routes():
|
|||||||
@router.post("/import")
|
@router.post("/import")
|
||||||
async def import_vcf(data: dict, _admin: str = Depends(require_admin)):
|
async def import_vcf(data: dict, _admin: str = Depends(require_admin)):
|
||||||
"""Import contacts from .vcf or CSV. Body: {"vcf": "..."} or {"csv": "..."}."""
|
"""Import contacts from .vcf or CSV. Body: {"vcf": "..."} or {"csv": "..."}."""
|
||||||
text = data.get("vcf") or data.get("text") or ""
|
# Coerce defensively: a non-string vcf/text/csv (e.g. a number or list
|
||||||
csv_text = data.get("csv") or ""
|
# in the JSON body) would otherwise reach .strip() and 500 with an
|
||||||
|
# AttributeError instead of degrading to a clean "no data" response.
|
||||||
|
text = str(data.get("vcf") or data.get("text") or "")
|
||||||
|
csv_text = str(data.get("csv") or "")
|
||||||
if text.strip():
|
if text.strip():
|
||||||
if "BEGIN:VCARD" not in text.upper():
|
if "BEGIN:VCARD" not in text.upper():
|
||||||
return {"success": False, "error": "No vCard data found"}
|
return {"success": False, "error": "No vCard data found"}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
"""POST /api/contacts/import must not 500 on a non-string vcf/text/csv value.
|
||||||
|
|
||||||
|
`text = data.get("vcf") or ... or ""` left a non-string value (e.g. a number)
|
||||||
|
in place, so the next `text.strip()` raised AttributeError -> HTTP 500. The
|
||||||
|
handler now coerces with str() and degrades to a structured "no data" response.
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from routes.contacts_routes import setup_contacts_routes
|
||||||
|
|
||||||
|
|
||||||
|
def _import_handler():
|
||||||
|
router = setup_contacts_routes()
|
||||||
|
for route in router.routes:
|
||||||
|
if getattr(route, "path", "").endswith("/import") and "POST" in getattr(route, "methods", set()):
|
||||||
|
return route.endpoint
|
||||||
|
raise AssertionError("import route not found")
|
||||||
|
|
||||||
|
|
||||||
|
def _call(data):
|
||||||
|
handler = _import_handler()
|
||||||
|
return asyncio.run(handler(data=data, _admin="admin"))
|
||||||
|
|
||||||
|
|
||||||
|
def test_non_string_vcf_degrades_cleanly():
|
||||||
|
resp = _call({"vcf": 123})
|
||||||
|
assert resp["success"] is False
|
||||||
|
assert "error" in resp
|
||||||
|
|
||||||
|
|
||||||
|
def test_non_string_csv_degrades_cleanly():
|
||||||
|
resp = _call({"csv": ["a", "b"]})
|
||||||
|
assert resp["success"] is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_empty_body_reports_no_data():
|
||||||
|
resp = _call({})
|
||||||
|
assert resp["success"] is False
|
||||||
|
assert resp["error"] == "No contact data found"
|
||||||
Reference in New Issue
Block a user