Two things tied EC to the host's origin: - Widget message check: matrix-widget-api's strictOriginCheck compares ev.origin with THIS frame's origin, so on call.chat.lotusguild.org every message from chat.lotusguild.org would be dropped and calls would not start. restrictToHost() instead requires ev.source === window.parent and ev.origin === parentUrl's origin. Same-origin deployments keep working (the host origin is our own origin there), and it is stricter than before: the sender must also be our parent window. - Soundboard: the host's blob: clip URL is origin-bound. io.lotus.inject_audio now accepts the clip's bytes (`audio`, ArrayBuffer, <= 8 MiB) and prefers them over `url`; hosts that only send `url` are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
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);
|
|
}
|