HarmonyOS 三方库工程化:供应链安全与上架检查(连载06-06)【共创稿事节】
把三方库接入工程,开发者通常关注它能否安装、调用是否稳定、接口是否符合业务类型。但到了发布节点,另一些问题会突然变得紧迫:依赖从哪里来,传递依赖是否变化,许可证是否允许当前使用方式,库是否处理用户信息,应用声明的权限是否与实际功能匹配。任何一个问题缺乏记录,都会让已经完成的功能重新进入排查环节。
前五篇已经从依赖锁定、Adapter 封装、异步错误处理、资源裁剪和升级回归搭起了一条工程链。收官时不再追加一套框架,而是把已有环节收拢为可以复核的发布门禁:来源可查、版本可追、调用有边界、数据有去向、发布信息可对应。 这些原则既能用于大型应用,也能帮助个人开发者在项目规模扩大之前建立基本秩序。

检查依赖,不能只看顶层包名
oh-package.json5 描述工程直接依赖了什么;oh-package-lock.json5 保存依赖解析得到的版本关系。前者表达意图,后者提供实际安装关系的基线。即使只改动一个顶层版本,传递依赖也可能随之变化,因此审查对象应该是整棵依赖图,而不是版本号最大的那一行。
以阅读应用的动效组件为例,生产依赖与开发依赖分开声明;正式工程需要结合实际选择的三方包填写真实名称与版本,不要把开发工具留在业务运行依赖中。
// entry/oh-package.json5:依赖声明的结构示例
{
"name": "entry",
"version": "1.0.0",
"description": "reading application entry",
"main": "",
"dependencies": {
"@ohos/lottie": "^2.0.0"
},
"devDependencies": {}
}
这里的 ^2.0.0 是允许解析一定版本范围的约束,不表示最终版本永久固定。将锁文件纳入版本管理,审查时比较锁文件差异,才能发现某个间接依赖是否被替换。对于一次只处理一个业务风险的升级,保持其他依赖不变有助于确定变化原因。ohpm clean 默认会删除锁文件,需要清理安装产物又保留基线时,可使用官方提供的 --keep-lockfile 选项。
# 记录工程依赖相关版本,便于人工核对
# 实际工程中在根目录执行
ohpm --version
ohpm install
ohpm list
# 清理安装产物时保留依赖锁文件
ohpm clean --keep-lockfile
一次供应链评审应至少记录包名称、目标用途、来源仓库、解析版本、间接依赖、许可证、维护状态和数据使用情况。其中“来源”不能仅凭包名判断,需核对实际下载地址、发布者和项目仓库;“维护状态”不能仅凭最近发布时间下结论,要结合修复记录和替换成本。
把依赖审查拆成机器规则与人工判断
工程自动化很擅长回答确定性问题:是否引入新的包、某个包是否不在许可名单、文件是否缺失、版本是否发生变化。但许可证条款的适用范围、隐私政策是否准确、某个功能为何需要权限,仍要由项目负责人结合应用行为审查。把所有项目都压缩成“安全通过”一个布尔值,反而会掩盖关键差异。
可以在仓库中维护一个简洁的依赖白名单,让脚本从 oh-package.json5 读取直接依赖,核对是否存在未经评审的新增包。下面的脚本使用 Node.js 自带的文件系统模块,针对规范 JSON 输入解析;若工程文件中使用 JSON5 注释或尾逗号,应由工程已有的 JSON5 解析工具先读取后再执行同一规则,避免直接用 JSON.parse 处理 JSON5 语法。
// audit-dependencies.ts:核心规则可在 CI 的 TypeScript 工具中使用
interface DependencyManifest {
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
}
interface ApprovedPackage {
name: string;
expectedRange: string;
reviewed: boolean;
}
function inspectDirectDependencies(
manifest: DependencyManifest,
approved: ApprovedPackage[]
): string[] {
const messages: string[] = [];
const dependencies: Record<string, string> = manifest.dependencies ?? {};
Object.keys(dependencies).forEach((name: string) => {
const record: ApprovedPackage | undefined = approved.find(
(item: ApprovedPackage) => item.name === name
);
if (!record || !record.reviewed) {
messages.push(`${name}: 缺少评审记录`);
} else if (dependencies[name] !== record.expectedRange) {
messages.push(`${name}: 声明版本范围发生变化`);
}
});
return messages;
}
const review: ApprovedPackage[] = [
{ name: '@ohos/lottie', expectedRange: '^2.0.0', reviewed: true }
];
const manifest: DependencyManifest = {
dependencies: { '@ohos/lottie': '^2.0.0' }
};
console.info(inspectDirectDependencies(manifest, review).join('\n'));
这段规则只负责直接依赖登记,不能替代对锁文件中传递依赖、包内容和安全公告的进一步分析。正因为它职责单一,才容易在每次提交时自动执行。需要额外检查的范围包括下载来源变更、原生二进制文件、网络地址、动态加载行为、许可证与第三方通知文本,并为问题建立负责人和处理状态。

