news 2026/10/2 7:16:25

HarmonyOS Hvigor + module.json5:权限声明、源码调用与隐私文案的三向静态核验【鸿蒙心迹】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HarmonyOS Hvigor + module.json5:权限声明、源码调用与隐私文案的三向静态核验【鸿蒙心迹】

应用准备发布时,权限问题经常以三个完全不同的文件出现:module.json5里是一组声明,ArkTS 里是调用与申请,隐私说明里又是一组面向用户的用途描述。三份内容分别看都像对的,放在一起却可能多一项、少一项,或者名称相同但用途完全对不上。

与其到提审前人工翻三遍,我更愿意把问题压缩成一次可重复的静态核验。本篇使用PermissionLedger作为示例工程,审计编号是perm_20261001_03,包名为com.example.permissionledger。演示结果固定为:配置声明 3 项,源码命中 2 项,隐私清单覆盖 2 项,ohos.permission.LOCATION只有声明、没有调用和用途说明,因此结果为BLOCKED。这些是为了讲清工具行为而设计的样例,不代表真实审核结论。

一、先把“权限没问题”改成可计算的集合关系

人工检查常见的说法是“该配的都配了”。这句话无法测试。工具需要更窄、更明确的判断:声明集合、代码使用集合、文案映射集合之间是什么关系。

PermissionLedger把每一项权限归入四类:

  • ALIGNED:配置有声明,源码有可识别的调用或申请,隐私清单有对应说明;
  • DECLARED_ONLY:只在module.json5出现,可能是遗留配置;
  • CODE_ONLY:源码引用了权限名或受保护能力,但配置没有声明;
  • POLICY_ONLY:隐私清单写了某项用途,当前版本代码与配置都没有对应项。

这里最重要的不是给DECLARED_ONLY判“有罪”,而是阻止它悄悄通过。某些权限可能由可选功能、条件编译、动态模块或第三方 SDK 使用,静态扫描看不见完整链路;工具应把它标为需要解释,而不是武断删除。相反,如果团队确认功能已经下线,遗留权限就应该从配置与文案里一起清理。

这也是本文与普通审核清单不同的地方:它不尝试模拟审核平台,更不承诺“脚本通过就一定上架”。它只在本地建立证据账本,把最容易漏掉的三向差异提前暴露出来。

二、配置端只读事实,不替开发者猜业务

示例工程声明三项权限:相机用于扫描凭证,网络用于提交处理结果,位置是上个版本遗留。为了让图片、正文和脚本使用同一份数据,名称固定如下。

这段配置解决什么问题:给静态工具一个明确、可定位的权限声明来源。

{ "module": { "name": "entry", "type": "entry", "requestPermissions": [ { "name": "ohos.permission.CAMERA", "reason": "$string:permission_camera_reason", "usedScene": { "abilities": ["EntryAbility"] } }, { "name": "ohos.permission.INTERNET" }, { "name": "ohos.permission.LOCATION", "reason": "$string:permission_location_reason", "usedScene": { "abilities": ["EntryAbility"] } } ] } }

requestPermissions是配置事实,但不是业务真相。声明了相机权限,不等于应用已经在合适时机向用户申请;写了reason,也不等于隐私说明与实际采集目的吻合。工具不会根据权限名自动生成一段听起来合理的文案,因为“为什么用相机”只能由业务回答。

还要注意权限类型差异。系统授权权限、普通权限、受限开放权限的申请方式和审核材料并不相同。本文的脚本只比较项目中的文本证据,不替代官方权限列表,也不决定某项权限是否需要动态授权。接入真实项目时,应以目标 SDK 对应的官方权限说明为准。

三、隐私文案先结构化,再生成面向用户的页面

直接解析一篇自然语言隐私政策很诱人,但容易得到虚假的确定性。文案里可能写“设备能力”“必要信息”这类宽泛措辞,正则即使命中,也无法证明用途清楚。更稳妥的做法是让项目维护一份结构化清单,再由发布流程把它与正式隐私文本关联。

这段数据解决什么问题:明确每项权限的业务用途、触发页面和用户动作,使文案覆盖可以被检查。

