/* Copyright 2026 Lotus Guild SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ type MessageLike = Pick; /** True when a message came from our parent window, at the host's origin. */ export function isFromHost( ev: MessageLike, parentWindow: Window, parentOrigin: string, ): boolean { return ev.source === parentWindow && ev.origin === parentOrigin; } interface InboundTransport { handleMessage: (ev: MessageEvent) => void; } /** * [cinny #43] Only handle widget messages sent by the host (our parent window) * from the origin it was loaded at (`parentUrl`). * * matrix-widget-api's `strictOriginCheck` compares `ev.origin` with this * frame's OWN origin, which only works while EC is served from the host's * origin; served from its own origin (e.g. call.chat.lotusguild.org) every * host message would be dropped. Turning the check off instead would let any * window post toWidget actions (including io.lotus.*). This compares against * the host's origin and also requires the sender to be our parent, so it is * correct same-origin and cross-origin alike. * * Swaps the transport's `message` listener; safe whether or not the transport * has started (`stop()` removes `handleMessage`, the guarded one after this). */ export function restrictToHost( transport: unknown, parentOrigin: string, parentWindow: Window = window.parent, inbound: Pick = window, ): void { const t = transport as InboundTransport; const original = t.handleMessage; const guarded = (ev: MessageEvent): void => { if (!isFromHost(ev, parentWindow, parentOrigin)) return; original(ev); }; inbound.removeEventListener("message", original as EventListener); t.handleMessage = guarded; inbound.addEventListener("message", guarded as EventListener); }