feat(desktop): call page on its own loopback origin, opt-in (#43)
CI / Build & Quality Checks (pull_request) Successful in 1m46s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 8s
CI / Playwright smoke (e2e) (pull_request) Successful in 10m24s
CI / Build & Quality Checks (pull_request) Successful in 1m46s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 8s
CI / Playwright smoke (e2e) (pull_request) Successful in 10m24s
The desktop app loads the bundled Element Call page from its own origin (http://localhost:<port>), so the call frame can read the app's storage (login token) and DOM — the hole #43 closed on the web by moving the page to call.chat.lotusguild.org. The desktop's local server can also answer on http://127.0.0.1:<port>: the same server and bundle, but a different origin (and still a secure context). resolveDesktopCallPageUrl loads the bundled page from there when the desktop config sets `desktopCallOrigin`: - only a loopback http origin on the SAME port as the app, no path, query or credentials; - only when the app itself runs on http://localhost (release builds; debug builds on tauri:// keep the same-origin page); - unset (every desktop build until cinny-desktop opts in, together with the server bind, CSP and permission changes it needs): unchanged. The web app is unchanged (elementCallUrl as before). Tested in a simulated desktop app (Tauri bridge stub + the desktop config.json, served on localhost and 127.0.0.1) against a local Synapse + LiveKit, two users: call page from http://127.0.0.1:<port>, parentUrl = the app origin; the frame gets SecurityError on parent.localStorage and parent.document (same-origin control: readable); join, speaking indicator, mic off/on, screenshare start/stop, layout switch and hang-up all work, no page errors — 12/12 in 5 of 6 runs, like the same-origin control (3 of 4; the misses on both sides were the local LiveKit connection). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
91f82d60e3
commit
7d7a379ce0
@@ -7,9 +7,9 @@
|
||||
* app loads it from that origin instead, so the call frame can no longer
|
||||
* reach this origin's storage (login token, crypto store) or service worker.
|
||||
*
|
||||
* Web only: the desktop app keeps its bundled copy (its CSP doesn't allow
|
||||
* another frame origin, and a network copy could drift from the bundle).
|
||||
* Anything that isn't an absolute https URL (http only on localhost, for
|
||||
* Web only: the desktop app keeps its bundled copy (a network copy could
|
||||
* drift from the bundle); see resolveDesktopCallPageUrl for how it isolates
|
||||
* it. Anything that isn't an absolute https URL (http only on localhost, for
|
||||
* development) is ignored, so a bad value falls
|
||||
* back to the bundled page instead of breaking calls.
|
||||
*/
|
||||
@@ -28,6 +28,40 @@ export const resolveCallPageUrl = (value: unknown, desktop: boolean): string | u
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* [Gitea #43] Desktop: the bundled call page from a second origin.
|
||||
*
|
||||
* The desktop app is served by its local server at http://localhost:<port>.
|
||||
* The same server answers on http://127.0.0.1:<port>, which is a different
|
||||
* origin (and still a secure context), so loading the bundled call page from
|
||||
* there cuts the call frame off from the app's storage (login token, crypto
|
||||
* store) without a network copy that could drift from the bundle.
|
||||
*
|
||||
* Only used when cinny-desktop sets `desktopCallOrigin` (it ships the server
|
||||
* and CSP changes this needs in the same release), only for a loopback http
|
||||
* origin on the SAME port as the app, and only when the app itself runs on
|
||||
* http://localhost (release builds). Anything else keeps the same-origin page.
|
||||
*/
|
||||
export const resolveDesktopCallPageUrl = (
|
||||
value: unknown,
|
||||
appOrigin: string,
|
||||
basePath: string,
|
||||
): string | undefined => {
|
||||
if (typeof value !== 'string' || value.trim() === '') return undefined;
|
||||
try {
|
||||
const app = new URL(appOrigin);
|
||||
const call = new URL(value);
|
||||
if (app.protocol !== 'http:' || app.hostname !== 'localhost' || !app.port) return undefined;
|
||||
if (call.protocol !== 'http:' || call.hostname !== '127.0.0.1') return undefined;
|
||||
if (call.port !== app.port || call.username || call.password) return undefined;
|
||||
if (call.pathname !== '/' || call.search || call.hash) return undefined;
|
||||
const base = basePath.replace(/\/+$/, '');
|
||||
return `${call.origin}${base}/public/element-call/index.html`;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
let callPageUrl: string | undefined;
|
||||
|
||||
export const setCallPageUrl = (url: string | undefined): void => {
|
||||
|
||||
Reference in New Issue
Block a user