{"bundleName":"com.example.permissionledger","policyVersion":"2026.10.01","permissions":[{"name":"ohos.permission.CAMERA","purpose":"扫描纸质凭证并生成待确认图片","trigger":"ScanPage / 点击扫描凭证"},{"name":"ohos.permission.INTERNET","purpose":"提交用户确认后的处理结果","trigger":"ResultPage / 点击提交"}]}

示例里故意没有LOCATION。这让差异非常直接:声明集合里有它,源码和隐私清单都没有它。工具输出DECLARED_ONLY,构建被阻断。开发者需要做选择,而不是补一句空泛文案糊过去:如果功能确实不用定位,就删掉声明;如果隐藏路径仍在使用,就补齐代码证据、触发时机和真实用途,并重新评估是否符合最小必要原则。

结构化清单也有边界。它不是正式隐私政策本身,不能代替用户可访问的完整文本、第三方 SDK 说明、数据保存期限、共享对象和权利入口。它更像发布工程中的索引:把代码里的能力与对外描述连起来,减少版本变更时漏改。

四、扫描器不追求“聪明”,先保证结果可复查

脚本从三个入口读取数据:

  1. 解析entry/src/main/module.json5的requestPermissions;
  2. 扫描entry/src/main/ets中显式出现的ohos.permission.*;
  3. 读取compliance/privacy-map.json的权限名称。

源码扫描采用保守策略:只认显式字符串,不推断某个 API 必然需要什么权限。这样会漏掉封装层、第三方库和字符串拼接,却不会凭空制造一条调用证据。工具输出每个命中的文件与行号,方便人工回到源文件复核。

这段 TypeScript 解决什么问题:计算三组权限的并集,为每项权限生成可解释状态,并在存在未对齐项时返回非零退出码。

typeStatus='ALIGNED'|'DECLARED_ONLY'|'CODE_ONLY'|'POLICY_ONLY';interfaceAuditRow{name:string;declared:boolean;usedInCode:boolean;described:boolean;status:Status;}functionclassify(d:boolean,c:boolean,p:boolean):Status{if(d&&c&&p)return'ALIGNED';if(d&&!c&&!p)return'DECLARED_ONLY';if(!d&&c)return'CODE_ONLY';return'POLICY_ONLY';}functionbuildRows(declared:Set<string>,code:Set<string>,policy:Set<string>):AuditRow[]{constall=newSet([...declared,...code,...policy]);return[...all].sort().map((name)=>{constd=declared.has(name);constc=code.has(name);constp=policy.has(name);return{name,declared:d,usedInCode:c,described:p,status:classify(d,c,p)};});}constrows=buildRows(readDeclared(),scanArkTS(),readPrivacyMap());constblocked=rows.some((row)=>row.status!=='ALIGNED');writeReport('build/permission-ledger.json',{auditId:'perm_20261001_03',status:blocked?'BLOCKED':'PASSED',rows});process.exitCode=blocked?2:0;

分类函数故意没有覆盖所有布尔组合的精细命名。例如声明与文案都有、代码未命中的情况,当前会落入需要处理的非对齐分支;真实工具可以增加DECLARED_POLICY_ONLY。文章保留四类,是为了让主流程清楚,而不是宣称这套枚举适合所有仓库。

更值得注意的是退出码。报告写出来但命令仍以 0 结束,CI 很容易继续产出包。示例在出现任何非ALIGNED项时返回 2,使 Hvigor 流程可以把它作为发布前置检查。是否对POLICY_ONLY也阻断,可以按团队规则配置;不过默认阻断更能促使版本变化被显式解释。

图中的 DevEco Studio 是演示配图。左侧列出module.json5、privacy-map.json和checkPermissions.ts,中间圈出DECLARED_ONLY,右侧模拟器显示审计结果,底部日志固定为audit=perm_20261001_03 status=BLOCKED declared=3 code=2 policy=2。它不是审核平台截图,也不能证明应用已经通过官方检查。

五、把脚本接到构建链路,而不是留在 README 里

一个没人执行的工具和一份没人更新的表格差不多。PermissionLedger把扫描命令挂在 release 产物生成之前:开发构建可以提示,候选发布构建则遇到差异立即失败。这样既不打断每一次 UI 调试,又不会让问题拖到签名产物之后。

