diff --git a/apps/mobile-shell/scripts/check-config.mjs b/apps/mobile-shell/scripts/check-config.mjs index 50151b8cf..316156035 100644 --- a/apps/mobile-shell/scripts/check-config.mjs +++ b/apps/mobile-shell/scripts/check-config.mjs @@ -955,6 +955,9 @@ if ( ) { throw new Error('mobile shell Android package must request only RECORD_AUDIO for same-origin H5 microphone gameplay'); } +if (appConfig.android?.permissions?.includes('android.permission.CAMERA')) { + throw new Error('mobile shell Android CAMERA permission must come from real camera plugins only'); +} for (const permission of blockedAndroidPermissions) { if (!appConfig.android?.blockedPermissions?.includes(permission)) { diff --git a/apps/mobile-shell/scripts/check-expo-config.mjs b/apps/mobile-shell/scripts/check-expo-config.mjs index c26f033ac..cb1597d91 100644 --- a/apps/mobile-shell/scripts/check-expo-config.mjs +++ b/apps/mobile-shell/scripts/check-expo-config.mjs @@ -212,6 +212,9 @@ assertSameSet( ['android.permission.CAMERA', 'android.permission.RECORD_AUDIO'], 'Android explicit permissions', ); +if (appConfig.android?.permissions?.includes('android.permission.CAMERA')) { + throw new Error('Expo source config must not hard-code Android CAMERA permission'); +} assertIncludes( expoConfig.android?.blockedPermissions, 'android.permission.MANAGE_EXTERNAL_STORAGE', diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 28438ab1b..8654ea4dc 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -2417,7 +2417,7 @@ ## 2026-06-18 原生壳身份与 capability 作用域门禁 - 背景:Expo 移动壳和 Tauri 桌面壳已经具备首批真实 HostBridge 能力,但如果包身份、桥版本、Android 默认权限、Tauri 窗口列表或 capability 文件在后续迭代中漂移,会把 H5 主站装进更宽的宿主权限面。 -- 决策:移动壳配置门禁固定 Expo `name`、`slug`、`userInterfaceStyle`、`assetBundlePatterns`、`extra.genarrativeHostBridgeVersion`,并要求 Android 默认 `permissions` 为空,只通过 `blockedPermissions` 阻断当前不需要的高风险权限;`apps/mobile-shell/scripts/check-config.mjs` 检查源 `app.json`,`apps/mobile-shell/scripts/check-expo-config.mjs` 检查 Expo CLI 最终解析出的 public config,防止 config plugin 或解析阶段引入身份、资源或权限漂移。新增权限必须先有真实宿主能力、系统权限说明和 H5 fallback。桌面壳配置门禁固定唯一 `label=main` 主窗口,`src-tauri/capabilities/` 只能存在 `main.json`,且该 capability 只能绑定 `windows=["main"]`、`permissions=["allow-host-bridge-request"]`,继续只暴露 `host_bridge_request` 一个受控入口。 +- 决策:移动壳配置门禁固定 Expo `name`、`slug`、`userInterfaceStyle`、`assetBundlePatterns`、`extra.genarrativeHostBridgeVersion`;Android 源 `app.json.permissions` 只能手写 `RECORD_AUDIO` 供同源 H5 实时声音玩法使用,`CAMERA` 必须只由真实 `expo-camera` / `expo-image-picker` 插件为扫码和拍摄能力生成到最终 Expo public config,仍通过 `blockedPermissions` 阻断当前不需要的高风险权限;`apps/mobile-shell/scripts/check-config.mjs` 检查源 `app.json`,`apps/mobile-shell/scripts/check-expo-config.mjs` 检查 Expo CLI 最终解析出的 public config,防止 config plugin 或解析阶段引入身份、资源或权限漂移。新增权限必须先有真实宿主能力、系统权限说明和 H5 fallback。桌面壳配置门禁固定唯一 `label=main` 主窗口,`src-tauri/capabilities/` 只能存在 `main.json`,且该 capability 只能绑定 `windows=["main"]`、`permissions=["allow-host-bridge-request"]`,继续只暴露 `host_bridge_request` 一个受控入口。 - 影响范围:`apps/mobile-shell/scripts/check-config.mjs`、`apps/mobile-shell/scripts/check-expo-config.mjs`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。 - 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。