fix: harden cloudonix cdr session validation

This commit is contained in:
Sabiha Khan 2026-06-10 17:33:28 +05:30
parent ace4903c0e
commit f7b3dc1ceb
3 changed files with 29 additions and 2 deletions

View file

@ -103,7 +103,8 @@ async def handle_cloudonix_cdr(request: Request):
return {"status": "error", "message": "Missing domain field"}
# Extract call_id to find workflow run
call_id = (cdr_data.get("session") or {}).get("token")
session = cdr_data.get("session")
call_id = session.get("token") if isinstance(session, dict) else None
logger.info(f"Cloudonix CDR data for call id {call_id} - {cdr_data}")
if not call_id:
logger.warning("Cloudonix CDR missing call_id field")

View file

@ -116,9 +116,11 @@ class StatusCallbackRequest(BaseModel):
disposition = data.get("disposition") or ""
status = disposition_map.get(disposition.upper(), disposition.lower())
session = data.get("session")
call_id = session.get("token") if isinstance(session, dict) else ""
return cls(
call_id=(data.get("session") or {}).get("token") or "",
call_id=call_id or "",
status=status,
from_number=data.get("from"),
to_number=data.get("to"),

View file

@ -64,6 +64,21 @@ async def test_cdr_route_handles_null_session():
assert result == {"status": "error", "message": "Missing call_id field"}
@pytest.mark.asyncio
async def test_cdr_route_handles_string_session():
"""A CDR payload with a non-object session is handled gracefully."""
request = _json_request(b'{"domain": "acme.cloudonix.io", "session": "abc"}')
with patch(
"api.services.telephony.providers.cloudonix.routes.db_client"
) as db_client:
db_client.get_workflow_run_by_call_id = AsyncMock(return_value=None)
result = await handle_cloudonix_cdr(request)
assert result == {"status": "error", "message": "Missing call_id field"}
def test_from_cloudonix_cdr_tolerates_missing_session_and_disposition():
"""``from_cloudonix_cdr`` must not crash on a partial CDR payload."""
# Missing both session and disposition.
@ -79,6 +94,15 @@ def test_from_cloudonix_cdr_tolerates_missing_session_and_disposition():
assert req.status == ""
def test_from_cloudonix_cdr_tolerates_string_session():
"""``from_cloudonix_cdr`` treats a non-object session as missing call_id."""
req = StatusCallbackRequest.from_cloudonix_cdr(
{"session": "abc", "disposition": "ANSWER"}
)
assert req.call_id == ""
assert req.status == "completed"
def test_from_cloudonix_cdr_maps_disposition_and_session_token():
"""Normal, well-formed CDR payloads still map correctly."""
req = StatusCallbackRequest.from_cloudonix_cdr(