Context
Raised in review of #298 (comment). Pre-existing behavior, not introduced by that PR, but retained background sessions widen the exposure.
Problem
WebPageSessionController.didInitiateNavigation schedules the cross-origin context swap asynchronously:
case .crossOrigin:
// TODO: move this to be synchronous work on decidePolicyFor:navigationAction instead
pendingContextSwap = Task { @MainActor [weak self] in
_ = await previousSwap?.value
await self.detachOldHandlerAndWait(from: webView)
self.contextId = newContextId
self.attachNewHandler(to: webView)
}
The destination page can begin executing JS while the previous origin's ScriptHandler and its BLE message processors are still attached, so a new (potentially untrusted) origin can interact with BLE state belonging to the origin it replaced. The window is bounded by detachOldHandlerAndWait, but nothing prevents the new origin's script from running inside it.
With multi-tab support, live sessions now persist in the background and cross-origin navigations happen on tabs the user is not watching, so the window is both more frequent and less observable.
What to build
Swap the context synchronously with the navigation decision rather than racing it:
- Perform the detach/attach in
decidePolicyFor:navigationAction, or
- Withhold the navigation decision until the detach has completed
Either way, the old origin's handlers must never be attached while the new origin's JS is executing. Remove the TODO at the call site once done.
Acceptance criteria
Context
Raised in review of #298 (comment). Pre-existing behavior, not introduced by that PR, but retained background sessions widen the exposure.
Problem
WebPageSessionController.didInitiateNavigationschedules the cross-origin context swap asynchronously:The destination page can begin executing JS while the previous origin's
ScriptHandlerand its BLE message processors are still attached, so a new (potentially untrusted) origin can interact with BLE state belonging to the origin it replaced. The window is bounded bydetachOldHandlerAndWait, but nothing prevents the new origin's script from running inside it.With multi-tab support, live sessions now persist in the background and cross-origin navigations happen on tabs the user is not watching, so the window is both more frequent and less observable.
What to build
Swap the context synchronously with the navigation decision rather than racing it:
decidePolicyFor:navigationAction, orEither way, the old origin's handlers must never be attached while the new origin's JS is executing. Remove the
TODOat the call site once done.Acceptance criteria