Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/webview/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
12 changes: 9 additions & 3 deletions test/webview/host.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down