Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Unauthenticated /ws/call WebSocket bypasses the PIN gate (anyone can talk to the agent and burn STT/LLM/TTS quota) #66

Description

@jaylfc

The webhook before_request guard validates Twilio signatures on /voice/* only (server.py:198-218) — the comment at server.py:204-205 explicitly notes the WS is unguarded. The /ws/call handler (server.py:759-825) accepts any connection on the public port 5050, opens a Deepgram live session, and on any transcribed speech calls the LLM and TTS.

Anyone who can reach port 5050 (internet-exposed by design, README.md:58) can converse with the agent and consume Deepgram/LLM/TTS quota — completely bypassing the PIN gate, lockout, and constant-time compare (server.py:642-665). The PIN guards a door next to an open window.

Fix: mint a one-time per-call token when generating the <Connect><Stream> TwiML (server.py:653-654), pass it via <Stream><Parameter> (arrives in the start event's customParameters), and close any WS connection whose start event doesn't carry a valid token mapped to the expected CallSid. Keep tokens single-use with a short TTL.

Gotchas: _ws_url() (server.py:221-227) has two call sites plus the WEBHOOK_URL_OVERRIDE path; for outbound calls, mint the token at /voice/outgoing TwiML time, not at POST /call time.

Full context: AUDIT.md (S1). Related: #8 covered the old unauthenticated control plane (since fixed by dashboard auth); this is the remaining gap on the webhook app.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0-criticalCritical — fix immediatelysecuritySecurity vulnerability or hardening

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions