HarmonyOS 7 AGC与Node.js:隐私政策双入口域名漂移【鸿蒙心迹】
一、两个入口写的是同一份隐私政策,用户却可能看到不同的页面
应用准备上架时,运营同学会把隐私政策链接填进 AppGallery Connect;客户端也会在设置页提供“隐私政策”入口。两处都可以打开浏览器,不代表它们始终指向同一份声明。活动期间临时换域名、国际化页面切换 CDN、站点重定向到旧版政策,甚至三方 SDK 升级时漏写了一项收集目的,都可能造成发布资料与应用内展示的不一致。这个问题和包签名是否正确没有直接关系,却能在发布阶段耗费很多来回确认的时间。
我更在意的是如何在“按下提交”之前拿到一份可以重复核查的证据,而不是设计一个听起来万能的自动审核器。应用政策链接属于公开信息,SDK 使用范围属于产品事实,AGC 配置又属于发布资料;这些数据的来源不同,不应该全部挤到某个脚本里靠字符串搜索做结论。比较可靠的起点,是先建立一个有限的声明清单,把期望值、观察值、异常原因与人工复核入口逐项列出。
本篇示例叫 PrivacyLinkGate,任务 PRV-1011-36,输入文件 privacy_manifest_08。它模拟一个 CityNotes 城市笔记应用,包名 com.example.citynotes,期待的隐私政策主机为 privacy.citynotes.example,版本为 v3。这些域名只是示例中的占位域名,不代表真实网络已经上线,也不应该拿来作为可以打开的公开链接。本轮只跑本地八条固定清单,没有抓取网页、没有解析真实DNS、没有请求真实重定向、没有上传AGC。

配套封面、IDE和手机画面均为生成式工程示意,并非 AppGallery Connect 官方后台截图。文中将 PRIVACY_REVIEW_HOLD 解释为“本地政策证据存在未解决问题,暂停人为发布判断”,不把它宣传为官方驳回状态。这里尤其需要严格:平台审核规则会随地区、应用类型与版本更新,本文的主机白名单、版本一致性和八项样例只是企业内部自检策略,不是华为 API 自动给出的合规证书。
二、官方要求决定必须核对什么,应用规则决定怎样核对
华为 AppGallery 的隐私要求强调,应用内应提供容易找到的隐私政策入口,提交到 AppGallery Connect 的链接要让用户能够访问相应声明,内容应清晰说明个人信息处理的目的、方式和范围。审核相关资料还提到第三方 SDK 收集信息的披露问题。直接从这些文字可以得出的是:政策应当可访问、一致且能解释数据处理,而不是“必须使用某个固定域名”或“只有同路径才算合规”。
因此,示例里将域名统一作为项目自己的发布约束,而非平台强制要求。我们规定中文和英文入口最终都要落在 privacy.citynotes.example,页面归属 v3,三方 SDK 的每条使用说明必须写明收集目的。好处是减少一份声明被悄悄替换为第三方落地页的机会;代价是组织需要维护域名白名单,并为合法的多主机部署留出明确审批流程。把企业规则和平台规则分开,后续产品迁移 CDN 时才知道应该修改哪一层。
对真实隐私政策审查而言,仅比较域名也远远不够。同一个域名下可能展示错误的语言、旧版本正文或不完整的 SDK 披露;两个不同域名也可能合法地承载同一受控内容。完整交付还需要用受控方式核对内容快照、政策版本、权利行使入口、运营主体与变更历史。本文不是要在短代码里压缩整个审核流程,而是从常见的“配置与声明漂移”切入,证明一张可回放的证据表为什么有用。
我们专门把“期望地址”和“实际落点”分开命名。declaredUrl 表示配置表里填写的入口链接,resolvedHost 则是人工或另一个受控采集流程填写的模拟观察值。真实生产环境要获取这个字段,应通过经授权的 HTTPS 请求与最终响应 URL,校验每一跳的协议与主机,并妥善处理重定向上限和超时。直接在 Node.js 中对 new URL(declaredUrl).hostname 求值,只能得到声明地址的主机,不能神奇地知道服务端最终跳去了哪里。
三、把八条数据拆成入口、重定向和SDK目的三组
为了避免文章、代码和图片各说一套,我们固定以下八个检查对象。P01 为应用内中文入口,P02 为 AGC 中文入口,P03 为应用内英文入口,P04 为 AGC 英文入口,四条的模拟版本均为 v3、观察主机符合约定,因此记为 PASS。P05 是第三方 SDK-A 的收集目的说明,目的字段已填写,也记为 PASS。至此五条通过数据已经确定,不能再为了凑统计改动。
P06 模拟一处跳转落点,声明地址仍在受控域名下,但夹具记录的最终主机变为 notice-third.example,因此是 HOST_DRIFT。P07 是第三方 SDK-B 的用途说明,purpose 留空,记为 SDK_PURPOSE_MISSING。P08 仍引用政策 v2,而本轮要求 v3,记为 REVISION_STALE。这三条每类各一,合计三条阻断。八条结果就是五通过、三阻断,整体状态 PRIVACY_REVIEW_HOLD。
这个分类顺序是经过刻意选择的。先检查版本,能够阻止旧政策数据被误当作新一轮合规清单;再检查模拟最终主机,避免链接落入不受控目标;最后验证 SDK 的使用目的。若某条记录同时有多个问题,当前简化策略只返回第一个阻断原因,诊断页可以保留其他候选告警,但计数必须只按最终分类统计。复杂的发布系统可以改成多错误列表,本例先坚持一条记录一个终态,方便读者用固定数据复核。
本次明确使用了企业内部术语 SDK-A 与 SDK-B,不是华为具体 SDK 的名字,也没有推断它们收集了什么个人信息。真正上架时,应从第三方供应商的隐私安全说明、实际集成版本和产品权限矩阵中生成披露条目;不得依据演示截图的“已说明”直接复制到真实政策中。审计脚本能指出字段为空,无法替业务负责人替用户解释处理目的是否真实、必要与充分。
四、先保护字段语义,再谈批量自动化
本例的数据结构中保留 kind、revision、declaredUrl、resolvedHost、purpose 等字段,不让一个名为 url 的字符串承担多个角色。完整 JSON 还应该包含来源、采集时间、操作者和证据摘要,不过为了让篇幅聚焦,本轮验证只读取必要字段。如果要记录真实用户可能访问的政策 URL,日志应当避免在查询字符串中包含识别符、票据或内部测试参数;这属于日志规范,与发布系统是否接受链接并不是同一件事。
以下规则使用 Node.js 标准库与 JavaScript 基础语法,不调用任何虚构的 AGC 客户端对象。new URL() 只是本地解析声明 URL;resolvedHost 是离线夹具里已经给定的模拟最终落点。这样能用静态输入复测白名单,但不能声称实际发起过网络访问。对缺字段和非法协议先给单独错误有助于工程扩展,本轮八条样例不命中这些防御性分支。
// tools/audit-privacy.mjs —— 企业自建本地校验器,不是AGC官方API
export const EXPECTED_HOST = 'privacy.citynotes.example';
export const EXPECTED_REVISION = 'v3';
export function auditRow(row) {
if (row.revision !== EXPECTED_REVISION) return 'REVISION_STALE';
if (row.kind !== 'sdk') {
try {
const url = new URL(row.declaredUrl);
if (url.protocol !== 'https:') return 'UNSAFE_SCHEME';
} catch { return 'INVALID_URL'; }
}
if (row.resolvedHost !== EXPECTED_HOST) return 'HOST_DRIFT';
if (row.kind === 'sdk' && !row.purpose?.trim()) {
return 'SDK_PURPOSE_MISSING';
}
return 'PASS';
}
如果把 HOST_DRIFT 检查写在“SDK目的字段缺失”之前,就需要保证 SDK 行也有一个可信的归属主机字段;本例为所有八条输入保留 resolvedHost,因此 P07 会顺利越过主机检查,再命中目的缺失。反之,生产系统里的 SDK 证据有时根本没有公开 URL,应该独立成不同类型规则,不能默默写一个默认主机当作真实证据。代码的可复用性恰恰来自它把假设写明了,而不是减少了几行条件。
对版本比较,也不建议仅使用字符串字典序。这里的 v3 是封闭的策略枚举,明确要求等于 v3;如果未来出现 v10、不同地区文案版本或允许部分语言延迟发布,就应引入有结构的版本序号、渠道与生效时间,而不是把 v10 > v3 当作自然规则。隐私政策长期可访问比单纯版本号更重要,业务在迁移时还需维护旧链跳转与存档策略。
五、把 Node.js 的测试放在接口边界,而不是放在真实网络上试运气
离线验证最方便的部分是可以控制所有输入,因此不必等真实网站上线才检查规则。与其写一个只判断成功总数的测试,不如逐条断言 P06、P07、P08 的具体阻断码,让数据契约成为事实标准。若某次改动不慎把 SDK 目的空字段归入 PASS,测试应该立即失败,而不是看总数恰好没变就通过。发布前自检要追求对错误类型的敏感性。
// fixtures/replay-privacy.mjs —— 8条固定输入的可执行回放
import assert from 'node:assert/strict';
import { auditRow, EXPECTED_HOST } from '../tools/audit-privacy.mjs';
const mk = (id, kind, revision='v3', host=EXPECTED_HOST, purpose='说明齐全') => ({
id, kind, revision, resolvedHost: host, purpose,
declaredUrl: 'https://privacy.citynotes.example/zh/v3'
});
const rows = [
mk('P01', 'entry'), mk('P02', 'entry'), mk('P03', 'entry'),
mk('P04', 'entry'), mk('P05', 'sdk'),
mk('P06', 'entry', 'v3', 'notice-third.example'),
mk('P07', 'sdk', 'v3', EXPECTED_HOST, ''),
mk('P08', 'entry', 'v2')
];
const expected = ['PASS','PASS','PASS','PASS','PASS',
'HOST_DRIFT','SDK_PURPOSE_MISSING','REVISION_STALE'];
const actual = rows.map(auditRow);
assert.deepEqual(actual, expected);
assert.equal(actual.filter(v => v === 'PASS').length, 5);
console.log('PRV-1011-36 checked=8 pass=5 blocked=3');
这份回放并没有检查中文和英文页面的真实文本,也没有验证跳转目标域名是否真的存在。上面 mk() 的默认声明 URL 只是示例占位,P03、P04 实际生产配置必须使用对应语言的地址,并另设内容一致性检查。把测试数据作为证据,要把这种简化记在结果里;不能因为固定八条满足预期,就声称双语政策页面实测通过。
如果将来确实加入 HTTPS 网络探测,至少还要规定超时、最大重定向次数、TLS证书验证、允许访问的地址范围和失败重试策略。不能盲目跟随来自配置或第三方的任意 URL,以免让内网构建服务变成主动访问敏感地址的通道。隐私链接检查更应该防止“为了做安全扫描,反而把不受控请求引入构建流水线”。本例刻意让网络阶段保持 NOT_RUN,就是把这条风险挡在工程边界之外。

DevEco 风格示意图里,工程树位于左侧、Node.js 规则处在中间、模拟器在最右侧、底部是 HiLog 形式的固定结果。这里不代表 Node.js 文件已经在 DevEco Studio 的设备进程里执行;实际落地更合理的结构是:CI 中运行本地清单审查脚本,生成 JSON 结果;HarmonyOS 应用只读取经过脱敏的报告数据用于展示。这样既不把 Node.js 的运行时能力错写成 ArkTS 系统 API,也能让发布运营和客户端开发共享一套可回放的结果。
六、真正容易混淆的是“配置相同”和“内容相同”
在产品评审会上,常有人问:“两个链接都能打开,为什么还要核对?”原因是浏览器打开成功只证明一次网络交互发生了,并不能证明文本满足发布要求。多语言站点可能重用模板却丢失了一段 SDK 说明;地区切换可能把用户带到另一地区的政策;发布配置中的链接还可能指向旧页面,而客户端点击的是新版。如果把这些都归成 HTTP 200,就无法解释审核意见为什么针对内容一致性。
因此实际项目应有两套不同的收据。第一套是本篇的配置收据,它描述入口来源、策略版本、模拟跳转主机与缺失披露字段;第二套才是内容收据,它需要在得到合法的页面内容后核对政策主体、版本、重要条款和三方 SDK 清单。内容收据可以由人工确认和版本化快照构成,不必假设纯字符串 diff 就能理解法律文本。工程工具擅长发现差异,不擅长代替合规负责人判断某种差异能否接受。
一个稳妥的合并方式是把入口 ID 和内容版本绑定,而不是拿 URL 当唯一身份。比如 P01 是应用内中文入口、P02 是 AGC 中文入口,两者都应关联到策略版本 v3 对应的中文内容证据。P03、P04 则关联英文证据。即使未来增加新的 CDN 主机,只要经过审批并提供内容证据,也可以有计划地更新白名单。这里的主机一致性是辅助约束,不应上升为“相同域名就是合规”的结论。
第三方 SDK 的披露更需要人来确认。SDK-A 在清单中被标记为“目的已说明”,脚本只能判断 purpose 非空,无法证明目的说明与真实代码的数据处理一致。SDK-B 缺失目的字段会被阻断,但补上一句任意文本也不意味着合规完成。专业的流程应将 SDK 版本、权限、数据字段、使用场景、供应商文档和隐私文本建立可追溯对应关系,变更后再触发人工复核,而不是让某个绿色 PASS 徽标代替这一工作。
七、八条结果如何展示,怎样避免把本地结果画成官方审核结论
示意手机主页面按照 P01 到 P08 排列八个检查对象,五条绿色 PASS 分别对应双语的应用内与 AGC 入口、以及 SDK-A 说明;三条红色记录对应 P06、P07、P08。它保留了 PRV-1011-36、privacy_manifest_08、v3、受控主机及 PRIVACY_REVIEW_HOLD 等固定字段,帮助审核人先看是哪一层资料出现漂移。主页面有一个“查看合规诊断”入口,名称只表示进入本地审查报告,不意味着进入华为官方后台。

诊断页把三项阻断展开。P06 提示 notice-third.example 不在项目受控主机名单,建议核对配置表与实际跳转;P07 说明 SDK-B 的 purpose 为空,建议对照实际功能与供应商资料;P08 提醒隐私版本 v2 与本轮 v3 策略不一致。所有原因都应指向可以修改的证据源,而不是只显示一个总分。处理人看见“域名漂移”就去核对跳转,看见“目的缺失”就联系业务负责人,排查路径能明显短一些。

图中的“模拟最终主机”和“检查时间”是从约定夹具构造的展示信息,不是程序在线追踪出来的真实响应。由于没有调用网络请求,状态 HTTP_FETCH NOT_RUN 必须一直保留;由于没有登录或提交到 AGC,AGC_SUBMIT NOT_RUN 也不能改成“审核成功”。如果未来真实引入网络扫描,新的证据应该记录采样时间、请求方法、响应链摘要与采集环境,这样才能解释某次链接漂移是瞬时故障还是长期配置错误。
还应该让发布流水线对“部分通过”持保守态度。八条中有五条 PASS 不表示可以放行,原因在于隐私声明是相互关联的整体证据。任意入口可能让用户访问不一致内容,缺失三方 SDK 披露也不应被其他通过项的数量抵消。因此只要本地存在阻断项,整体业务状态就保持 PRIVACY_REVIEW_HOLD。这不是华为平台定义的状态码,而是团队为了避免误发布设计的操作门禁。
八、异常、释放与长期维护,比第一次跑通脚本更重要
真实流水线里,清单生成与发布提交不是同一瞬间完成。脚本检查时用的是 v3,提交前可能又更新为 v4;代码集成的第三方 SDK 可能从一个版本升级到另一个版本;隐私站点也可能在审核期间变化。为了避免旧结果被拿去证明新发布,报告至少应带上任务 ID、配置修订、源清单摘要、生成时间和目标候选包身份。当这些键发生变化时,应使旧报告失效并重新收集证据,而不是让一个历史绿色状态永久可用。
脚本异常时也应失败关闭。例如 JSON 语法错误、受控域名列表为空、输入行重复、证据缺少来源,都应让流水线输出 INCOMPLETE_EVIDENCE 之类的专门错误,并停止生成“通过”报告。网络超时或网页不可用不能自动推断成条款不合规,也不能悄悄转换成 PASS;应输出“本次不可验证”,交给人工或受控重试。把技术故障和合规事实区别开来,才能让运维同学在午夜接到告警时知道先找谁。
资源释放虽然不如图像解码直观,同样不能省。离线脚本读取文件后应结束句柄,网络扩展需正确关闭请求和取消超时任务,报告生成后对临时缓存与敏感诊断材料设置清理策略。为了稳定复现,可以保存脱敏的输入摘要,但不建议把应用内部账号、测试 token、签名秘钥、用户个人信息直接放入分享给审核沟通方的日志或截图中。CI 证据的保存周期也应与团队的合规留档要求相匹配。
工程扩展时,还需要明确多语言政策“等价”的定义。单纯字数接近或哈希不同,都不足以给出合规结论。可以将章节编号、处理目的分类、SDK 名称及用户权利入口做结构化抽取,再让双语审核人员确认翻译一致性。若条款含各地区特有的法律表述,工具最多将其标注为待审,不应偷偷统一替换。发布质量来自受控的分工,而不是企图让一个 Node.js 正则解决法律文本的全部语义问题。
九、结论:先让发布证据有来处,才谈自动拦截
本轮 PrivacyLinkGate 所实现的只是一个边界清楚的最小模型:受控主机为 privacy.citynotes.example、目标策略版本 v3,八条业务输入中五条通过、三条分别因跳转主机、SDK 目的字段与政策版本不匹配而阻断,最终 PRIVACY_REVIEW_HOLD。本地 JavaScript 校验器与固定输入回放可以给出可重复的业务结果,但它不具备 AGC 的后台权限,也没有证明真实页面可访问或内容符合审核要求。
这套方法可以作为 HarmonyOS 项目发布前的配置检查清单起点。下一步最有价值的工作不是增加一张更像官方后台的图片,而是把真实客户端入口、AGC 提交资料、受控站点采样和供应商 SDK 文档纳入同一份有版本的证据链,再由负责人确认并归档。能够解释每个 PASS 从哪儿来、每个 BLOCK 应当怎样修,比笼统地说“隐私合规已通过”更适合实际工程交付。
官方参考资料与适用边界:华为 AppGallery 审核指南隐私条款(https://developer.huawei.com/consumer/en/doc/app/50104-07);AppGallery 政策中心的常见问题(https://developer.huawei.com/consumer/fr/doc/app/FAQ-faq-09);AppGallery Connect 配置隐私描述(https://developer.huawei.com/consumer/es/doc/app/agc-help-release-app-privacy-desc-0000002313477969)。这些文档支持“入口与披露应一致、完整”的原则,但没有把本例自建的主机名单或 PRIVACY_REVIEW_HOLD 状态定义为官方接口。
更多推荐




所有评论(0)