实际仓库可以在 Hvigor 自定义任务中启动 Node 脚本,或者让 CI 先执行node tools/checkPermissions.mjs再调用构建命令。本文不绑定某个私有流水线插件,也不杜撰 Hvigor API;关键约束只有两个:扫描输入必须对应即将发布的源码版本,非零退出码必须真正阻止发布步骤继续。

这段 ArkTS 代码解决什么问题:在开发版应用里读取已经生成的报告,把阻断原因展示为可追溯的审计详情。

interfacePermissionAuditSummary{auditId:string;status:'PASSED'|'BLOCKED';declaredCount:number;codeCount:number;policyCount:number;orphanPermission:string;}@Entry@Componentstruct AuditReportPage{@Statereport:PermissionAuditSummary={auditId:'perm_20261001_03',status:'BLOCKED',declaredCount:3,codeCount:2,policyCount:2,orphanPermission:'ohos.permission.LOCATION'};build(){Column({space:12}){Text(`审计${this.report.auditId}`)Text(this.report.status).fontColor('#C62828')Text(`声明${this.report.declaredCount}/ 代码${this.report.codeCount}`)Text(`文案${this.report.policyCount}`)Text(`待处理:${this.report.orphanPermission}`)}.padding(24).alignItems(HorizontalAlign.Start)}}

正式产品没必要把内部审计报告打进用户安装包。这个页面只适合开发 variant 或独立诊断 Demo。生产构建应排除报告资源与诊断入口,避免把内部路径、规则和未完成的配置暴露出去。

运行页在 10:26 展示三组计数与BLOCKED状态。红色箭头指向声明 3 / 代码 2 / 文案 2,说明问题不是“少写一个 reason 字符串”,而是三份事实没有闭合。审计编号与底部日志一致,方便在 CI 附件和页面之间对照。

六、修复动作应删除无用声明,而不是给遗留项编故事

面对ohos.permission.LOCATION,最快的表面修复是在隐私清单里补一项,再在源码里放一段不会触发的权限字符串。这样三组集合相等了,却让工具失去意义。

正确顺序是从业务入口反查。这个样例只有扫描凭证和提交结果:扫描页用相机,结果页用网络,没有地图、附近服务、地址定位或后台轨迹。因此位置权限没有业务入口,应从module.json5删除相关声明与资源字符串。修复后重新运行脚本,期望得到:声明 2、代码 2、文案 2,三项状态都为ALIGNED,总结果PASSED。

如果产品随后真的加入“自动填写拍摄地点”,也不应简单把旧声明加回来。需要重新回答:是否必须精准位置,模糊位置能否满足;在哪个用户动作后申请;拒绝后核心功能如何降级;页面文案是否说明处理目的。官方位置权限指导还区分精准、模糊和后台场景,配置与申请方式必须按实际目标 API 与场景核对。

详情图承担诊断作用:上半部分列出LOCATION的三列证据——声明“有”、代码“无”、文案“无”;下半部分给出“删除遗留声明并重跑”的建议,生命周期记录是SCANNING → COMPARED → BLOCKED。它与 03 的总览不是同一页面。

七、静态核验看不到的地方,必须明确写在边界里

第一,第三方 SDK 可能在二进制内部访问受保护能力,源码字符串扫描看不到。依赖清单、SDK 隐私说明、初始化时机和网络行为仍需单独检查。

第二,权限名可能被封装成常量、拼接字符串,或由其他模块传入。扫描器应支持项目自己的常量表与模块图,而不是只靠一条正则走遍所有仓库。

第三,声明存在不代表运行时申请时机正确。涉及用户授权的权限,应在实际需要时申请,并处理拒绝、再次进入、权限被撤回后的降级。静态脚本无法替代这些路径测试。

第四,正式隐私政策比权限映射广得多。账号数据、日志、剪贴板、网络标识、第三方共享、保存期限与用户权利,未必都对应系统权限。三向核验只是其中一层。

第五,审核规则、权限等级和开放方式会变化。脚本里的规则库要记录来源与更新时间,不能把某个历史 SDK 的结论长期固化。本文只使用官方已公开的配置与权限名称,未声称覆盖当前分发平台的全部政策。

第六,结果页是演示界面,BLOCKED是本地工具状态,不是 AppGallery Connect 返回的审核结论。把两者混在一起,会让文章产生虚假的亲历感,也会误导读者把工具当作官方判定器。

