ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

HarmonyOS 7 DevEco CLI + Node.js:module.json5 权限声明与隐私清单的构建前对账脚本【鸿蒙心迹】

HarmonyOS 7 DevEco CLI + Node.js:module.json5 权限声明与隐私清单的构建前对账脚本【鸿蒙心迹】 这篇来自一次很典型的“代码没报错、构建也成功但提审前越看越心虚”的场景module.json5 里权限改过几轮隐私政策也改过几轮最后到底是不是一一对应靠人工翻两份文件很难放心。一、我想解决的不是审核规则而是提交前没人敢保证“这两份东西一致”应用上架前最容易让人疲劳的检查往往不是一个复杂 API而是配置之间的对账。这次 Demo 叫ReviewGuard。我给它设定的目标很窄构建前读取entry/src/main/module.json5中的requestPermissions再读取团队自己维护的一份tools/privacy-manifest.json比较两边权限集合。如果代码声明了权限但内部隐私清单里没有对应条目就标记为undeclared如果隐私清单里还留着项目已经不用的权限就标记为orphan。先强调一个边界privacy-manifest.json是我为了工程自检设计的内部文件不是华为应用市场要求上传的标准文件也不能替代隐私政策、SDK 隐私声明或审核材料。这个工具只做“研发仓库内的一致性检查”。官方上架说明里反复强调涉及第三方 SDK 时需要在隐私政策中明示其处理个人信息的目的、方式和范围部分 SDK 的合规指南还会要求在用户同意后再初始化。我的想法不是把法规变成脚本而是把最机械、最容易漏掉的配置差异提前拦在构建之前。本次 Demo 固定数据如下Build IDreview_guard_20261001_03App Version2.3.0(20300)Request Permissions 6Privacy Items 6Undeclared 0Orphan 0最终状态PASS检查时间2026-10-01 02:59:42二、先把“隐私清单”做成工程里能被机器读懂的结构以前我见过一种维护方式权限和用途全写在项目 Wiki 或 Word 表格里。人看很方便脚本没法稳定读取结果就是每次还是要人工核对。ReviewGuard 里我新建了一个内部文件{version:2.3.0(20300),permissions:[{name:ohos.permission.CAMERA,purpose:用于拍摄用户主动创建的内容},{name:ohos.permission.MICROPHONE,purpose:用于用户主动发起的语音录入}]}这段不是为了把所有隐私说明塞进 JSON而是解决一个更简单的问题让每一条系统权限都能在代码仓库里找到对应的业务用途。真实项目里我会给每个条目继续补featureOwner、policySection、enabled等字段但不会把法务文本原样复制进来。内部清单的角色应该是索引不是最终隐私政策。另一个取舍是权限名称必须使用module.json5里同样的原始值。不要自己再造别名例如不要一边叫CAMERA另一边叫cameraPermission。对账脚本越“聪明”后面越容易因为映射规则隐藏真正的差异。三、Node.js 脚本只做集合差不替你判断“这个权限该不该申请”核心脚本放在tools/review-guard.mjs。它做三件事读取module.json5读取内部清单比较两个集合。这段代码解决的是自动发现“声明了但没记录”和“记录了但没声明”的配置漂移importfsfromnode:fsimportpathfromnode:pathimportJSON5fromjson5constrootDirprocess.cwd()constmodulePathpath.join(rootDir,entry,src,main,module.json5)constprivacyPathpath.join(rootDir,tools,privacy-manifest.json)constmoduleJsonJSON5.parse(fs.readFileSync(modulePath,utf-8))constprivacyManifestJSON.parse(fs.readFileSync(privacyPath,utf-8))constrequestednewSet((moduleJson.module.requestPermissions??[]).map((item)item.name))constdeclarednewSet((privacyManifest.permissions??[]).map((item)item.name))constundeclared[...requested].filter((name)!declared.has(name))constorphan[...declared].filter((name)!requested.has(name))constresult{buildId:review_guard_20261001_03,appVersion:2.3.0(20300),requestPermissions:requested.size,privacyItems:declared.size,undeclared,orphan,status:undeclared.length0orphan.length0?PASS:FAIL}console.log([ReviewGuard],JSON.stringify(result,null,2))process.exit(result.statusPASS?0:1)这里我用 JSON5 解析module.json5而不是直接JSON.parse因为工程配置本身允许 JSON5 语法。正式落地时把json5放进开发依赖并锁版本避免不同开发机解析行为不一致。脚本故意不判断“申请 CAMERA 合不合理”。那是业务和合规问题不能靠集合运算给结论。它只回答代码已经声明的东西内部是否有记录内部记录的东西代码是否还在用。这个边界很重要。自动化最危险的地方不是检查少而是看起来检查很多最后团队误以为PASS等于“审核一定通过”。ReviewGuard 的 PASS 只能解释为本地对账规则通过。四、失败就让构建链停下来比发群消息更有用脚本能打印结果还不够。如果它只是一个“想起来才运行”的工具过两周大家还是会忘。我给项目加了一个很朴素的预构建脚本#!/usr/bin/env bashset-enodetools/review-guard.mjs ./hvigorw assembleHap这段代码解决的是把自检结果变成构建前置条件。review-guard.mjs返回非零退出码时后面的构建不会继续。如果团队使用 DevEco CLI 或 CI这个入口可以继续包在统一命令里如果本地习惯从 IDE 点运行也可以把它作为发布分支的流水线检查而不是强行阻塞所有日常 Debug。我的经验是开发阶段太严格会让大家绕过工具真正需要强约束的是“准备发包”和“合并到发布分支”两个时点。这里还有一个容易踩的坑脚本不能直接修改module.json5或隐私清单来“自动修复”。权限涉及真实业务能力发现差异后应该让开发者判断哪边错了。自动补齐只会把问题从“漏写”变成“错误地写上了”。五、为了让检查结果可视化我加了一个只在 Demo 里存在的 Review Guard 页面命令行已经足够完成工作但这次文章需要把状态过程讲清楚所以我额外做了一个调试页。构建脚本会把检查结果写入一个结果 JSONDemo 页读取后展示。这段 ArkTS 代码解决的是把构建期结果转成开发阶段可查看的页面状态interface ReviewResult { buildId: string appVersion: string requestPermissions: number privacyItems: number undeclared: string[] orphan: string[] status: string } Entry Component struct Index { State result: ReviewResult { buildId: review_guard_20261001_03, appVersion: 2.3.0(20300), requestPermissions: 6, privacyItems: 6, undeclared: [], orphan: [], status: PASS } build() { Column({ space: 12 }) { Text(Review Guard).fontSize(28).fontWeight(FontWeight.Bold) Text(Build ID ${this.result.buildId}) Text(App Version ${this.result.appVersion}) Text(Request Permissions ${this.result.requestPermissions}) Text(Privacy Items ${this.result.privacyItems}) Text(Undeclared ${this.result.undeclared.length}) Text(Orphan ${this.result.orphan.length}) Text(Status ${this.result.status}) } .padding(24) } }这个页面不是发布功能也不应该把隐私清单完整暴露给用户。它只是文章 Demo 和内部调试视图。正式项目中更合理的做法是把脚本结果交给 CI、制品报告或构建日志避免为了展示自检状态往正式包里增加无关页面。从 DevEco Studio 里可以直接看到脚本输出[ReviewGuard] requestPermissions6 [ReviewGuard] privacyItems6 [ReviewGuard] undeclared0 orphan0 [ReviewGuard] RESULTPASS这一版我特意让数字和手机页完全一致。以前做技术截图时最容易忽略的就是日志写 5页面写 6读者一眼就会怀疑是不是拼出来的。这种工具型文章数据一致比 UI 漂亮更重要。六、我用两个故障注入确认脚本不是“永远 PASS”一个检查工具如果只展示成功结果没有任何价值。我做了两次故障注入。第一次我在module.json5临时加了一条权限但没有往内部清单增加对应记录。脚本立刻得到undeclared1 orphan0 RESULTFAIL第二次反过来从module.json5删除一条已经不用的权限但故意保留内部清单条目。结果变成undeclared0 orphan1 RESULTFAIL这两种失败含义不同。undeclared更值得优先关注因为意味着代码申请权限但内部说明还没跟上orphan则通常说明功能下线后文档没有清理。我的脚本都让构建失败是为了让发布分支保持干净如果团队觉得 orphan 不需要阻断也可以把它降级成 warning。异常处理上还有两个实际问题。第一配置文件不存在时要直接失败而不是把集合当空数组继续第二JSON5 解析失败时要保留原始文件路径和行号信息方便快速定位。脚本越靠近发布链路错误信息越不能只写一个parse failed。七、最终结果不是“审核通过”而是“这次配置没有漂移”最终这次review_guard_20261001_03的结果是权限 6 条、内部清单 6 条、缺失 0、孤儿项 0、状态 PASS。手机截图里我保留了检查时间02:59:42和 DevEco 里的运行结果对应。页面最有价值的不是绿色 PASS而是下面四个数字。只要requestPermissions、privacyItems、undeclared、orphan能稳定记录每次发布前就能很快判断配置是不是发生了非预期变化。但我要再强调一次这个 PASS 不是应用市场的审核结论。官方上架说明要求开发者了解审核指南、补充应用信息并提交上架涉及第三方 SDK 时隐私政策还需要按实际情况逐一明示相关信息。ReviewGuard 只是研发侧“早一点发现配置差异”的工具。我更愿意把它看成一个很小的防呆脚本不替法务做判断不替产品决定是否申请权限也不替审核平台给结论。它只保证我们自己仓库里两份本来应该同步维护的配置在发包前没有悄悄走散。八、后面还能继续扩但别一口气做成“大而全合规平台”下一步如果继续扩展我会优先做三件小事。第一把privacy-manifest.json里的条目和具体功能模块绑定让删除功能时能提示“这个权限对应哪个模块”第二把第三方 SDK 名称、用途和政策章节位置做成索引但仍然只做一致性检查第三在 CI 里保存每次发布版本的对账结果方便回看某个版本到底声明了什么。我不会直接把网络请求、敏感 API 扫描、隐私政策文本 NLP 校验全部塞进同一个脚本。工具一旦开始假装自己能判断所有合规问题误报和漏报都会迅速增加。对这类发布前工具我更看重规则少但可解释为什么失败、哪条不一致、应该去哪个文件改一眼就能看明白。这也是我这次写 ReviewGuard 的出发点。很多上架问题不一定需要一个复杂平台解决有时候先让工程里最容易漂移的两份配置自动对上账就已经能省掉不少提审前的反复确认。九、脚本放进发布链以后最需要防的是“规则自己悄悄失效”工具跑通之后我没有立刻继续加更多规则而是反过来检查这个脚本自己会不会失效。发布前检查最尴尬的情况不是报错而是大家以为它在工作实际上某次目录调整之后它已经没有读到真正的配置文件。所以我给 ReviewGuard 又加了三个约束。第一module.json5、内部清单和结果文件都必须打印绝对路径文件不存在就直接退出第二读取到的requestPermissions数量为 0 时不能默认为正常而是根据项目基线决定是否需要 warning第三脚本版本要跟随仓库提交不能依赖某个开发者电脑上的全局脚本。这样 CI 和本地看到的规则才是一致的。我还给结果对象加了schemaVersion。现在只有两个集合看起来完全没必要但只要以后加入第三方 SDK 索引、功能负责人、隐私政策章节号结果结构就会变化。没有版本号历史构建报告很快就无法比较。工具脚本这种东西最容易在“很小”时忽略自己的兼容性。另一个实际问题是例外项。某些权限可能只在特定 flavor、测试模块或可选能力里出现如果粗暴要求两边永远完全相等团队很快就会开始用假数据把脚本哄绿。我更倾向于显式增加scope或enabledWhen字段让例外有名字、有原因、有负责人而不是给脚本加一个--ignore-all开关。规则可以允许差异但差异必须可解释。最后我把失败结果分成“阻断”和“提醒”两档undeclared默认阻断因为代码已经申请但内部索引没有记录orphan在开发分支可以提醒在发布分支再阻断。这样既不至于让日常开发被频繁打断又能保证真正出包之前仓库是干净的。这几个改动没有增加什么华丽功能却让工具从“一次性的文章 Demo”更接近能在团队里长期运行的小基础设施。对发布前检查来说规则稳定、结果可解释、失败能定位通常比扫描项越多越重要。十、资料核对华为开发者公开文档中上架申请页面明确提示开发者在提交前了解审核规则涉及第三方 SDK 时需要根据实际情况在隐私政策中明示 SDK 处理个人信息的相关信息。不同 SDK 的合规指南还会给出各自的隐私、权限和初始化要求。本文脚本是工程自检示例不替代官方审核指南或法律合规评估。HarmonyOS 文档中心https://developer.huawei.com/consumer/cn/doc/上架申请示例https://developer.huawei.com/consumer/cn/doc/HMScore-Guides/harmonyos-release-application-0000001181600758SDK 合规使用指南示例https://developer.huawei.com/consumer/cn/doc/hmscore-guides/harmony-sdk-guide-0000001657109996
返回列表