隐私披露与权限声明需要对得上
发布审核中常见的误区,是认为“我没有直接收集数据,因此三方 SDK 的数据处理与我无关”。应用实际上引入的库若会调用网络、读取设备能力或处理个人信息,发布材料就需要反映这些行为。应由应用方逐一核对 SDK 的收集目的、方式、范围及第三方共享情况,并按实际情况完善用户告知与选择机制。
例如一个阅读应用集成远程推荐服务,服务可能请求用户的收藏偏好。产品不能只在隐私说明中笼统写“改善体验”,而应说明必要的数据字段、处理目的、使用对象,以及何时发起请求。若用户拒绝非必要数据处理,应保证基础阅读路径仍能使用。开发上可以将推荐能力的入口收束到单一服务接口,避免页面随意调用外部 SDK。
// privacy/RecommendationGate.ets:业务层的数据授权门禁
export interface ConsentState {
recommendationEnabled: boolean;
updatedAt: number;
}
export interface RecommendationClient {
fetchByTopics(topics: string[]): Promise<string[]>;
}
export class RecommendationGate {
private consent: ConsentState = {
recommendationEnabled: false,
updatedAt: 0
};
private client: RecommendationClient;
constructor(client: RecommendationClient) { this.client = client; }
updateConsent(enabled: boolean, timestamp: number): void {
this.consent = { recommendationEnabled: enabled, updatedAt: timestamp };
}
async getSuggestedTitles(topics: string[]): Promise<string[]> {
if (!this.consent.recommendationEnabled) return [];
return this.client.fetchByTopics(topics);
}
}
这段代码的核心在于“未授权时不进入推荐调用链”,而不是把是否允许请求留给每个页面各自判断。实际产品需要将同意记录、撤回入口、权限请求时机和业务提示与持久化方案配合起来。用户撤回同意后,后续请求应立即遵循新状态;已经产生的数据应依照相应政策与业务规范处理。
同样,module.json5 中的权限声明应与应用实际使用相对应。对不再调用的接口,要同时评估能否删除权限;对确实需要的敏感或受限权限,应按发布平台的填写要求提供明确的使用场景和原因。不要因为 SDK 文档给了一份较大的权限清单,就照单全收。权限声明、实际代码路径、弹窗提示和隐私材料必须指向同一项功能。
用四张核对表接住发布前最后一公里
| 检查层次 | 具体问题 | 保留的核对依据 |
|---|---|---|
| 来源与完整性 | 下载源、包名、版本、传递依赖是否可追踪 | 包清单与锁文件差异 |
| 使用与授权 | 许可证和资源归属是否符合产品使用方式 | 许可证核对记录与第三方通知 |
| 数据与权限 | SDK 数据处理、用户告知、权限申请是否一致 | 数据流梳理、权限表与隐私材料 |
| 构建与回退 | release 产物是否采用已评审依赖 | 构建配置、版本基线与回退策略 |
另外,还要确认第三方库不在初始化阶段自行开启用户未授权的非必要能力;业务层的错误提示不能将令牌、个人数据和内部访问地址直接展示出来;日志保留业务追踪需要的摘要即可。若存在原生库,核对目标架构与包体变化;若使用动态特性,核对加载时机和回退路径。这些工作不是一张签字表能够替代的,而是要能从代码和配置上找到相应落实点。

把安全与交付做成同一条开发习惯
供给端的软件包始终在更新,应用自身也在增加功能。供应链审查不是发布当天才发生的集中行动,而应贯穿选择、引入、封装、升级、回归、上架的全过程。引入库时留下用途与许可证记录;封装接口时限制它获得的数据;升级时比较依赖关系与行为差异;发布时再把声明和权限对照真实代码路径。团队能够解释每个依赖为什么存在、由谁维护、获取了哪些数据、异常时如何降级,三方库才能真正成为工程效率的加分项。
本系列从 ohpm 依赖配置起步,到 Adapter、异步处理、包体裁剪、升级回归,再到供应链与上架检查,形成了一套可逐步落地的治理方法。它并不要求所有项目都采用复杂的基础设施,而是强调:每次引入一个外部能力,至少应同步增加一份能被团队读懂、能继续维护的工程依据。可靠的发布,往往从这种日常习惯中形成。
参考文档
更多推荐

所有评论(0)