八、一次发布前核验应该留下什么

运行结束后,仓库至少应留下四样东西:机器可读 JSON 报告、供人阅读的差异摘要、对应提交版本,以及规则库版本。perm_20261001_03这样的审计编号应贯穿日志、报告和流水线附件,但不要作为权限状态的唯一证据。

团队评审时可以只问三句话:这项权限由哪个用户动作触发;代码证据在哪里;对外说明是否与真实处理一致。如果任何一项回答只能是“可能以后会用”,它就不该无条件留在发布配置里。

工具的价值不是让合规工作自动消失,而是把模糊问题变成可定位的差异。module.json5、ArkTS 和隐私文案不再各自正确,而是共同描述同一个版本的真实行为。到这一步,提审前的检查才从记忆型劳动变成可复查的工程步骤。

九、误报不能靠忽略名单无限增长

静态工具上线后,团队很快会遇到第一个例外:某项权限由内部基础库统一申请,业务模块里看不到字符串。最省事的处理是把它加进忽略名单,但长期这样做,忽略名单会变成第二份没人理解的权限配置。

更合适的做法是为例外建立“带到期时间的证据”。每条豁免至少包含权限名、责任模块、代码定位、业务入口、审批人和复查日期。工具仍然把原始状态写进报告,只是在最终门禁计算时注明“由哪条豁免覆盖”。这样读报告的人能区分真正对齐与暂时接受,不会看到一片绿色就误以为所有链路都被扫描器发现了。

另一个常见误报来自测试代码。测试夹具为了覆盖拒绝流程,可能出现大量权限字符串;如果扫描器把ohosTest、示例目录和生产源码混在一起,会把配置集合越扫越大。输入范围应由构建 variant 决定,并在报告头部列出实际扫描的目录。排除规则也要进入版本控制,避免某台 CI 机器与开发者本地得到不同结果。

常量封装则适合用解析规则处理,而不是豁免。例如项目把权限集中在PermissionNames.ets,扫描器可以读取常量声明并追踪直接引用;如果继续跨函数、跨包推断,复杂度会迅速接近编译器。这里要接受一个现实:静态工具的目标是发现高价值差异,不是证明程序不存在任何权限行为。

文案侧也会出现“一项权限多个用途”。相机可能用于扫码,也可能用于头像拍摄。结构化清单应允许同一权限对应多个触发点,但最终面向用户的说明不能退化成含糊的“用于业务功能”。扫描器可以检查用途非空、触发页存在,却不能判断措辞是否充分,这一步仍需要产品、法务与开发共同确认。

对于INTERNET这类普通权限,是否必须出现在面向用户的权限弹窗说明,与受控权限并不相同。示例把它纳入三向账本,是为了追踪网络用途,不是在宣称系统会弹出同样的授权框。真实规则可以把“配置对齐”和“个人信息处理说明”拆成不同维度,避免所有权限套一把尺子。

十、让报告能服务一次真实的版本判断

一个可用的报告不应只输出true或false。顶部先写审计编号、提交哈希、构建 variant、扫描时间和规则版本;中间按状态分组列权限;每一行给出配置位置、源码命中位置和文案条目;底部给出阻断原因与建议动作。这样即使流水线日志被截断,下载 JSON 仍能还原判断过程。

报告还应保持确定性。同一提交连续运行两次,权限顺序、文件路径表示和状态排序都应一致,避免版本库里出现没有意义的差异。示例的buildRows()在输出前按权限名排序,就是为了这个目的。扫描文件也应排序,路径统一使用仓库相对路径,不要把开发机用户名写进产物。

增量检查可以改善速度,但不应只扫描本次修改文件。权限声明、封装常量和隐私清单可能分别位于不同模块,只看 diff 容易漏掉跨文件关系。比较安全的优化是缓存每个文件的扫描结果,再在最终阶段重建全量集合;缓存键至少包含文件内容摘要与规则版本。

当结果从BLOCKED修复成PASSED时,流水线应保留前后两个报告,而不是只覆盖最新文件。评审者可以看到LOCATION被删除,三组计数从3/2/2变成2/2/2,以及对应提交。这种变化记录比一句“已修复权限问题”更可信。

