From c984bb4b56efff999ecfb6337cba437a69f4cce5 Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Fri, 16 Oct 2026 17:06:24 -0400 Subject: [PATCH] A crash report's strings reach the log on one line: anyone may post one, and a line break could otherwise forge another log line Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0141G6xqLeNRYEtviLWSB5Up --- packages/server/src/index.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index cabaa9c..0f0728b 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -117,6 +117,12 @@ const IMAGE_MAX_BYTES = 2_500_000; const crashReportsPerAddress = new SlidingLimit(10, 60 * 60 * 1000); const CRASH_REPORT_MAX_BYTES = 64 * 1024; +/** Anyone may post a report, so its strings reach the log on one line, + * with no control characters to forge another. */ +function printable(value: unknown, max: number): string { + return String(value ?? "").replace(/[\u0000-\u001f\u007f]/g, " ").slice(0, max); +} + /** Chromium sends the Reporting API's crash reports here, after the fact, * from the browser itself: a tab that crashes takes the page's own error * reporting down with it. Each crash is logged and filed in Sentry as a @@ -139,10 +145,10 @@ function receiveBrowserReports(req: IncomingMessage, res: ServerResponse): void if (!Array.isArray(reports)) return; for (const r of reports as { type?: unknown; url?: unknown; user_agent?: unknown; body?: Record }[]) { if (r?.type !== "crash" || !crashReportsPerAddress.allow(address)) continue; - const url = String(r.url ?? "").slice(0, 200); + const url = printable(r.url, 200); const room = url.match(/\/join\/([A-Za-z0-9]{4})/)?.[1]?.toUpperCase() ?? "none"; - const reason = typeof r.body?.reason === "string" ? r.body.reason.slice(0, 40) : "unknown"; - const userAgent = String(r.user_agent ?? "").slice(0, 300); + const reason = typeof r.body?.reason === "string" ? printable(r.body.reason, 40) : "unknown"; + const userAgent = printable(r.user_agent, 300); console.warn(`browser tab crashed: room ${room}, reason ${reason}, ${userAgent}`); Sentry.captureMessage("A player's browser tab crashed", { level: "warning",