54 lines
1.9 KiB
TypeScript
54 lines
1.9 KiB
TypeScript
/*
|
|||
|
|
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<MessageEvent, "source" | "origin">;
|
||
|
|
|
||
|
|
/** 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, "addEventListener" | "removeEventListener"> = 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);
|
||
|
|
}
|