From b5b11192c409c998ff09d58da8377f4a9d4b855c Mon Sep 17 00:00:00 2001 From: Filippo Vecchiato Date: Tue, 18 Aug 2026 15:10:57 +0100 Subject: [PATCH 1/3] feat(container): gate WebRTC on a decision resolved before the product realm An async permission request inside the product's own realm is forgeable, so the host bakes the decision in as a literal and a fresh grant only applies on the next load. --- android/truapi-host/README.md | 9 +- .../kotlin/io/parity/truapi/TrUAPIHost.kt | 16 +- ios/truapi-host/README.md | 27 ++- .../TrUAPIHost/Resources/truapi-container.js | 21 +- .../Sources/TrUAPIHost/TrUAPIHost.swift | 16 +- js/container/package-lock.json | 38 ++++ js/container/package.json | 4 +- js/container/src/freeze.ts | 38 ++++ js/container/src/index.ts | 45 ++--- js/container/src/webrtc.test.ts | 189 ++++++++++++++++++ js/container/src/webrtc.ts | 60 ++++++ playground/src/lib/auto-test.ts | 2 +- playground/src/lib/webrtc-check.ts | 39 +++- 13 files changed, 456 insertions(+), 48 deletions(-) create mode 100644 js/container/src/freeze.ts create mode 100644 js/container/src/webrtc.test.ts create mode 100644 js/container/src/webrtc.ts diff --git a/android/truapi-host/README.md b/android/truapi-host/README.md index 71086bdd5..c8f8fa9cb 100644 --- a/android/truapi-host/README.md +++ b/android/truapi-host/README.md @@ -261,7 +261,14 @@ core.notifyChainClosed(chainConnectionId) // following `loadUrl` replaces, so the product would lose the endpoint. Scope // it to the product origin. The page reads `window.__truapi_localhost.url` and // passes it to `@parity/truapi`'s `createWebSocketProvider`. -val bootstrap = LocalhostBridgeBootstrap.script(endpoint.port, endpoint.token) +// A peek, never a prompt — see LocalhostBridgeBootstrap.script. Baked in as a +// literal because the container enforces it inside the product's own realm, +// where an async permission request would be forgeable. A fresh grant therefore +// only takes effect once the web view reloads. +val webRtcAllowed = core.permissionAuthorizationStatus( + PermissionAuthorizationRequest.Remote(RemotePermissionRequest(RemotePermission.WebRtc)) +) == PermissionAuthorizationStatus.AUTHORIZED +val bootstrap = LocalhostBridgeBootstrap.script(endpoint.port, endpoint.token, webRtcAllowed) main.post { val productUrl = "https://your-product.example/" if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) { diff --git a/android/truapi-host/src/main/kotlin/io/parity/truapi/TrUAPIHost.kt b/android/truapi-host/src/main/kotlin/io/parity/truapi/TrUAPIHost.kt index e1aa4726c..1425776b5 100644 --- a/android/truapi-host/src/main/kotlin/io/parity/truapi/TrUAPIHost.kt +++ b/android/truapi-host/src/main/kotlin/io/parity/truapi/TrUAPIHost.kt @@ -408,15 +408,26 @@ private class HostCallbackAdapter(private val bridge: HostBridge) : HostCallback object LocalhostBridgeBootstrap { /** * Returns a `