From 8b575bcc2c5881a1819fa1c71376fdc69cf4aa44 Mon Sep 17 00:00:00 2001 From: Mitsuki Fukunaga Date: Fri, 17 Jul 2026 07:01:36 +1000 Subject: [PATCH] fix(webview): preview-only logging for rejected host messages The validator-rejection branch in subscribeToHost logged the full event.data payload to the console. Align it with the host-side precedent in quoll-editor-panel.ts (handleInbound), which previews only type + top-level keys instead of leaking arbitrary payload content. --- src/webview/host.ts | 9 ++++++++- test/webview/host.test.ts | 12 +++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/webview/host.ts b/src/webview/host.ts index 3ad3cb70..78b1c8fc 100644 --- a/src/webview/host.ts +++ b/src/webview/host.ts @@ -154,7 +154,14 @@ export function subscribeToHost(handler: (message: HostToWebview) => void): () = type: data.type, }); } else { - console.error("[quoll] host message rejected by validator", event.data); + // Symmetric with the host-side inbound-validation log (quoll-editor-panel.ts): + // preview only type + top-level keys, never the full payload, so an + // unvalidated message can't leak arbitrary content to the console. + const preview = + data !== null && typeof data === "object" + ? { type: data.type, keys: Object.keys(data) } + : { type: typeof event.data }; + console.error("[quoll] host message rejected by validator", preview); } return; } diff --git a/test/webview/host.test.ts b/test/webview/host.test.ts index 1a9ca595..d92819ff 100644 --- a/test/webview/host.test.ts +++ b/test/webview/host.test.ts @@ -175,12 +175,18 @@ describe("subscribeToHost — boundary validation + diagnostics", () => { expect(detail).toMatchObject({ expected: PROTOCOL_VERSION, got: 2, type: "document" }); }); - it("rejects and logs a shapeless payload via the validator branch", () => { - window.dispatchEvent(new MessageEvent("message", { data: { hello: "world" } })); + it("rejects and logs a shapeless payload via the validator branch, previewing only type + keys", () => { + window.dispatchEvent( + new MessageEvent("message", { data: { hello: "world", secret: "sensitive-value" } }) + ); expect(handler).not.toHaveBeenCalled(); expect(errorSpy).toHaveBeenCalledTimes(1); - expect(String(errorSpy.mock.calls[0][0])).toContain("rejected by validator"); + const [message, detail] = errorSpy.mock.calls[0]; + expect(String(message)).toContain("rejected by validator"); + expect(detail).toEqual({ type: undefined, keys: ["hello", "secret"] }); + // The raw payload value must never reach the console — only its shape. + expect(JSON.stringify(detail)).not.toContain("sensitive-value"); }); it("logs an undefined payload as an empty payload, not a validator rejection", () => {