Skip to content

Fix (critical): scope app bridge responses/events to their webview#794

Open
Rigidity wants to merge 1 commit into
appsfrom
fix/apps-bridge-event-scope
Open

Fix (critical): scope app bridge responses/events to their webview#794
Rigidity wants to merge 1 commit into
appsfrom
fix/apps-bridge-event-scope

Conversation

@Rigidity

@Rigidity Rigidity commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Severity: Critical (C1)

Problem

Bridge responses and per-app runtime events are delivered with Emitter::emit. In Tauri 2.10, Webview::emit routes to manager.emit, which iterates all webviews and delivers to every JS listener registered for that event name — regardless of which webview handle you call it on. The scoped emit_to/emit_filter variants were never used.

Because approval-gated results (notably wallet.getSecretKey, and signed wallet.sendXch results) are delivered over the sage-bridge:response rail, a second user app running concurrently (taskbar runtimes allow multiple live apps) can register getCurrentWebview().listen('sage-bridge:response', …) and read another app's payload directly via event.payload — including secret key material. The same applies to the sage-bridge:event / sage-system-bridge:* rails.

Fix

Deliver sage-bridge:response and the user/system runtime-event rails with emit_to(<app webview label>, …) so each payload reaches only the originating app's webview. The SDK subscribes via getCurrentWebview().listen(...) (an EventTarget::Webview { label } listener), which emit_to(AnyLabel { label }) matches exactly.

The broadcast to the trusted main Sage webview (apps:runtime-event, consumed by AppsContext) is intentionally left as-is and documented, because the main UI subscribes with the core listen (target Any) and that channel carries host-level state, not per-app secrets.

Follow-up (not in this PR)

As defense-in-depth, scope the core:event:allow-listen capability in apps.json / system-apps.json to an allowlist of event names so an app webview cannot subscribe to apps:runtime-event or the opposite rail at all. The current Tauri ACL schema in gen/schemas doesn't expose per-event scoping, so this needs a capability/schema change tracked separately.

Testing

No local build (per request); relying on CI. Worth adding an integration test asserting app B never receives app A's sage-bridge:response.


Note

High Risk
Security-critical fix for wallet/bridge secret leakage across concurrent apps; behavior change is narrow but touches authentication-sensitive delivery paths.

Overview
Fixes a critical cross-app leak where sage-bridge:response and per-app sage-bridge:event / sage-system-bridge:event traffic was sent with Tauri emit, which in 2.10 can deliver to every webview listening on that event name—not only the originating app.

emit_bridge_response_to_app and emit_runtime_event_to_app_id now use emit_to with the app's webview label so bridge responses and user/system runtime events reach only that app's webview. Host-level apps:runtime-event emissions to the trusted Sage main webview stay on emit; a comment documents that split.

Reviewed by Cursor Bugbot for commit 616f9ee. Bugbot is set up for automated code reviews on this repo. Configure here.

Bridge responses and per-app runtime events were delivered with
`Emitter::emit`, which in Tauri broadcasts to every webview that has a
JS listener for the event name rather than only the intended one. Since
`wallet.getSecretKey` (and other approval-gated) results are delivered
over the `sage-bridge:response` rail, a second user app running
concurrently could subscribe to that event and capture another app's
secret key material and bridge results.

Deliver `sage-bridge:response` and the user/system runtime event rails
with `emit_to(<app webview label>, ...)` so each payload reaches only the
originating app's webview. The broadcast to the trusted main Sage webview
(`apps:runtime-event`) is unchanged and documented.
@Hadamcik

Hadamcik commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Cherry-picked minus the comment section
Hadamcik@4ce4f6a

Can be closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants