Commit Graph
6 Commits
Author SHA1 Message Date
Lotus CIandClaude Opus 5.5 df395776b4 fix(security): widget API only accepts messages from the widget's own frame
CI / Build & Quality Checks (push) Successful in 5m0s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 12s
CI / Trigger Desktop Build (push) Successful in 7s
CI / Playwright smoke (e2e) (push) Successful in 13m13s
matrix-widget-api's host transport handled a message from ANY window on the
page as long as it carried the widget's id; its strictOriginCheck only
compares with the host's own origin and is off by default. The call's id is
the fixed 'call-embed', so any other frame (a room widget, a URL-preview
embed) could post fromWidget actions as the call. Reproduced locally: an
opaque-origin frame posting one io.lotus.hotkey keydown for the PTT key
turned a push-to-talk user's mic on ("● Live").

restrictWidgetMessages() swaps each ClientWidgetApi transport's listener
for one that requires ev.source === the widget iframe's window and
ev.origin === the widget's origin. Applied to the call and to room widgets
(so one widget can't impersonate another). Verified: the spoof no longer
opens the mic; PTT/deafen from inside the call, screenshare, speaking
indicator and room widgets (capability prompt, send, live events) unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-26 22:22:57 -04:00
Lotus CIandClaude Opus 5.5 c1661e48fa feat(widgets): ask before a room widget may read or send in the room (#205)
CI / Build & Quality Checks (push) Successful in 1m54s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 13s
CI / Trigger Desktop Build (push) Successful in 7s
CI / Playwright smoke (e2e) (push) Successful in 9m15s
Room widgets were limited to display-only capabilities because the driver
couldn't serve anything else. Now:

- classifyWidgetCapabilities: display caps are still granted silently; reading
  or sending events/state in the widget's OWN room is offered to the user;
  everything else (other rooms' timelines, to-device, account data, uploads,
  user directory, delayed events) stays denied. Writing protected state
  (power levels, join rules, encryption, membership, ACLs, widgets, …) is
  never offered, and the driver refuses it again at send time.
- WidgetPermissionPrompt names the widget, the host that runs it and who added
  it; each request in plain words ("Send messages of type m.text in this
  room · as you"); reading is pre-ticked, sending is not; Deny / Escape grant
  nothing extra. "Remember my choice" stores it per viewer (localStorage),
  tied to the widget URL, so a changed URL asks again.
- GeneralWidgetDriver implements sendEvent / readRoomTimeline / readRoomState
  / readEventRelations, each refusing any room but the widget's own;
  RoomWidgetView feeds the room's live (decrypted) events and state updates,
  which ClientWidgetApi forwards only if the widget holds the capability.

Verified in Chromium with a cross-origin test widget against a local Synapse:
power-levels and timeline:* requests are never shown; after allowing
send+read the widget's message lands on the server, its power-levels write
is rejected, it receives live messages, and after a reload the remembered
choice skips the prompt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-24 21:17:18 -04:00
jaredandClaude Opus 5 34fe223aa4 chore(lint): remove unused eslint-disable directives
CI / Build & Quality Checks (push) Successful in 1m22s
CI / Trigger Desktop Build (push) Successful in 8s
Part of the #97 warning ratchet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-12 20:28:59 -04:00
jaredandClaude Opus 5 32e5fec6f3 fix(widgets): room widgets render again (bypass broken isValidUrl)
matrix-widget-api 1.17.0's WidgetParser rejects every URL (it compares
URL.protocol "https:" to "https"), so the widgets panel was always empty.
Build Widget objects from the raw state events with a correct scheme
check plus the existing origin check. Unit-tested against a real state
event.

Fixes #15

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-12 19:46:06 -04:00
jaredandClaude Opus 4.8 101e4116e8 refactor(DP15): centralize sendStateEvent as-any cast in typed helper
Add a typed `sendStateEvent(mx, roomId, eventType, content, stateKey?)`
helper in utils/room.ts that mirrors the DP16 account-data helper pattern.
The SDK's typed `sendStateEvent` overload rejects the fork's custom
`StateEvent` enum values, so every call site cast arg 2 to `any` (which
also collapsed the content type). The single `as any` cast now lives inside
the helper; a generic `content: T extends object` keeps each call site's
content type checked.

Route all 32 `mx.sendStateEvent(..., StateEvent.X as any, ...)` casts across
20 files through the helper. The 2 dynamic-string casts in developer-tools
(SendRoomEvent, StateEventEditor) pass a runtime string, not an enum value,
so they stay as-is.

No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 23:32:50 -04:00
jaredandClaude Opus 4.8 43f4ceb45d feat(rooms): Room Widgets (MSC1236 im.vector.modular.widgets)
Phase C.1 of the protocol-gaps roadmap, gate-green (693 tests). Generalizes the
Element Call widget host into a general room-widget feature:
- StateEvent.Widget + widgetsPanelAtom + useRoomWidgets (WidgetParser).
- RoomWidgetView: sandboxed-iframe host via ClientWidgetApi with a conservative
  GeneralWidgetDriver (approves only benign display caps — no room-event
  send/read/to-device). Blocks same-origin widget URLs (sandbox breakout guard).
- WidgetsPanel: list / open / add / remove, PL-gated on im.vector.modular.widgets,
  https + non-same-origin URL validation. Mounted like the media gallery (header
  toggle + 3-way content-panel exclusivity + mobile full-screen overlay).
- Tested URL/capability/id helpers.

Requires the prod CSP frame-src widening (matrix repo) for external widgets.
v1 cuts (capability consent prompt, Jitsi/sticker types, user widgets) noted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 13:27:23 -04:00