Skip to content

Commit 37d54a9

Browse files
committed
Fix UTF-16-safe voice media log truncation
1 parent 632efa2 commit 37d54a9

2 files changed

Lines changed: 9 additions & 1 deletion

File tree

extensions/voice-call/src/media-stream.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,13 @@ describe("MediaStreamHandler security hardening", () => {
444444
expect(reason).toContain("forged line entry");
445445
});
446446

447+
it("truncates websocket close reason without splitting UTF-16 surrogate pairs", () => {
448+
const reason = sanitizeLogText(`abc\uD83D\uDE80tail`, 4);
449+
expect(reason).toBe("abc...");
450+
expect(reason).not.toContain("\uD83D");
451+
expect(reason).not.toContain("\uDE80");
452+
});
453+
447454
it("closes idle pre-start connections after timeout", async () => {
448455
const shouldAcceptStreamCalls: Array<{ callId: string; streamSid: string; token?: string }> =
449456
[];

extensions/voice-call/src/media-stream.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
type TalkEventInput,
2424
type TalkSessionController,
2525
} from "openclaw/plugin-sdk/realtime-voice";
26+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
2627
import { type RawData, WebSocket, WebSocketServer } from "ws";
2728

2829
/**
@@ -109,7 +110,7 @@ export function sanitizeLogText(value: string, maxChars: number): string {
109110
if (sanitized.length <= maxChars) {
110111
return sanitized;
111112
}
112-
return `${sanitized.slice(0, maxChars)}...`;
113+
return `${truncateUtf16Safe(sanitized, maxChars)}...`;
113114
}
114115

115116
function normalizeWsMessageData(data: RawData): Buffer {

0 commit comments

Comments
 (0)