From de77f593874257aeab57fefd247e552fed7188b4 Mon Sep 17 00:00:00 2001 From: kdletters Date: Sat, 20 Jun 2026 04:30:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B6=E5=8F=A3H5=E5=8E=9F=E7=94=9F=E5=88=86?= =?UTF-8?q?=E4=BA=AB=E7=9B=AE=E6=A0=87=E5=90=8C=E6=AD=A5=E8=BE=B9=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit H5 原生 App 分享目标同步前复用共享分享 payload 边界 H5 HostBridge 测试覆盖无效分享目标不发送原生请求 原生壳门禁反查 share.setTarget 发送前校验 --- scripts/check-native-shells.mjs | 11 ++++++ src/services/host-bridge/hostBridge.test.ts | 41 +++++++++++++++++++++ src/services/host-bridge/hostBridge.ts | 4 ++ 3 files changed, 56 insertions(+) diff --git a/scripts/check-native-shells.mjs b/scripts/check-native-shells.mjs index 2552b7655..172eb2c89 100644 --- a/scripts/check-native-shells.mjs +++ b/scripts/check-native-shells.mjs @@ -1093,6 +1093,17 @@ function assertH5HostBridgePayloadBoundaries() { 'H5 HostBridge facade must normalize share.open payloads with the shared share boundary', ); } + if ( + !h5HostBridgeSource.includes( + 'const normalizedPayload = normalizeHostBridgeShareOpenPayload(message);', + ) || + !h5HostBridgeSource.includes("normalizedPayload.status !== 'valid'") || + !h5HostBridgeSource.includes("'share.setTarget', {\n target: message,") + ) { + throw new Error( + 'H5 HostBridge facade must validate native share.setTarget payloads before sending them to native shells', + ); + } if ( !h5HostBridgeSource.includes( 'const normalizedPayload = normalizeHostBridgeExportTextPayload(params);', diff --git a/src/services/host-bridge/hostBridge.test.ts b/src/services/host-bridge/hostBridge.test.ts index 0b5c59364..2a829f3ac 100644 --- a/src/services/host-bridge/hostBridge.test.ts +++ b/src/services/host-bridge/hostBridge.test.ts @@ -878,6 +878,47 @@ describe('hostBridge', () => { expect(setHostShareTarget({ type: 'test' })).toBe(false); }); + test('原生 App 宿主分享目标同步前先拒绝无效目标', () => { + const invoke = vi.fn(); + window.history.replaceState( + null, + '', + nativeAppPath(['share.setTarget']), + ); + window.__TAURI__ = { + core: { + invoke: asTauriInvoke(invoke), + }, + }; + + expect(setHostShareTarget({})).toBe(false); + expect( + setHostShareTarget({ + title: '危险作品', + url: 'https://example.com/works/detail?work=PZ-1', + }), + ).toBe(false); + expect(invoke).not.toHaveBeenCalled(); + + expect( + setHostShareTarget({ + title: '暖灯猫街', + work: 'PZ-00000001', + }), + ).toBe(true); + expect(invoke).toHaveBeenCalledWith('host_bridge_request', { + request: expect.objectContaining({ + method: 'share.setTarget', + payload: { + target: { + title: '暖灯猫街', + work: 'PZ-00000001', + }, + }, + }), + }); + }); + test('原生 App 宿主通过 HostBridge 处理导航、登录和支付', async () => { const invoke = vi.fn(async (_command: string, args?: Record) => { const request = (args as { request: { id: string; method: string } }) diff --git a/src/services/host-bridge/hostBridge.ts b/src/services/host-bridge/hostBridge.ts index 9fc9a180c..54eebcaff 100644 --- a/src/services/host-bridge/hostBridge.ts +++ b/src/services/host-bridge/hostBridge.ts @@ -699,6 +699,10 @@ export function setHostShareTarget(message: unknown) { if (!canUseNativeHostCapability('share.setTarget')) { return false; } + const normalizedPayload = normalizeHostBridgeShareOpenPayload(message); + if (normalizedPayload.status !== 'valid') { + return false; + } void requestNativeAppHostBridge('share.setTarget', { target: message, }).catch(() => {