最后,脚本必须允许人工停止发布。机器没发现差异,并不代表风险为零;相反,机器发现差异也不代表一定要补文案。PermissionLedger给人的不是最终答案,而是一张足够清晰的地图:哪里声明了,哪里调用了,哪里向用户解释了,哪些空白需要在交付前做出明确决定。

为了避免报告成为形式主义,团队还可以定期抽取一项ALIGNED权限做逆向核验:从页面上的用户动作出发,走到运行时申请、实际能力调用、失败降级和隐私说明,而不是只检查三个字符串是否相等。如果链路中途断开,就把新的证据类型补进规则,而不是修改样例让它继续变绿。工具因此会逐步贴近真实项目,同时保持每条规则可说明、可撤销。

发布后发现权限配置问题时,也应把当时的报告与修复提交绑定。不要只更新规则库后重跑当前分支,因为这会抹掉“旧版本为什么通过”的线索。保留历史规则版本、输入摘要和原始结果,才能区分扫描器当时没覆盖、配置随后变化,还是人工豁免判断失误。对于需要长期维护的应用,这份可追溯性往往比单次通过更有价值。

十一、参考资料

  1. 华为开发者文档:module.json5配置文件
    https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/module-configuration-file
  2. 华为开发者文档:声明权限
    https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/declare-permissions
  3. 华为开发者文档:向用户申请授权
    https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/request-user-authorization
  4. 华为开发者文档:申请位置权限开发指导
    https://developer.huawei.com/consumer/cn/doc/doccenter-capabilities/location-permission-guidelines
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 7:12:37

真正好用的Markdown笔记软件!维克日记免费跨平台离线使用,界面极简还带夜间主题,办公族记笔记必备!

摘要&#xff1a;维克日记&#xff08;Vic Diary&#xff09;是一款跨平台本地Markdown日记/笔记软件&#xff0c;采用Electron跨平台框架构建。核心优势包括&#xff1a;纯本地存储、WebDAV自主同步、AES加密、极简界面与夜间主题。本文详细介绍其技术架构、核心功能、版本更新…

作者头像 李华
网站建设 2026/10/2 7:11:32

烟尘雾雨环境下应急现场单视频三维实景复现

烟尘雾雨环境下应急现场单视频三维实景复现摘要火灾、爆炸、洪涝、野外灾害等突发应急场景普遍伴随烟尘弥散、雨雾笼罩、湿气折射等恶劣气象工况&#xff0c;大气悬浮颗粒会造成光线散射、画面雾化、细节衰减、特征模糊&#xff0c;导致传统二维视频观测失真、常规三维重建特征…

作者头像 李华
网站建设 2026/10/2 7:10:51

如何用CodexHost进行远程开发:在VPS上SSH运行Claude Code的完整教程

如何用CodexHost进行远程开发&#xff1a;在VPS上SSH运行Claude Code的完整教程 【免费下载链接】codex-host Run Pi and Claude Code directly in Codex Desktop. 在 Codex Desktop 中直接运行 Pi 和 Claude Code。 项目地址: https://gitcode.com/gh_mirrors/co/codex-host…

作者头像 李华
网站建设 2026/10/2 7:09:39

hindsight 记忆系统实战:为 LLM Agent 构建可回溯的长期记忆

1. 从“事后诸葛亮”到“事前预警”&#xff1a;hindsight 到底想解决什么问题第一次看到 “hindsight” 这个词&#xff0c;我脑子里蹦出来的就是那句老话——“事后诸葛亮”。字面意思就是“后见之明”&#xff0c;但放在 agent memory 和 LLM 这个语境里&#xff0c;它其实是…

作者头像 李华
网站建设 2026/10/2 7:08:38

GitHub热榜项目怎么刷才有价值:看懂、跑通、评估三步法

GitHub热榜这地方&#xff0c;要么不刷&#xff0c;一刷就是一个小时。每天早上的日榜就像一份技术圈的早餐菜单&#xff0c;热门项目换得飞快&#xff0c;昨天还挂在那里的仓库&#xff0c;今天可能已经跌出前二十五。2026年9月25日这期日榜我完整刷了几遍&#xff0c;印象最深…

作者头像 李华