ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Cherry Studio Mini App manifest.json 完全指南:App ID 规则、权限声明展开与网络主机白名单校验

Cherry Studio Mini App manifest.json 完全指南:App ID 规则、权限声明展开与网络主机白名单校验 Cherry Studio Mini App manifest.json 完全指南App ID 规则、权限声明展开与网络主机白名单校验【免费下载链接】cherry-studioAI productivity studio with smart chat, autonomous agents, and 300 assistants. Unified access to frontier LLMs项目地址: https://gitcode.com/GitHub_Trending/ch/cherry-studioMini App 包的manifest.json是作者与宿主之间的契约文件它在包被预览时校验一次解包后再校验一次且两次必须完全一致否则安装被拒。本文基于当前仓库文档 manifest.md 与核心实现 miniAppManifest.ts 逐字段讲解 manifest 的取值规则、appId 命名约束、权限门禁none/grant/sibling体系、网络主机白名单语义以及从源码层面印证“同意卡上看到的授权 实际落盘的授权”这一安全不变量。一、manifest.json 的定位预览与安装双校验的契约manifest.json位于.miniapp包根目录。从源码结构看它的生命周期贯穿安装全流程预览阶段无论是本地文件、URL 还是内置应用三种来源的预览入口installFlow.ts 中的previewFileForInstall/previewUrlForInstall/previewBuiltinForInstall都会解析并校验 manifest把字段与展开后的权限列表交给渲染进程的同意卡展示确认阶段用户点确认后confirmFromFile会先对比压缩包 SHA-256再解包并调用assertManifestUnchanged用JSON.stringify逐字节比较解包出的 manifest 与同意卡上展示的 manifest——两者不一致直接抛出 Package file changed since preview 并拒绝安装见 installFlow.ts#L434-L438。这意味着一个在用户同意后、安装前被换掉 manifest 的包必然被拒绝。同意卡展示的内容就是最终被执行的授权边界这也是该文档反复强调“两次校验必须匹配”的原因。此外包相对路径有三条硬性规则由PackageRelativePathSchema实现见 miniAppManifest.ts#L198-L206必须使用 POSIX 分隔符/不允许绝对路径、不允许反斜杠任何一级目录都不能是..首级目录不能使用保留目录__cherryMINI_APP_RESERVED_DIR运行时代宿主资产见 miniAppManifest.ts#L12。二、字段逐项说明以下是完整的字段表继承自原文档并对照MiniAppManifestSchema实现字段必填类型规则id是string反向 DNS 应用 ID规则见下文“App id”一节。它成为应用的源origincherry-miniapp://id/name是本地化文本每个值 ≤ 64 字符description是本地化文本每个值 ≤ 200 字符。会显示在同意卡上——要说明这个应用是做什么的version是string合法 semver≤ 32 字符。更新要求版本号严格递增entry是包相对路径打开时加载的文档。必须存在且是常规文件解包后由assertExtractedTree用statSync().isFile()复查目录或符号链接都不算数见 archive.ts#L88-L102icon否{ path, sha256 }要么都写、要么都不写。sha256是图标字节的 SHA-256 小写十六进制摘要安装和更新时都会用真实字节验证图标条目 ≤ 5 MBreleaseNotes否本地化文本每个值 ≤ 500 字符。描述当前版本改了什么纯文本更新时渲染在权限 diff 下方permissions否默认[]string[]必授权限≤ 32 项。用户不接受全部则安装被拒optionalPermissions否默认[]string[]在同一张卡片上默认勾选提供——用户取消不需要的项——且之后可撤销。通配符展开后不得与permissions重叠network否默认[]string[]cherry.network.fetch可到达的主机。≤ 20 项、唯一、裸主机名update否{ url, urlCn? }宿主检查更新的地址。urlCn是可选的国内加速镜像必须提供相同字节从本地文件安装的包会忽略该字段两个值得注意的实现细节version强制 semver源码注释解释得很直白——普通字符串会让更新检查退化为字典序比较1.10.0 1.9.0并且给服务器推送降级版本留下空间因此用semverValid校验miniAppManifest.ts#L296-L304icon.sha256让图标变更“可见”更新检查时宿主手里只有新旧两份 manifest只比较路径会漏掉“icon.png路径不变但字节变了”这种最常见的换脸方式摘要在安装和更新时都拿真实字节验证声明不符即按篡改包拒绝。2.1 本地化文本Localized text本地化字段可以是纯字符串也可以是按键为语言代码的对象至少要有en或zh之一其他语言代码可选最多 20 个键name: My Game name: { en: My Game, zh: 我的游戏, ja: マイゲーム }解析链resolveLocalizedTextminiAppManifest.ts#L266-L270精确 localezh-TW→ 语言子标签zh→en→zh。只写一个zh就能覆盖zh-CN、zh-TW、zh-HK后两步必然至少命中一个正是 schema 强制en/zh二选一的保证。“至少一个en/zh”的设计意图源码注释是强制要求作者为其没打算服务的语言写文案只会得到占位符而非翻译而这条要求保证了任何 locale 的解析都有一条确定终止的兜底链。2.2 App id 规则App id 的正则原文档给出的等价形式^(?:[a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])(?:\.(?:[a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9]))*$规则原因只允许小写字母、数字、.和-无下划线不能以-开头或结尾id 占据 URL 的 host 位置。Chromium 会把 host 小写化两个仅大小写不同的 id 会塌缩成同一个 origin——也就是共享同一份存储≤ 120 字符id 同时被用作安装目录名和 journal 文件名第一个 label 不能是 Windows 设备名con、prn、aux、nul、com0–com9、lpt0–lpt9con.example.app在 Windows 上无法创建为目录即使带扩展名也不行com.example.con没问题——只有第一个 label 起作用com.cherrystudio.*为保留前缀官方应用专用任何来自其他来源的包使用该前缀都会被拒绝实现对照miniAppManifest.ts#L148-L173MiniAppIdSchema用正则 .max(120)WINDOWS_RESERVED集合 refine 实现上述规则。Windows 设备名判断只取id.split(.)[0]与文档一致官方前缀常量MINI_APP_OFFICIAL_ID_PREFIX com.cherrystudio.。值得注意的是该前缀不在 schema 层强制——schema 不知道包来自哪里且同一 schema 还被复用来解析 journal 文件名和解绑参数拒绝非官方来源使用保留前缀的规则由安装器在拿到source后执行assertOfficialNamespace。官方应用的信任锚点是编译期常量MINI_APP_OFFICIAL_ORIGINS [https://cherryai.com]源码注释明确说明共享主机如https://github.com/CherryHQ/不合格因为 originscheme host port无路径会把其他租户放进信任边界内且其下载 URL 会重定向与该设计redirect: error的原则冲突。三、权限体系叶子、通配符与三种门禁权限条目的取值要么是叶子file.save要么是命名空间通配符file.*。通配符只是作者编写时的简写它在同意时展开为当时存在的叶子集合且从不落盘存储。原因是源码注释原话大意存储下来的通配符会持续匹配 Cherry 未来新增的方法——等于宿主悄悄扩大了用户多年前授予的权限这与“更新不得扩大权限”是同一类失败只是作者换成了 Cherry 自己。展开逻辑见expandPermissionsminiAppManifest.ts#L107-L118通配符按命名空间前缀匹配当前MINI_APP_PERMISSIONS表中的叶子sibling和none方法永远不进入结果集因为它们不可授予。3.1 方法门禁表只有grant门禁的方法可以声明。sibling方法在其命名空间内任一叶子被授予后立即可用none方法无需任何授权。完整方法表与MINI_APP_METHODS常量一一对应miniAppManifest.ts#L37-L75方法门禁声明方式app.getInfonone—app.getPermissionsnone—ai.chatgrantai.chat或ai.*ai.getCapabilitiessibling—跟随任一ai.*授权ai.cancelnone—storage.get/set/delete/keysgrant叶子或storage.*storage.usagesibling—跟随任一storage.*授权file.save/load/list/delete/exportgrant叶子或file.*file.usagesibling—跟随任一file.*授权notification.showgrantnotification.show或notification.*clipboard.read/writegrant叶子或clipboard.*network.fetchgrantnetwork.fetch或network.*门禁设计的取舍在源码注释里有完整论述举两例ai.cancel是 none 而非 sibling停止自己正在花钱的调用不是能力给它设门禁只会让“止损”比“花钱”更难触达sibling是内省调用没有storage.usage的应用照样会一直写入直到触发配额错误没有ai.getCapabilities的应用在用户换模型时无法降级——这正是该方法存在的意义。对一个没有保护对象却有真实破坏性效果的“权限”不算权限。运行时闸门assertMethodAllowedgrants.ts#L170-L181驱动自MINI_APP_METHODS表且只做精确匹配、刻意不做前缀/通配匹配——如果调用时还匹配通配符意味着 Cherry 明天新增的方法已被今天授予的授权覆盖。这与“通配符只在同意时展开一次”形成闭环。用户永远不会直接看到这些方法名同意卡与详情面板展示的是渲染端文案目录下miniApp.permission.*的本地化文案命名空间标题与描述、每个叶子一个标签。新增一个grant方法必须同时补充en-us和zh-cn两套文案否则契约测试会失败——这是把“可声明权限”与“用户可见文案”绑定在一起的工程约束。3.2 跨字段规则校验期全部拒绝以下违规在MiniAppManifestSchema.superRefineminiAppManifest.ts#L349-L387中实现打包/安装校验时即报错规则违规示例一个叶子展开后不能既在必授又在可选里permissions: [storage.*]optionalPermissions: [storage.get]network主机必须存在某处声明了network.*权限有network: [api.example.com]但没有network.fetch声明了network.*权限必须至少有一个主机permissions: [network.fetch]且network: []第一条特别容易漏[storage.*]与[storage.get]文本上不重叠但展开后重叠——这是把必授权项伪装成可撤销选项的常见手法所以重叠检查放在展开之后做。第三条的实现上按命名空间前缀p.startsWith(network.)而非点名network.fetch判断源码注释解释一旦未来出现第二个network.*方法点名式的检查会悄悄失效。3.3 撤销语义必授权限安装后不可撤销唯一移除方式是卸载可选权限可在应用详情面板撤销并重新授予下一次调用即生效当前授权状态通过cherry.app.getPermissions()查询该方法是 none 门禁——它报告的是调用者自己的授权状态无需保护对象“声明”与“已授予”在存储上是分开的grants.ts 开头注释manifest 记录的是声明数据库miniAppGrantTable记录的是用户实际同意的授予“这次更新是否扩大了权限”就是两者的 diffdiffDeclaredSets且刻意以“旧声明 vs 新声明”计算——用户已撤销的叶子不会被一次未变更的更新重新报告为“新权限”回滚也不会把用户主动撤销的授权还回去。四、网络主机白名单network是作用域不是权限network是network.fetch的作用域scope本身不是一种权限——单个主机无法被单独撤销无法撤销的“权限”只是参数。条目是精确匹配的裸主机名不带 scheme、路径、端口或通配符network: [api.example.com, cdn.example.com]cherry.network.fetch接受的请求满足https://协议、默认端口显式:443会被 URL 解析器规范化掉其他端口直接拒绝、且 hostname 精确命中白名单。api.example.com不覆盖www.api.example.com也不覆盖example.com。更新中新增主机会在更新卡上展示并要求同意diffDeclaredHostsgrants.ts#L140-L143。主机名在 schema 层还有一条常被忽略的规则HostnameSchemaminiAppManifest.ts#L279-L282最后一个 label 是纯数字的主机名在安装期就被拒绝而不是运行期才发现——这是 WHATWG URL 规范的“ends in a number”规则这类 host 会被 URL 解析器读成 IPv4 地址导致network.fetch拒绝所有对该主机的请求安装期放行等于承诺了一个宿主永远无法兑现的访问。运行时匹配集中在一个函数isAllowedUrlnetwork.ts#L76-L89注释强调“一个算法定义一次”主机名白名单有十种看似合理的实现其中后缀匹配、父域匹配都是危险的。此外该能力还做了白名单之外的纵深防御请求发出前 DNS 解析全部记录任一解析结果命中私网/链路本地地址段127/8、10/8、169.254/16、100.64/10等即拒绝防作者控制 DNS 把声明主机解析到内网形成 SSRF 代理NAT64/6to4/Teredo 前缀整体拒绝198.18.0.0/15 刻意放行以兼容 Fake-IP 类代理Host、Cookie等禁止头被过滤Host决定反向代理如何路由是主机名白名单最重要的绕过面重定向直接以redirect: error拒绝——重定向出的目标不在白名单的约束范围内。五、完整示例{ id: com.example.mygame, name: { en: My Game, zh: 我的游戏 }, description: { en: A tiny sample game., zh: 一个小样例游戏。 }, version: 1.0.0, entry: index.html, icon: { path: icon.png, sha256: 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 }, permissions: [ai.chat, storage.*, file.save, file.load], optionalPermissions: [notification.show, network.fetch], network: [api.example.com], releaseNotes: { en: Fixes a save bug., zh: 修复了一个存档问题。 }, update: { url: https://example.com/mygame/manifest.json, urlCn: https://cdn.example.cn/mygame/manifest.json } }注意示例里optionalPermissions声明了network.fetch而network恰好有一个主机——这正是跨字段规则要求的“成对出现”可选权限同样参与该检查源码检查的是 required 与 optional 合并后的canReachNetwork。六、体积与结构限制约束上限manifest.json条目256 KB归档解压前50 MB解压后总量100 MB归档内条目数2000图标条目5 MB这些常量集中在 miniAppManifest.ts#L128-L132MINI_APP_MAX_PACKAGE_BYTES等与 archive.ts#L34MAX_ENTRIES 2000。源码注释解释了两条设计逻辑所有限额都在对应内存分配之前执行是内存的边界而不是事后报告归档上限50 MB刻意低于解压上限100 MB压缩是攻击者的杠杆解包后是发货体积两倍属正常一千倍则不是。manifest 256 KB 的额度界的是传输而非结构——所以network还要在 schema 层加“≤ 20 项且唯一”的约束重复是拒绝而非静默去重静默折叠会让作者误以为配置生效了否则一个合法的大 manifest 可以塞下数千个主机每个都变成同意卡上一行最终故障形态是“不可读的权限列表”。七、与 distribution manifest 的关系update.url上提供的 manifest 是包内这份 manifest 加上一个package块包下载地址、sha256、size可选urlCn与iconUrl完整规则见 packaging.md 的 Distribution manifest 一节。schema 层对应两个不同的类型包内用的是MiniAppManifestSchemaupdate可选——纯本地包合法地没有更新块分发用的是MiniAppDistributionManifestSchemaupdate与package均必填且强制update.urlCn与package.urlCn同生同灭、package.iconUrl必须存在icon.sha256可供校验。作者落地检查清单可以浓缩为id 全小写、首 label 避开 Windows 设备名、非官方来源不碰com.cherrystudio.*name/description至少提供en或zh长度在 64/200 字符内权限按需声明通配符只用于省事重叠的必授/可选声明会在打包期报错要网络就必须成对声明network.*权限与主机列表主机写精确的裸域名提供update块时按 distribution manifest 规则发布urlCn提供相同字节升级只升 semver 版本号releaseNotes只写当前版本的变更。所有字段语义的最终依据是 miniAppManifest.ts 中的 zod schema 与MINI_APP_METHODS门禁表——安装器、同意卡、运行时授权三处都读同一份定义因此文档与实现之间不存在第二套真值来源。【免费下载链接】cherry-studioAI productivity studio with smart chat, autonomous agents, and 300 assistants. Unified access to frontier LLMs项目地址: https://gitcode.com/GitHub_Trending/ch/cherry-studio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表