From 439a424209836250f83b731ebb163dfc46fe7d0b Mon Sep 17 00:00:00 2001 From: Shakti Prasad Mohapatra Date: Sat, 29 Aug 2026 23:59:01 +0530 Subject: [PATCH] fix(sip): raise header value cap to 4 KB for carrier tokens like X-Twilio-CallToken MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Twilio's Immutable Call Forwarding flow requires X-Twilio-CallToken to be forwarded unchanged on the outbound leg. The token is opaque and carries SHAKEN/STIR + DIV PASSporTs, so it routinely exceeds the current 1024-byte cap and CreateSIPParticipant rejects the request with 'invalid header value for X-Twilio-CallToken: value too long (max 1024 characters)' before the INVITE is ever sent. RFC 3261 §7.1 bounds the message (8 KB datagram on UDP), not individual header values. 4096 keeps a single header from dominating a datagram while tolerating real carrier tokens; the character-set validation is unchanged. Fixes #789 --- .changeset/sip-header-value-cap.md | 6 ++++++ livekit/sip_validation.go | 15 +++++++++++++-- livekit/sip_validation_test.go | 5 +++-- 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 .changeset/sip-header-value-cap.md diff --git a/.changeset/sip-header-value-cap.md b/.changeset/sip-header-value-cap.md new file mode 100644 index 000000000..4e8c35c2c --- /dev/null +++ b/.changeset/sip-header-value-cap.md @@ -0,0 +1,6 @@ +--- +"github.com/livekit/protocol": patch +"@livekit/protocol": patch +--- + +Raise the SIP header value cap from 1 KB to 4 KB so carrier headers like Twilio's `X-Twilio-CallToken` (Immutable Call Forwarding token with SHAKEN/STIR PASSporTs, >1 KB) pass `CreateSIPParticipant` validation \ No newline at end of file diff --git a/livekit/sip_validation.go b/livekit/sip_validation.go index 814cd0c7c..da1f5a364 100644 --- a/livekit/sip_validation.go +++ b/livekit/sip_validation.go @@ -149,6 +149,12 @@ func init() { headerValuesCharacters.AddUTF8() } +// maxHeaderValueLength caps a single SIP header value. It is not an RFC limit +// (RFC 3261 §7.1 bounds the message, not headers), but guards against abusive +// or oversized values. Set above carriers' real-world maximums — Twilio's +// X-Twilio-CallToken exceeds 1 KB — and well below the 8 KB UDP datagram bound. +const maxHeaderValueLength = 4096 + // Required headers for SIP requests per RFC 3261 Section 8.1.1 var RequiredRequestHeaders = map[string]bool{ "via": true, @@ -257,8 +263,13 @@ func ValidateHeaderValueResult(name, value string) ValidationResult { return ValidationResult{} } - if len(value) > 1024 { - return ValidationFailure(fmt.Errorf("header %s: value too long (max 1024 characters)", name)) + // RFC 3261 caps the whole message (8 KB per §7.1 when UDP), not individual + // header values. Carriers do exceed 1 KB on a single header — e.g. Twilio's + // X-Twilio-CallToken carries an Immutable Call Forwarding token with + // SHAKEN/STIR + DIV PASSporTs that must be forwarded unchanged. 4096 keeps + // a single header from dominating a datagram while tolerating those. + if len(value) > maxHeaderValueLength { + return ValidationFailure(fmt.Errorf("header %s: value too long (max %d characters)", name, maxHeaderValueLength)) } // Basic character validation - printable ASCII. We're stricter than the spec here - no UTF-8 for now diff --git a/livekit/sip_validation_test.go b/livekit/sip_validation_test.go index efccb6dbe..369182211 100644 --- a/livekit/sip_validation_test.go +++ b/livekit/sip_validation_test.go @@ -99,7 +99,8 @@ var ValidHeaderValues = []string{ "Header with unicode café", // Unicode "Header with unicode 世界", // Unicode "Header with unicode émojis 🎉", // Unicode with emojis - strings.Repeat("a", 1024), // max length + strings.Repeat("a", 1024), // beyond carrier needs (e.g. X-Twilio-CallToken) + strings.Repeat("a", 4096), // max length } // Note: These restrictions are NOT in RFC 3261 but are applied for security/performance @@ -110,7 +111,7 @@ var InvalidHeaderValues = []string{ "Header with\x01control", // control character "Header with\x1Funit separator", // control character "Header with\x7Fdelete", // delete character - strings.Repeat("a", 1025), // too long + strings.Repeat("a", 4097), // too long } // testCaseName truncates a test case name to maxLen and adds dots with total size