lotus(security): harden denoise base, audio-inject, decorations
CI / Build embedded bundle (push) Successful in 1m8s
CI / Publish to Gitea npm registry (push) Has been skipped

Holistic security audit findings:
- C1 (CRITICAL): force lotusDenoiseBase to same-origin before it reaches
  audioWorklet.addModule()/fetch — a crafted call-link param could
  otherwise load attacker JS/WASM as a worklet processing the live mic.
  Non-same-origin/malformed values fall back to bundled ./denoise/.
- H1 (HIGH): gate audio-inject behind explicit lotusAudioInject=1 (still
  acks the action so no transport hang) — it publishes under the local
  user's identity, so it must not be silently armed for every call.
- M1 (MED): cap the decoration roster at 512 entries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Lotus CI
2026-06-30 00:00:40 -04:00
co-authored by Claude Opus 4.8
parent 39b57db3b2
commit b8543c3fe1
3 changed files with 30 additions and 2 deletions
+5
View File
@@ -12,6 +12,7 @@ import { type IWidgetApiRequest } from "matrix-widget-api";
import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
import { widget } from "../widget";
import { LotusWidgetActions } from "./lotusActions";
import { lotusFlag } from "./lotusWidget";
/** Hard cap so a malformed/huge clip can't hold a published track open forever. */
const MAX_CLIP_MS = 30_000;
@@ -48,7 +49,11 @@ export function startLotusAudioInject(vm: CallViewModel): () => void {
const activeClips = new Set<() => void>();
const handler = (ev: CustomEvent<IWidgetApiRequest>): void => {
// Always ack so the transport doesn't hang, but only act when the host has
// explicitly opted in: audio-inject publishes under the local user's
// identity, so it must not be silently armed for every call.
void w.api.transport.reply(ev.detail, {});
if (!lotusFlag("lotusAudioInject")) return;
const data = ev.detail.data as
| { url?: unknown; volume?: unknown }
| undefined;