禁止壳层伪声明登录支付能力
补充 Expo 与 Tauri 壳层登录支付能力禁伪声明检查 同步宿主壳方案和共享决策中的登录支付能力边界
This commit is contained in:
@@ -297,6 +297,7 @@ const sharedCapabilities = extractStringArrayExport(
|
||||
'HOST_BRIDGE_CAPABILITIES',
|
||||
);
|
||||
const desktopCapabilities = extractDesktopCapabilities(main);
|
||||
const sdkBackedCapabilities = ['auth.requestLogin', 'payment.request'];
|
||||
const unknownDesktopCapabilities = desktopCapabilities.filter(
|
||||
(capability) => !sharedCapabilities.includes(capability),
|
||||
);
|
||||
@@ -306,6 +307,14 @@ if (unknownDesktopCapabilities.length > 0) {
|
||||
);
|
||||
}
|
||||
|
||||
for (const capability of sdkBackedCapabilities) {
|
||||
if (desktopCapabilities.includes(capability)) {
|
||||
throw new Error(
|
||||
`desktop shell must not declare ${capability} until a real SDK/channel flow is implemented`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.productName !== 'Genarrative') {
|
||||
throw new Error('desktop shell productName must be Genarrative');
|
||||
}
|
||||
@@ -433,6 +442,17 @@ assertSameList(
|
||||
'desktop shell dev hostCapabilities',
|
||||
);
|
||||
|
||||
for (const capability of sdkBackedCapabilities) {
|
||||
if (
|
||||
resolveHostCapabilitiesFromUrl(mainWindow.url).includes(capability) ||
|
||||
resolveHostCapabilitiesFromUrl(config.build?.devUrl ?? '').includes(capability)
|
||||
) {
|
||||
throw new Error(
|
||||
`desktop shell URL must not advertise ${capability} without a real SDK/channel flow`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const allowedPermissions = [
|
||||
'core:default',
|
||||
'allow-host-bridge-request',
|
||||
|
||||
@@ -144,6 +144,7 @@ const iosMobileCapabilities = extractStringArrayExport(
|
||||
);
|
||||
const mobileCapabilitySet = new Set(mobileCapabilities);
|
||||
const iosMobileCapabilitySet = new Set(iosMobileCapabilities);
|
||||
const sdkBackedCapabilities = ['auth.requestLogin', 'payment.request'];
|
||||
const unknownMobileCapabilities = mobileCapabilities.filter(
|
||||
(capability) => !sharedCapabilities.includes(capability),
|
||||
);
|
||||
@@ -162,6 +163,17 @@ if (unknownIosMobileCapabilities.length > 0) {
|
||||
);
|
||||
}
|
||||
|
||||
for (const capability of sdkBackedCapabilities) {
|
||||
if (
|
||||
mobileCapabilitySet.has(capability) ||
|
||||
iosMobileCapabilitySet.has(capability)
|
||||
) {
|
||||
throw new Error(
|
||||
`mobile shell must not declare ${capability} until a real SDK/channel flow is implemented`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const capability of iosMobileCapabilities) {
|
||||
const switchCase = `case '${capability}':`;
|
||||
if (
|
||||
@@ -419,6 +431,14 @@ if (!appSource.includes('capabilities: resolveMobileHostCapabilities()')) {
|
||||
throw new Error('mobile shell URL must use resolveMobileHostCapabilities()');
|
||||
}
|
||||
|
||||
for (const capability of sdkBackedCapabilities) {
|
||||
if (appSource.includes(`'${capability}'`) || appSource.includes(`"${capability}"`)) {
|
||||
throw new Error(
|
||||
`mobile shell URL must not advertise ${capability} without a real SDK/channel flow`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const capability of [
|
||||
'host.getRuntime',
|
||||
'appearance.getColorScheme',
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
- 2026-06-18 宿主 runtime 回读:主 App 启动时会通过真实 `host.getRuntime` 回读 Expo / Tauri runtime 并缓存过滤后的能力清单,能力来源为 URL `hostCapabilities` 与宿主真实回包的并集;裁剪壳或旧入口 URL 缺少 `hostCapabilities` 时也能启用真实声明能力,但仍不会仅凭 `native_app` 或 transport 存在推断能力可用。
|
||||
- 2026-06-18 壳能力防漂移:`npm run mobile-shell:typecheck` 与 `npm run desktop-shell:typecheck` 会校验 Expo / Tauri 壳声明的 capability 均来自共享 HostBridge 白名单,并校验壳 runtime 回包、H5 URL `hostCapabilities` 和实现分支保持一致;新增能力必须先更新契约和真实壳实现,再通过这些检查。
|
||||
- 2026-06-18 原生壳统一验收门禁:根级 `npm run check:native-shells` 统一执行 H5 HostBridge 关键测试、Expo 壳 typecheck / test / config smoke / Metro export smoke、Tauri 壳 typecheck / cargo test 和桌面 release `--no-bundle` 构建烟测;根级 `npm run check` 会在 lint、主站测试、构建和内容检查后继续执行该门禁,避免 HostBridge、两端壳、Expo managed config、移动端 production bundle 和桌面 release 入口验收散落成容易漏跑的单项命令。
|
||||
- 2026-06-18 登录 / 支付能力禁伪声明:`auth.requestLogin` 和 `payment.request` 保留在共享 HostBridge 契约中供未来真实接入,但 Expo / Tauri 壳在真实 SDK、渠道流程和后端契约落地前不得声明这些 capability,也不得把它们写入入口 URL `hostCapabilities`;两端检查脚本会拒绝伪声明,请求实际到达壳层时仍返回明确 unsupported 并让 H5 fallback。
|
||||
- 2026-06-18 分享卡图片导出:新增 `file.exportImage` HostBridge capability,H5 分享卡下载在 native app 中优先把 canvas 生成的 base64 图片交给宿主导出;Expo 壳写缓存图片后交给系统分享 / 保存面板,Tauri 壳通过系统保存对话框写入图片字节。该能力只接受 `image/png` / `image/jpeg` / `image/webp`、单次 5 MiB 内图片数据,成功只返回文件名和字节数,不暴露本机绝对路径;宿主未声明时保留浏览器下载。
|
||||
- 2026-06-18 应用角标能力:新增 `app.setBadgeCount` HostBridge capability,H5 只传 `0-99999` 整数并在宿主未声明时静默 fallback;Expo 壳只在 iOS 声明并通过 React Native `PushNotificationIOS` 设置应用图标角标,Android 不声明、不伪造成功;Tauri 壳通过主窗口 `set_badge_count` 设置任务栏角标,底层平台不支持时返回真实错误。
|
||||
- 2026-06-18 草稿生成未读角标:平台壳层把“可见作品架里未读的草稿生成完成更新”同步到 `app.setBadgeCount`;同一草稿的 work/profile/session 等多个恢复 ID 只计 1,已读、失败、生成中和不可见草稿不计入。该角标只消费已有 HostBridge 能力,宿主不支持或设置失败不影响 H5 红点、作品架或后端状态。
|
||||
|
||||
@@ -112,8 +112,8 @@ type HostBridgeEvent = {
|
||||
| --- | --- | --- | --- |
|
||||
| `host.getRuntime` | 返回宿主、平台、版本和能力清单 | 支持 | 支持 |
|
||||
| `appearance.getColorScheme` | 读取宿主当前配色模式 | 支持系统 Appearance 读取 | 支持窗口 theme 读取 |
|
||||
| `auth.requestLogin` | 打开宿主登录或账号绑定流程 | 支持 | 可先返回不支持 |
|
||||
| `payment.request` | 发起宿主支付 | 依平台策略接入 | 桌面二维码 / 外部浏览器 |
|
||||
| `auth.requestLogin` | 打开宿主登录或账号绑定流程 | 未声明,继续走 H5 fallback | 未声明,继续走 H5 fallback |
|
||||
| `payment.request` | 发起宿主支付 | 未声明,等待真实 IAP / Google Play Billing / 国内渠道流 | 未声明,等待真实桌面支付渠道流 |
|
||||
| `share.setTarget` | 同步当前作品分享目标 | 支持 | 支持 |
|
||||
| `share.open` | 打开分享动作 | 支持系统分享面板 | 复制分享文本到剪贴板 |
|
||||
| `navigation.openNativePage` | 打开受控宿主页 | 支持同源 H5 route | 支持同源 H5 route |
|
||||
@@ -230,6 +230,7 @@ GameBridge 禁止:
|
||||
- 每个请求必须有超时,重复 `id` 不得重复执行支付、登录等非幂等动作。
|
||||
- 能力按 `capabilities` / `hostCapabilities` 下发,H5 会过滤未知能力,并根据声明结果决定是否展示入口、发起宿主请求或走 fallback;进入 `native_app` 后主 App 会再通过真实 `host.getRuntime` 回读一次宿主 runtime 并缓存能力,用来补齐裁剪壳或旧入口 URL 缺少 `hostCapabilities` 的场景。不能只凭 `native_app` 宿主类型假设能力可用。
|
||||
- 壳能力声明与两端壳测试必须通过 `npm run check:native-shells` 统一校验;排查单端问题时可再分别运行 `npm run mobile-shell:typecheck`、`npm run mobile-shell:test`、`npm run mobile-shell:config`、`npm run mobile-shell:export`、`npm run desktop-shell:typecheck`、`npm run desktop-shell:test` 或 `npm run desktop-shell:build -- --no-bundle`。声明的 capability 必须存在于共享 HostBridge 白名单,壳 runtime 回包、H5 URL `hostCapabilities`、壳实现、Expo managed config、移动端 production bundle 和桌面 release 构建入口不得漂移。
|
||||
- 登录和支付能力在真实 SDK、渠道流程、后端契约和失败回退全部落地前不得进入 Expo / Tauri capabilities,也不得写进入口 URL `hostCapabilities`;两端配置检查会拒绝 `auth.requestLogin` 和 `payment.request` 的伪声明。
|
||||
- 宿主壳不得把长期 token、支付密钥或用户敏感资料回传给 H5。
|
||||
- Tauri 禁止把 shell / fs 等高危插件作为默认能力暴露给主 WebView。
|
||||
- RN WebView 禁止打开任意 URL 后仍保留完整 HostBridge;跳外链只允许 `http:`、`https:`、`mailto:`、`tel:`,并使用系统浏览器或降级能力,危险协议直接阻断。
|
||||
@@ -317,6 +318,8 @@ GameBridge 禁止:
|
||||
|
||||
2026-06-18 追加:移动壳 Metro export 烟测进入统一验收。`npm run check:native-shells` 会执行 `npm run mobile-shell:export`,用 Expo CLI 分别为 Android 和 iOS 生成 production bundle,校验 Metro metadata 与 `AppEntry` bundle 已产出后清理临时 `.expo-export-smoke/` 目录。该烟测不生成 APK、AAB 或 IPA,也不写商店资料,只证明移动壳入口、Expo/RN 依赖和平台差异代码能被真实生产 bundler 打包。
|
||||
|
||||
2026-06-18 追加:登录 / 支付能力伪声明进入门禁。`auth.requestLogin` 和 `payment.request` 保留在共享 HostBridge 契约中供未来真实接入,但 Expo 与 Tauri 壳在没有真实 SDK、渠道流程和后端契约前不得声明这些 capability,也不得把它们写入 `hostCapabilities`;请求到达壳层时仍返回明确 unsupported,让 H5 fallback 承接。
|
||||
|
||||
2026-06-18 追加:两端壳的生产源码和配置禁止出现 mock / fake / placeholder / stub / TODO / FIXME 以及对应中文脚手架词;测试文件仍可使用 `vi.mock` 或等价测试替身。`apps/mobile-shell/scripts/check-config.mjs` 与 `apps/desktop-shell/scripts/check-config.mjs` 会扫描各自生产入口、配置和壳实现,防止把临时替身或占位文案带进可分发壳。
|
||||
|
||||
2026-06-18 追加:移动壳启动页与 Android adaptive icon 复用现有真实品牌图标 `apps/mobile-shell/assets/icon.png`,背景色固定为 H5 壳根背景 `#fffdf9`。该 PNG 是 1024x1024 RGBA 透明前景品牌资产,不新增占位图;Expo `splash` 使用同一图标 `contain` 展示,Android `adaptiveIcon.foregroundImage` 使用同一透明前景图,`check-config.mjs` 会校验图标尺寸、透明像素、启动页和 adaptive icon 配置。
|
||||
|
||||
Reference in New Issue
Block a user