diff --git a/apps/desktop-shell/scripts/check-config.mjs b/apps/desktop-shell/scripts/check-config.mjs index 081a574fd..e1e1085e2 100644 --- a/apps/desktop-shell/scripts/check-config.mjs +++ b/apps/desktop-shell/scripts/check-config.mjs @@ -686,6 +686,8 @@ const requiredMainSnippets = [ 'WindowEvent::Focused', 'WindowEvent::DragDrop', 'host_bridge_event_script', + 'origin: window.location.origin', + 'source: window', 'resolve_desktop_network_status', 'network.statusChanged', 'file.imageDropped', diff --git a/apps/desktop-shell/src-tauri/src/main.rs b/apps/desktop-shell/src-tauri/src/main.rs index 08a7e1352..326242778 100644 --- a/apps/desktop-shell/src-tauri/src/main.rs +++ b/apps/desktop-shell/src-tauri/src/main.rs @@ -872,7 +872,7 @@ fn host_bridge_event_script(event: &str, payload: Value) -> Result { await assertion; }); + test('忽略非当前窗口来源伪造的 HostBridge 回包', async () => { + const channel = new MessageChannel(); + const postMessage = vi.fn((message: string) => { + const request = JSON.parse(message) as HostBridgeRequest; + window.dispatchEvent( + new MessageEvent('message', { + data: JSON.stringify({ + bridge: HOST_BRIDGE_PROTOCOL, + version: HOST_BRIDGE_VERSION, + id: request.id, + ok: true, + result: { + handled: 'forged', + }, + } satisfies HostBridgeResponse), + origin: window.location.origin, + source: channel.port1, + }), + ); + dispatchHostBridgeResponse({ + bridge: HOST_BRIDGE_PROTOCOL, + version: HOST_BRIDGE_VERSION, + id: request.id, + ok: true, + result: { + handled: 'native', + }, + }); + }); + window.ReactNativeWebView = { postMessage }; + + await expect(requestNativeAppHostBridge('share.open')).resolves.toEqual({ + handled: 'native', + }); + + channel.port1.close(); + channel.port2.close(); + }); + test('订阅 React Native WebView 注入的宿主事件', () => { window.ReactNativeWebView = { postMessage: vi.fn(), @@ -160,6 +201,8 @@ describe('nativeAppHostBridge', () => { canGoBack: true, }, }), + origin: window.location.origin, + source: window, }), ); unsubscribe(); diff --git a/src/services/host-bridge/nativeAppHostBridge.ts b/src/services/host-bridge/nativeAppHostBridge.ts index 4705f294d..3a0041fe0 100644 --- a/src/services/host-bridge/nativeAppHostBridge.ts +++ b/src/services/host-bridge/nativeAppHostBridge.ts @@ -132,6 +132,23 @@ function dispatchNativeEvent(event: HostBridgeEvent) { } } +function isNativeInjectedMessageEvent(event: MessageEvent) { + const nativeWindow = resolveNativeWindow(); + if (!nativeWindow) { + return false; + } + + if (event.source && event.source !== nativeWindow) { + return false; + } + + if (event.origin && event.origin !== nativeWindow.location.origin) { + return false; + } + + return true; +} + function ensureNativeBridgeListener() { const nativeWindow = resolveNativeWindow(); if (!nativeWindow || nativeBridgeListenerInstalled) { @@ -139,6 +156,10 @@ function ensureNativeBridgeListener() { } nativeWindow.addEventListener('message', (event) => { + if (!isNativeInjectedMessageEvent(event)) { + return; + } + const value = parseNativeMessage(event.data); if (isHostBridgeResponse(value)) { settleNativeResponse(value);