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