收口移动外链桥接系统调用

将 Expo app.openExternalUrl 的 Linking 调用收口到 navigation 模块

让移动 dispatch 只委托外链请求载荷

同步移动壳配置门禁和共享记忆
This commit is contained in:
2026-06-19 23:39:00 +08:00
parent 582fb20347
commit 2f37a143f1
4 changed files with 12 additions and 13 deletions
+7 -4
View File
@@ -1668,7 +1668,7 @@ if (
'const externalUrlPayload = normalizeHostBridgeExternalUrlPayload(',
) ||
!hostBridgeNavigationSource.includes(
'openMobileShellExternalNavigation(navigator, externalUrlPayload.url)',
'openMobileShellExternalNavigation(Linking, externalUrlPayload.url)',
)
) {
throw new Error(
@@ -1928,9 +1928,11 @@ for (const snippet of [
}
if (
!dispatchSource.includes('openMobileHostBridgeExternalUrl(Linking, request.payload)') ||
!dispatchSource.includes('openMobileHostBridgeExternalUrl(request.payload)') ||
!dispatchSource.includes('openMobileHostBridgeNativePage(navigation, request.payload)') ||
!dispatchSource.includes('reloadMobileHostBridgeWebView(navigation)') ||
dispatchSource.includes("from 'expo-linking'") ||
dispatchSource.includes('Linking.') ||
dispatchSource.includes('openMobileShellExternalNavigation') ||
dispatchSource.includes('resolveMobileShellWebViewUrl') ||
dispatchSource.includes('normalizeHostBridgeExternalUrlPayload') ||
@@ -1940,7 +1942,8 @@ if (
}
for (const snippet of [
'openMobileHostBridgeExternalUrl',
'openMobileShellExternalNavigation(navigator, externalUrlPayload.url)',
"import * as Linking from 'expo-linking'",
'openMobileShellExternalNavigation(Linking, externalUrlPayload.url)',
'normalizeHostBridgeExternalUrlPayload',
'openMobileHostBridgeNativePage',
'resolveMobileShellWebViewUrl',
@@ -1952,7 +1955,7 @@ for (const snippet of [
}
}
if (!hostBridgeNavigationSource.includes('openMobileShellExternalNavigation(navigator, externalUrlPayload.url)')) {
if (!hostBridgeNavigationSource.includes('openMobileShellExternalNavigation(Linking, externalUrlPayload.url)')) {
throw new Error(
'mobile shell HostBridge external URL flow must use the shared external navigation helper',
);
@@ -1,5 +1,3 @@
import * as Linking from 'expo-linking';
import { type HostBridgeRequest } from '../../../../packages/shared/src/contracts/hostBridge';
import {
captureImageFile,
@@ -56,10 +54,7 @@ export async function dispatchMobileHostBridgeRequest(
case 'appearance.getColorScheme':
return ok(request, getMobileAppearanceColorScheme());
case 'app.openExternalUrl':
return ok(
request,
await openMobileHostBridgeExternalUrl(Linking, request.payload),
);
return ok(request, await openMobileHostBridgeExternalUrl(request.payload));
case 'app.reloadWebView':
return ok(request, reloadMobileHostBridgeWebView(navigation));
case 'network.status':
@@ -1,3 +1,5 @@
import * as Linking from 'expo-linking';
import {
type HostBridgeError,
type NavigateNativePagePayload,
@@ -17,7 +19,6 @@ import {
} from './protocol';
export async function openMobileHostBridgeExternalUrl(
navigator: MobileShellExternalNavigator,
payload: unknown,
) {
const externalUrlPayload = normalizeHostBridgeExternalUrlPayload(
@@ -28,7 +29,7 @@ export async function openMobileHostBridgeExternalUrl(
}
if (
!(await openMobileShellExternalNavigation(navigator, externalUrlPayload.url))
!(await openMobileShellExternalNavigation(Linking, externalUrlPayload.url))
) {
throw {
code: 'host_error',
@@ -26,7 +26,7 @@
- 2026-06-18 移动壳 WebView 导航收紧:Expo WebView 自身拦截外域导航时复用 HostBridge 外链协议白名单,只把 `http:``https:``mailto:``tel:` 交给 `Linking.openURL``javascript:``file:`、相对异常路径等危险目标直接阻断,避免离开同源主站后仍保留完整 HostBridge。
- 2026-06-19 移动壳 WebView 外链协议共源:`apps/mobile-shell/src/shell/navigation.ts` 的 WebView 外链离壳判断必须调用共享 `normalizeHostBridgeExternalUrl`,不得在 shell 层另写协议判断;`apps/mobile-shell/scripts/check-config.mjs` 会拒绝重新硬编码 `mailto:` / `tel:` / `javascript:` 等协议分支,`navigation.test.ts``HOST_BRIDGE_EXTERNAL_URL_PROTOCOLS` 反查当前允许协议。
- 2026-06-19 移动壳 WebView 外链打开收口:Expo WebView 外链拦截统一调用 `openMobileShellExternalNavigation(Linking, request.url)`,该 helper 先复用共享外链协议 normalizer,再调用 `canOpenURL` 确认系统可处理,最后才 `openURL`;系统不能打开或 URL 被拒绝时只阻断留壳,不伪造成功也不把危险协议交给系统。`ShellApp` 不再内联 `Linking.canOpenURL` / `Linking.openURL` Promise 链,移动壳配置检查和 `navigation.test.ts` 会覆盖该顺序。
- 2026-06-19 移动壳外链打开 helper 共用:Expo WebView 外域拦截和 HostBridge `app.openExternalUrl` 都必须复用 `openMobileShellExternalNavigation` 执行系统外链打开动作;HostBridge 分支仍先调用 `normalizeHostBridgeExternalUrlPayload` 保留 payload 错误语义,但不再单独维护 `Linking.canOpenURL` / `Linking.openURL` 顺序。移动壳配置检查会拒绝 `app.openExternalUrl` 绕开该 helper,避免两条离壳路径漂移。
- 2026-06-19 移动壳外链打开 helper 共用:Expo WebView 外域拦截和 HostBridge `app.openExternalUrl` 都必须复用 `openMobileShellExternalNavigation` 执行系统外链打开动作;HostBridge 分支仍先调用 `normalizeHostBridgeExternalUrlPayload` 保留 payload 错误语义,`apps/mobile-shell/src/host-bridge/navigation.ts` 自己承接 `expo-linking` 系统 API 调用,不再让 `dispatch.ts` 直接导入 `Linking`维护 `Linking.canOpenURL` / `Linking.openURL` 顺序。移动壳配置检查会拒绝 `app.openExternalUrl` 绕开该 helper 或分发层重新导入 `expo-linking`,避免两条离壳路径漂移。
- 2026-06-19 移动壳系统分享 URL 边界:Expo `share.open` 调用 React Native 系统分享面板前,只允许把 `url``href``path``targetPath``work` 归一为 `https://app.genarrative.world` 同源公开 URL;外域、协议相对 URL、`javascript:` 等危险目标必须返回 `invalid_request`,且显式非法 payload 不得回退到之前缓存的 `share.setTarget` 目标。分享实现复用移动壳入口 URL 的生产主站 origin,配置检查会拒绝重新声明同值 origin 或移除协议相对 URL 拦截。
- 2026-06-19 桌面壳系统分享 URL 边界:Tauri `share.open` 写入系统剪贴板前同样只允许把 `url``href``path``targetPath``work` 归一为 `https://app.genarrative.world` 同源公开 URL;外域、协议相对 URL、`javascript:` 等危险目标必须返回 `invalid_request`,且显式非法 payload 不得回退到之前缓存的 `share.setTarget` 目标。桌面壳配置检查会拒绝移除同源分享 URL 归一和协议相对 URL 拦截。
- 2026-06-19 原生壳分享桥接边界:Expo `share.setTarget` / `share.open` 的缓存目标、分享 payload 归一和系统分享调用统一收口在 `apps/mobile-shell/src/host-bridge/share.ts`Tauri `share.setTarget` / `share.open` 的缓存目标、分享文本生成、剪贴板 fallback 写入和 HostBridge 响应统一收口在 `apps/desktop-shell/src-tauri/src/host_bridge/share.rs`。两端 `dispatch` 只负责委托对应 share 模块,配置检查会拒绝分发层直接持有分享状态、生成分享文本或写入分享剪贴板结果。