Skip to content

Commit 9492362

Browse files
[Security] Apply sanitize_message to Anthropic and STT error paths (#45119)
Signed-off-by: jperezde <jperezde@redhat.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 7852e50 commit 9492362

4 files changed

Lines changed: 91 additions & 5 deletions

File tree

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3+
"""Tests that error messages in Anthropic and speech-to-text entrypoints
4+
are sanitized to prevent memory address leakage.
5+
6+
Verifies the fix for the incomplete CVE-2026-22778 remediation where
7+
PIL repr addresses leaked via the Anthropic API router and the
8+
speech-to-text WebSocket paths.
9+
"""
10+
11+
import pytest
12+
13+
from vllm.entrypoints.serve.utils.api_utils import sanitize_message
14+
15+
16+
class TestSanitizeMessageCoversLeakPatterns:
17+
"""Ensure sanitize_message strips addresses from realistic exceptions."""
18+
19+
@pytest.mark.parametrize(
20+
("raw", "expected"),
21+
[
22+
(
23+
"cannot identify image file <_io.BytesIO object at 0x7a95e299e750>",
24+
"cannot identify image file <_io.BytesIO object>",
25+
),
26+
(
27+
"cannot identify image file <_io.BytesIO object at 0x7f3c1a2b4d90>",
28+
"cannot identify image file <_io.BytesIO object>",
29+
),
30+
(
31+
"<PIL.PngImagePlugin.PngImageFile image mode=RGB "
32+
"size=8x8 at 0x7f3c1a2b4d90>",
33+
"<PIL.PngImagePlugin.PngImageFile image mode=RGB size=8x8>",
34+
),
35+
(
36+
"Error processing <_io.BytesIO object at 0xdeadbeef>: invalid header",
37+
"Error processing <_io.BytesIO object>: invalid header",
38+
),
39+
],
40+
ids=[
41+
"bytesio-standard",
42+
"bytesio-different-addr",
43+
"pil-image-repr",
44+
"mid-string-repr",
45+
],
46+
)
47+
def test_address_stripped(self, raw: str, expected: str):
48+
assert sanitize_message(raw) == expected
49+
50+
def test_safe_message_unchanged(self):
51+
msg = "Invalid request: missing 'messages' field"
52+
assert sanitize_message(msg) == msg
53+
54+
def test_multiple_addresses_stripped(self):
55+
raw = "<obj at 0xaaa> and <obj at 0xbbb>"
56+
result = sanitize_message(raw)
57+
assert "0x" not in result
58+
59+
60+
class TestAffectedModulesUseSanitize:
61+
"""Verify that affected modules call sanitize_message (source-level)."""
62+
63+
@pytest.mark.parametrize(
64+
"module",
65+
[
66+
"vllm.entrypoints.anthropic.api_router",
67+
"vllm.entrypoints.anthropic.serving",
68+
"vllm.entrypoints.speech_to_text.realtime.connection",
69+
],
70+
)
71+
def test_module_calls_sanitize_message(self, module: str):
72+
import importlib.util
73+
from pathlib import Path
74+
75+
spec = importlib.util.find_spec(module)
76+
assert spec is not None and spec.origin is not None, (
77+
f"Cannot locate module {module}"
78+
)
79+
source = Path(spec.origin).read_text()
80+
assert "sanitize_message" in source, f"{module} does not call sanitize_message"
81+
assert "import" in source and "sanitize_message" in source

vllm/entrypoints/anthropic/api_router.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from vllm.entrypoints.openai.engine.protocol import ErrorResponse
2020
from vllm.entrypoints.serve.utils.api_utils import (
2121
load_aware_call,
22+
sanitize_message,
2223
validate_json_request,
2324
with_cancellation,
2425
)
@@ -75,7 +76,7 @@ async def create_messages(request: AnthropicMessagesRequest, raw_request: Reques
7576
content=AnthropicErrorResponse(
7677
error=AnthropicError(
7778
type="internal_error",
78-
message=str(e),
79+
message=sanitize_message(str(e)),
7980
)
8081
).model_dump(),
8182
)
@@ -121,7 +122,7 @@ async def count_tokens(request: AnthropicCountTokensRequest, raw_request: Reques
121122
content=AnthropicErrorResponse(
122123
error=AnthropicError(
123124
type="internal_error",
124-
message=str(e),
125+
message=sanitize_message(str(e)),
125126
)
126127
).model_dump(),
127128
)

vllm/entrypoints/anthropic/serving.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
StreamOptions,
4545
)
4646
from vllm.entrypoints.openai.models.serving import OpenAIServingModels
47+
from vllm.entrypoints.serve.utils.api_utils import sanitize_message
4748
from vllm.entrypoints.serve.utils.request_logger import RequestLogger
4849

4950
if TYPE_CHECKING:
@@ -846,7 +847,9 @@ def start_block(block: AnthropicContentBlock):
846847
logger.exception("Error in message stream converter.")
847848
error_response = AnthropicStreamEvent(
848849
type="error",
849-
error=AnthropicError(type="internal_error", message=str(e)),
850+
error=AnthropicError(
851+
type="internal_error", message=sanitize_message(str(e))
852+
),
850853
)
851854
data = error_response.model_dump_json(exclude_unset=True)
852855
yield wrap_data_with_event(data, "error")

vllm/entrypoints/speech_to_text/realtime/connection.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from vllm import envs
1616
from vllm.entrypoints.openai.engine.protocol import ErrorResponse, UsageInfo
17+
from vllm.entrypoints.serve.utils.api_utils import sanitize_message
1718
from vllm.exceptions import VLLMValidationError
1819
from vllm.logger import init_logger
1920

@@ -72,7 +73,7 @@ async def handle_connection(self):
7273
await self.send_error("Invalid JSON", "invalid_json")
7374
except Exception as e:
7475
logger.exception("Error handling event: %s", e)
75-
await self.send_error(str(e), "processing_error")
76+
await self.send_error(sanitize_message(str(e)), "processing_error")
7677
except WebSocketDisconnect:
7778
logger.debug("WebSocket disconnected: %s", self.connection_id)
7879
self._is_connected = False
@@ -262,7 +263,7 @@ async def _run_generation(
262263

263264
except Exception as e:
264265
logger.exception("Error in generation: %s", e)
265-
await self.send_error(str(e), "processing_error")
266+
await self.send_error(sanitize_message(str(e)), "processing_error")
266267

267268
async def send(
268269
self, event: SessionCreated | TranscriptionDelta | TranscriptionDone

0 commit comments

Comments
 (0)