收口移动外链桥接系统调用
将 Expo app.openExternalUrl 的 Linking 调用收口到 navigation 模块 让移动 dispatch 只委托外链请求载荷 同步移动壳配置门禁和共享记忆
This commit is contained in:
@@ -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 模块,配置检查会拒绝分发层直接持有分享状态、生成分享文本或写入分享剪贴板结果。
|
||||
|
||||
Reference in New Issue
Block a user