收紧桌面命令权限产物门禁

要求 Tauri 自动生成权限目录必须存在且只包含 HostBridge command 权限

反查 host_bridge_request 自动权限文件的 allow / deny 声明

同步桌面壳权限边界记录
This commit is contained in:
2026-06-19 16:45:50 +08:00
parent 94f12f0c95
commit d224ffbf82
2 changed files with 17 additions and 12 deletions
+16 -11
View File
@@ -1029,25 +1029,30 @@ function extractNativeAppTauriInvokeCommands(source) {
function assertGeneratedPermissions(commandNames) {
if (!fs.existsSync(generatedPermissionDir)) {
return;
throw new Error('desktop shell generated permissions directory is missing');
}
const expectedPermissionFiles = new Set(
commandNames.map((command) => `${command}.toml`),
);
for (const entry of fs.readdirSync(generatedPermissionDir, {
const expectedPermissionFiles = commandNames
.map((command) => `${command}.toml`)
.sort();
const permissionEntries = fs.readdirSync(generatedPermissionDir, {
withFileTypes: true,
})) {
}).sort((left, right) => left.name.localeCompare(right.name));
assertSameList(
permissionEntries.map((entry) =>
`${entry.isDirectory() ? 'dir' : 'file'}:${entry.name}`,
),
expectedPermissionFiles.map((file) => `file:${file}`),
'desktop shell generated permission files',
);
for (const entry of permissionEntries) {
if (entry.isDirectory()) {
throw new Error(
`desktop shell generated permissions must not include nested directory ${entry.name}`,
);
}
if (!expectedPermissionFiles.has(entry.name)) {
throw new Error(
`desktop shell generated permission exposes an unexpected command: ${entry.name}`,
);
}
const permissionFile = new URL(entry.name, generatedPermissionDir);
const permissionSource = fs.readFileSync(permissionFile, 'utf8');
@@ -90,7 +90,7 @@
- 2026-06-18 桌面壳文件 bytes 校验:Tauri 图片 / 音频导入导出不得只信扩展名或 H5 声明 MIME;Rust 侧必须识别 PNG / JPEG / WebP、MP3 / MP4-M4A / WAV / OGG / WebM bytes 头部,要求导入文件扩展名对应 MIME 与真实 bytes 匹配,导出 payload 的 `mimeType``base64Data` 解码 bytes 匹配。不匹配返回 `invalid_request`,继续不暴露本机绝对路径或通用文件系统能力。配置检查和 cargo test 覆盖该边界。
- 2026-06-18 移动壳文件 bytes 校验:Expo 图片 / 音频导入导出不得只信系统 picker 返回 MIME、文件扩展名或 H5 声明 MIME;移动壳必须识别 PNG / JPEG / WebP、MP3 / MP4-M4A / WAV / OGG / WebM base64 bytes 头部,要求导入 MIME 归一结果与真实 bytes 匹配,导出 payload 的 `mimeType``base64Data` 解码 bytes 匹配。不匹配返回 `invalid_request`,不会写入缓存文件、调起系统分享或把内容回传给 H5。移动图片导出还必须按 MIME 给系统分享 / 保存面板补齐 `.png` / `.jpg` / `.webp` 文件名扩展,避免缓存文件名与真实图片类型漂移。配置检查和移动壳测试覆盖该边界。
- 2026-06-18 桌面壳 DevTools 边界:Tauri 主 WebView 配置必须显式 `devtools=false`Cargo 依赖不得启用 Tauri `devtools` feature;桌面壳本地调试走普通浏览器和 Vite,不把 debug / release 桌面包变成可打开浏览器检查器的调试容器。配置检查会拒绝主窗口 DevTools 或 release feature 被重新打开。
- 2026-06-18 桌面壳 Tauri 命令白名单:桌面壳源码、Tauri build manifest、主窗口 capability 和本地自动生成权限目录都只能暴露 `host_bridge_request` 一个受控 command;所有桌面能力继续在 Rust 内部按 HostBridge method 白名单分发,不新增可被 H5 直接 `invoke` 的 Tauri command,也不授予插件 JS guest API。检查脚本会拒绝多余 command、权限列表顺序漂移和残留的自动生成权限文件。
- 2026-06-18 桌面壳 Tauri 命令白名单:桌面壳源码、Tauri build manifest、主窗口 capability 和本地自动生成权限目录都只能暴露 `host_bridge_request` 一个受控 command;所有桌面能力继续在 Rust 内部按 HostBridge method 白名单分发,不新增可被 H5 直接 `invoke` 的 Tauri command,也不授予插件 JS guest API。检查脚本会拒绝自动生成权限目录缺失、权限文件集合漂移、多余 command、权限列表顺序漂移和残留的自动生成权限文件。
- 2026-06-18 桌面壳 capability 最小化:Tauri 主窗口 capability 只授予 `allow-host-bridge-request`,不得授予 `core:default``core:*:default`、任意 core 子权限或 dialog / fs / notification / opener / clipboard / deep-link / window-state 等插件权限。窗口、菜单、托盘、剪贴板、文件、通知和外链能力只能由 Rust 壳内部调用,再经 `host_bridge_request` 分发。
- 2026-06-18 HostBridge request id replayExpo 和 Tauri 壳都必须按 request id 回放首次完成结果;同 id 进行中的请求共享同一执行结果,已完成请求直接回放缓存响应,避免系统分享、外链、剪贴板、文件选择 / 保存、本地通知、窗口导航等宿主副作用被重复触发。两端配置检查和测试会锁住 replay 结构。
- 2026-06-18 HostBridge request envelope 校验:共享契约提供 `isHostBridgeMethod``normalizeHostBridgeRequestId`,Expo 壳直接复用,Tauri 壳镜像同一白名单和 id 规则;空 id、控制字符 id、超长 id 和未知 method 都必须在 replay / 能力分发前返回 `invalid_request`,已知但当前壳未实现的登录 / 支付等 method 才返回 `unsupported_method`