fix(realtime): bound Muse audio before decoding

This commit is contained in:
Young Han 2026-09-02 13:40:35 -07:00 • committed by ryan-crabbe-berri
parent b82b31a44f
commit 1acb994998
2 changed files with 19 additions and 8 deletions

View file

@ -268,6 +268,15 @@ class MuseRealtimeAdapter:
if not isinstance(audio_value, str):
await self._reject("invalid_request_error", "invalid_audio", "Audio must be a base64 string")
return
max_backlog_bytes: Final = config.bytes_per_second * _MAX_AUDIO_BACKLOG_SECONDS
max_encoded_bytes: Final = 4 * ((max_backlog_bytes + 2) // 3)
if len(audio_value) > max_encoded_bytes:
await self._reject(
"invalid_request_error",
"audio_backlog_exceeded",
"Audio append exceeds the four-second backlog limit",
)
return
try:
audio: Final = base64.b64decode(audio_value, validate=True)
except (binascii.Error, ValueError):
@ -278,7 +287,6 @@ class MuseRealtimeAdapter:
return
if not audio:
return
max_backlog_bytes: Final = config.bytes_per_second * _MAX_AUDIO_BACKLOG_SECONDS
if len(audio) > max_backlog_bytes:
await self._reject(
"invalid_request_error",

View file

@ -3,7 +3,7 @@ import base64
import json
from collections.abc import Callable
from typing import Final
from unittest.mock import AsyncMock, MagicMock
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
@ -246,18 +246,21 @@ async def test_absolute_pacing_delays_only_audio_ahead_of_wall_time():
@pytest.mark.asyncio
async def test_append_larger_than_four_seconds_is_rejected_without_dropping_prefix():
async def test_append_larger_than_four_seconds_is_rejected_without_decoding():
adapter, provider_ws, _ = await _configured_adapter(rate=16_000)
oversized_pcm: Final = b"\x00\x00" * (16_000 * 4 + 1)
max_pcm_bytes: Final = 16_000 * 2 * 4
oversized_audio: Final = "A" * (4 * ((max_pcm_bytes + 2) // 3) + 1)
with patch( # test-quality-ok: proves rejection happens before an attacker-controlled allocation
"litellm.llms.meta.realtime.handler.base64.b64decode"
) as decode:
await adapter.send(json.dumps({"type": "input_audio_buffer.append", "audio": oversized_audio}))
await adapter.send(
json.dumps({"type": "input_audio_buffer.append", "audio": base64.b64encode(oversized_pcm).decode()})
)
error: Final = json.loads(await adapter.recv())
assert error["error"]["code"] == "audio_backlog_exceeded"
assert adapter.close_code == 1008
assert not any(isinstance(frame, bytes) for frame in provider_ws.sent)
decode.assert_not_called()
await adapter.close()