先看结论与判断条件
- 验收对象是当前发布候选实际解析并打包的依赖集合,不是采购合同、README 或 Gradle 声明中的理想集合。
- 直接依赖与传递依赖必须共同锁定版本和来源仓库,否则同一份构建脚本可能在不同时间得到不同的 Native 载荷。
- APK 内每个 lib/ABI 目录都要建立 SO 摘要、归属组件、引入路径和处置责任,缺少归属的二进制不能静默放行。
- ABI 声明、构建产出、最终打包与设备运行是四类证据,任何一类都不能替代其余三类。
- SLSA provenance 与 in-toto statement 适合绑定产物摘要和构建声明,但格式正确不等于声明真实,也不证明运行时安全。
- 放行门禁应对新增、移除、版本漂移、摘要变化、未知 SO 和证明缺失分别给出责任人、处置时限与可审计结论。
先把验收对象从 SDK 名称改成候选包事实
AI SDK 往往不只是一项 Java 或 Kotlin 依赖。语音、视觉、推理和向量计算组件可能携带预编译本地库,也可能通过传递依赖继续引入运行时、图像编解码、网络或硬件适配库。采购单上写着一个 SDK 名称,最终 APK 中却可能出现多个组织、多个版本和多个 ABI 的二进制。供应链验收若只核对顶层坐标,会把真正进入进程的代码留在审计范围之外。
正确的验收对象应绑定一个不可混淆的发布候选,包括应用版本、构建编号、APK 或 AAB 摘要、签名身份、依赖解析结果和 APK 内文件摘要。团队随后才能回答三个基本问题:哪个组件把某个 SO 带进来,构建时拿到的是哪一份字节,出现安全通告或兼容故障时由谁负责处置。缺少候选摘要的清单只能作为计划,不能作为已验收记录。
这里还要区分声明、解析、打包和运行。Gradle 文件声明的是依赖意图,解析报告展示构建工具选中的版本,APK 清单展示交付文件,设备测试才说明特定环境是否成功加载。四者之间可能因版本冲突、排除规则、变体、拆包、压缩和厂商插件出现差异。工程判断是把四层证据分栏保存,而不是用一句“SDK 已接入”合并全部结论。
| 证据层 | 回答的问题 | 最低记录 | 不能替代的证据 |
|---|---|---|---|
| 依赖声明 | 项目希望引入什么 | 坐标、版本约束、仓库、变体 | 解析后的真实版本 |
| 解析结果 | 构建工具最终选择什么 | 直接与传递依赖图、冲突原因 | APK 中真实文件 |
| 交付产物 | 用户设备将收到什么 | 候选摘要、SO 路径、文件摘要 | 设备装载与业务结果 |
| 运行验证 | 目标环境是否按预期执行 | 设备、ABI、系统、日志、结果 | 来源与构建证明 |
| 责任记录 | 发生变更或漏洞由谁处理 | 组件归属、联系人、处置状态 | 任何技术层的原始证据 |
锁住解析图,才能解释直接依赖与传递依赖
Android Gradle dependency resolution 说明直接依赖与传递依赖会共同形成解析图,版本冲突、约束和选择规则会决定最终结果。对 AI SDK 来说,这意味着顶层版本没有变化时,传递组件仍可能因动态版本、仓库元数据、约束调整或其他库的升级而改变。验收应保存可重复生成的解析报告,并把每个节点的选择原因、被替代版本和引入路径作为清单字段。
版本锁定不能只记录 group、name、version。还需要记录下载仓库、制品类型、变体属性、校验摘要和依赖锁文件的来源提交。企业内部镜像可能缓存或重新托管同名制品,公共仓库也可能存在多个坐标指向相似组件。工程判断是把“坐标相同但字节不同”视为独立变更,门禁比较文件摘要,而不是看到版本字符串未变就自动沿用旧结论。
解析差异必须有可解释的基线。新增节点要说明业务需求和引入方,移除节点要确认功能是否迁移或被打包进其他制品,版本变化要关联变更说明与安全处置,来源仓库变化要重新确认信任边界。依赖树可以缩小调查范围,但不能单独证明某个 SDK 是故障或风险的唯一根因,因此结论应写成“发现变化并等待归属确认”,而不是直接给厂商定责。
| 差异 | 需要追问 | 应保存的证据 | 放行条件 |
|---|---|---|---|
| 新增依赖 | 由谁引入,承担什么功能 | 解析路径、制品摘要、评审记录 | 归属和风险边界明确 |
| 版本变化 | 兼容性与安全修复是什么 | 旧新摘要、变更说明、回归结果 | 变更理由与测试相互对应 |
| 来源变化 | 仓库或镜像为何改变 | 仓库标识、下载记录、校验摘要 | 来源策略允许且摘要受控 |
| 依赖移除 | 功能删除还是被合并 | 解析差异、APK 文件差异 | 没有残留未知载荷 |
| 冲突改选 | 哪条规则覆盖了声明版本 | selection reason 与约束链 | 负责人接受实际解析结果 |
从 APK 反查每个 SO 的 ABI、摘要与归属
Native 清单应从候选 APK 的 ZIP 条目开始,而不是从源码仓库猜测。常见本地库位于 lib/ABI/name.so,验收程序可以只读列举路径、未压缩大小和 SHA-256,再与依赖解析节点建立归属关系。若同名 SO 在多个 ABI 下出现,应分别计算摘要,因为不同架构的机器码本来就不同。若路径不符合约定或库名无法归属,门禁应产生待确认项。
Android ABIs 强调每个 ABI 有独立的调用约定、寄存器使用、数据对齐和设备支持范围。清单因此不能只写一个“支持 arm64”的布尔值,而应按 ABI 记录构建输入、SO 文件、依赖关系和设备验证。声明支持某个 ABI 不代表对应文件已经被构建和打包,也不代表它在真实设备上成功装载。静态清单能证明文件存在,运行结论仍要由目标设备证据承担。
归属映射通常无法只靠文件名完成。一个 SDK 可能重命名第三方库,也可能把多个开源组件静态链接到单个 SO;反过来,同一个基础库可能被多个 SDK 共同引用。可执行的做法是要求供应方给出组件清单和构建说明,再用 Gradle 解析图、AAR 内容、APK 路径与摘要交叉核对。无法拆分的静态组合应登记为组合件,并把漏洞处置责任明确到交付该组合件的一方。
| 字段 | 用途 | 异常信号 | 处置动作 |
|---|---|---|---|
| APK 路径 | 定位 ABI 与文件名 | 目录或命名不符合预期 | 确认打包插件与交付方式 |
| 文件摘要 | 绑定实际字节 | 版本不变但摘要变化 | 重新核对来源与构建 |
| 归属组件 | 连接 SDK 与二进制 | 未知或多个责任方 | 补供应方说明和内部负责人 |
| 支持 ABI | 定义设备覆盖 | 声明与文件集合不一致 | 阻止扩大兼容结论 |
| 处置责任 | 承接漏洞与升级 | 只有采购方没有技术联系人 | 补齐升级、回滚和通知路径 |
把 SDK Index 当作评审信号,不当作完整物料清单
Google Play SDK Index 可为部分商业 SDK 提供版本采用、权限、可靠性、安全和数据处理等评审线索。它适合在引入前帮助团队发现需要追问的问题,也适合在版本选择时补充外部视角。不过它不覆盖所有私有 SDK、开源库和企业内部构建物,更不会替项目列出 APK 中真实存在的每一个 SO。
使用 SDK Index 时,评审记录应保留查询对象、查询日期、对应版本和被采纳的具体建议。若 SDK 没有收录,结论只能是“该数据源没有覆盖”,不能推导为安全或不安全。若页面对权限、数据处理或版本状态给出提示,团队还要回到应用清单、运行配置和实际候选包核对,因为相同 SDK 在不同接入方式下可能暴露不同能力。
商业验收更关心问题能否闭环。采购合同应明确版本通知、Native 组件清单、漏洞通知、修复版本、停服或回滚方案和支持期限;技术清单负责证明当前候选包装了什么。两者连接后,外部评审信号才会变成可执行动作。只有风险标签而没有责任人和升级路径,通常会在真正出现通告时重新陷入临时追查。
- 记录 SDK Index 查询日期与对应 SDK 版本
- 未收录只标记数据源未覆盖,不作安全推断
- 权限与数据处理提示回到真实清单和配置核对
- 供应方交付 Native 组件和第三方许可证清单
- 合同明确漏洞通知、修复版本与回滚联系人
- 外部信号不能覆盖 APK 二进制清单
用 provenance 和 attestation 绑定构建声明与产物摘要
SLSA Provenance v1.1 把 provenance 组织为与产物主体、构建者、构建类型、外部参数和依赖材料有关的声明。对 AI SDK 验收而言,最有价值的不是获得一个文件名漂亮的证明,而是确认 subject 摘要与当前下载制品或最终候选中的对象能否对应,builder 身份是否属于认可边界,参数与材料是否足以解释这次构建。
in-toto Attestation Statement v1 提供将 artifact digest 与有类型声明负载绑定的通用结构。该结构能减少报告与候选包错配,但不会自动让声明内容变真。验收还需验证签名者、签名策略、声明类型和摘要算法,并确认门禁读取的是当前候选对应的声明。把未签名 JSON 附在交付目录里,只能算供应方说明,不能自动升级为可信构建证据。
证明链应允许出现不同证据等级。供应方能给出可验证 attestation 时,记录签名者、策略和摘要匹配结果;只能提供组件清单时,记录文件来源与人工复核;完全没有构建信息时,明确标成证明缺失,并由风险所有者决定是否接受。工程判断是拒绝用“有证明”这一布尔字段掩盖验证细节,门禁至少输出匹配、失配、无法验证三种状态。
| 检查项 | 可核查事实 | 常见误区 | 门禁输出 |
|---|---|---|---|
| subject 摘要 | 是否匹配目标制品 | 只比文件名或版本 | 匹配、失配或缺失 |
| builder 身份 | 声明由谁产生 | 任意签名都视为可信 | 策略允许或待确认 |
| buildType 与参数 | 如何触发和配置构建 | 参数存在就等于可重复 | 信息充分或不足 |
| 依赖材料 | 构建引用哪些输入 | 材料清单替代最终包扫描 | 可关联或不可关联 |
| 声明签名 | 载荷是否保持完整 | 格式正确就接受内容 | 验证通过、失败或未验证 |
按 SSDF 明确供应方、研发、安全与发布责任
NIST SP 800-218 SSDF 把来源、构建、验证、变更和供应链风险放进安全开发流程。它是组织级实践框架,不会替项目决定某个 AI SDK 应否上线。团队可以借它拆清职责:供应方解释组件与构建来源,研发维护解析和集成,安全团队核对证明与风险,发布负责人把门禁结果绑定到候选身份,业务风险所有者处理仍未关闭的例外。
责任清单必须覆盖正常升级和紧急处置。正常升级要知道谁评审变更、谁维护回归样例、谁更新基线;安全通告到来时要能根据组件名、版本、SO 摘要和静态组合关系定位受影响候选;无法立即升级时要有禁用、回滚或风险接受路径。这里的关键不是承诺统一修复时长,而是确保每一类状态都有明确决策人和留痕方式。
例外也需要到期条件。某个私有 SDK 暂时没有 provenance,不应在表格里永久写“供应商原因”;应记录缺失内容、接受理由、适用候选、批准人、复核触发条件和替代证据。候选摘要变化、SDK 版本变化、未知 SO 出现或安全通告发布,都可以触发重新评审。没有触发器的例外会在后续版本中被无意识继承。
| 角色 | 输入责任 | 判断责任 | 必须留下的记录 |
|---|---|---|---|
| SDK 供应方 | 组件、版本、构建与许可证信息 | 解释交付内容和修复路径 | 清单、证明、通知联系人 |
| 应用研发 | 解析图、集成配置、候选产物 | 确认引入路径和业务影响 | 锁文件、差异、回归结果 |
| 安全评审 | 来源、摘要、通告与边界 | 判定证据是否满足策略 | 核验结果与未决项 |
| 发布负责人 | 签名候选与门禁回执 | 确认结果绑定正确候选 | 摘要、签名身份、发布决定 |
| 风险所有者 | 未关闭例外与业务影响 | 接受、延期或拒绝 | 理由、范围和复核触发器 |
用只读脚本生成可复核的 Native 供应链清单
自动化的第一步应保持只读和可重复。输入可以是一份由构建流程导出的依赖 JSON 与待验收 APK,输出每个依赖节点、每个 SO 的 ABI、路径、大小和摘要。脚本不需要修改 APK,也不应猜测未知二进制的厂商归属。它的职责是把事实稳定地摆到审阅者面前,并在依赖列表为空、APK 无效或没有 Native 文件时返回非零状态。
下面的 Python 示例读取命令行指定的依赖文件和 APK。依赖文件要求包含 dependencies 数组,每项提供 coordinate、source 和 sha256;脚本随后扫描 lib 目录,把 SO 按 ABI 汇总并计算摘要。输出中的 owner 保持 unresolved,直到团队用 AAR、供应方清单和解析路径补齐。这样的默认值比根据文件名自动归因更可靠,因为错归属会把后续漏洞通知交给错误责任方。
这段代码只能生成静态事实清单,不能证明库在设备上成功加载,也不能判断摘要对应的代码是否安全。生产流程还应保存脚本版本、输入文件摘要和执行日志,并把输出与基线比较。若发现新增 SO、摘要变化、未知来源或 ABI 集合改变,应由独立策略决定阻断还是进入例外审批,不能让清单生成器自行作商业风险结论。
- 依赖输入来自同一候选的实际解析结果
- APK 摘要和签名身份另行固定
- 每个 SO 都按 ABI 保存路径与摘要
- 未知 owner 必须进入人工归属队列
- 脚本异常和空结果返回非零状态
- 输出不代替设备运行与构建证明
from pathlib import Path
import hashlib
import json
import sys
import zipfile
if len(sys.argv) != 3:
raise SystemExit(2)
lock_path = Path(sys.argv[1])
apk_path = Path(sys.argv[2])
if not lock_path.is_file() or not apk_path.is_file():
raise SystemExit(2)
lock_data = json.loads(lock_path.read_text(encoding="utf-8"))
dependencies = lock_data.get("dependencies", [])
if not isinstance(dependencies, list) or not dependencies:
raise SystemExit(2)
normalized_dependencies = []
for item in dependencies:
coordinate = str(item.get("coordinate", "")).strip()
source = str(item.get("source", "")).strip()
digest = str(item.get("sha256", "")).lower().strip()
if not coordinate or not source or len(digest) != 64:
raise SystemExit(2)
normalized_dependencies.append({"coordinate": coordinate, "source": source, "sha256": digest})
so_files = []
with zipfile.ZipFile(apk_path) as archive:
for entry in archive.infolist():
parts = entry.filename.split("/")
if len(parts) == 3 and parts[0] == "lib" and entry.filename.endswith(".so"):
payload = archive.read(entry)
so_files.append({"abi": parts[1], "path": entry.filename, "size": entry.file_size, "sha256": hashlib.sha256(payload).hexdigest(), "owner": "unresolved"})
if not so_files:
raise SystemExit(2)
result = {"apk": apk_path.name, "dependencies": normalized_dependencies, "nativeLibraries": sorted(so_files, key=lambda value: value["path"])}
print(json.dumps(result, ensure_ascii=False, indent=2))把差异、限制和行动按钮写进最终放行记录
最终门禁不应只输出通过或失败。对每个候选,记录应列出依赖解析是否锁定、未知 SO 是否清零、ABI 声明与文件集合是否一致、制品摘要是否变化、provenance 或替代证据是否可核验、SDK Index 信号是否已处理、漏洞与升级责任是否明确。每项结论都要链接到原始证据,并保留检查时间和执行者。
放行条件需要与证据等级匹配。解析图和 APK 清单完整,可以证明当前候选包含什么;provenance 验证通过,可以证明某份声明与产物及构建身份的绑定;目标设备回归通过,才能对指定设备和场景记录运行结果。任何结论都应限定到对应候选、ABI 和设备范围。本文没有接入具体项目数据,因此不声称性能、兼容性、攻击阻断或客户结果。
准备引入或升级 AI SDK 时,可以先整理候选 APK、依赖锁文件、SDK 与 SO 对照表、支持 ABI、供应方证明、签名流程和设备矩阵,再通过御盾中央平台提交软件加固评估申请。评估入口的作用是承接真实材料与项目边界;在材料到位之前,供应链清单应保持待确认状态,不用营销承诺替代可复核证据。
- 候选摘要、签名身份与清单一一绑定
- 直接和传递依赖没有未解释漂移
- 全部 SO 具备 ABI、摘要、来源与责任人
- 证明状态区分匹配、失配与无法验证
- 静态事实、工程判断和运行结果分栏记录
- 例外包含批准人、适用范围与复核触发器
- 申请评估时提交真实候选和验收边界
事实依据与适用边界
以下内容区分官方事实、本文工程判断和不能外推的范围,避免把设计建议写成未经验证的产品结论。
| 本文判断 | 事实或工程依据 | 适用限制 |
|---|---|---|
| 直接依赖和传递依赖共同决定构建最终解析到的组件版本。 | Android Gradle dependency resolution 描述依赖图、版本冲突、约束与选择规则。 | 解析图只能解释构建选择,不能证明某个 SDK 是运行故障或安全问题的唯一根因。 |
| SDK 评审可以参考版本采用、权限、可靠性、安全和数据处理等公开线索。 | Google Play SDK Index 提供部分商业 SDK 的评审信息与开发者指引。 | 该索引不覆盖所有私有、开源或内部 SDK,也不替代候选 APK 的二进制清单。 |
| 不同 Android ABI 对调用约定、寄存器、对齐和设备覆盖有不同要求。 | Android ABIs 说明了 Android NDK 支持 ABI 的架构与约定差异。 | 配置中声明 ABI 不证明对应 SO 已被构建、打包或在目标设备运行验证。 |
| 构建来源证明可以把产物主体与构建者、构建类型、外部参数和依赖材料关联。 | SLSA Provenance v1.1 定义 provenance 中 subject、builder、buildType、parameters 与 dependencies 等字段。 | provenance 记录构建声明,不单独证明产物没有漏洞或运行时行为安全。 |
| 供应链声明可以用产物摘要绑定有类型的证明负载。 | in-toto Attestation Statement v1 定义 subject 与 predicateType、predicate 的通用声明结构。 | 声明格式正确不保证内容真实,仍需验证签名者、策略、摘要和当前候选的一致性。 |
| 安全开发流程应保留来源、构建、验证和变更证据,并管理供应链风险。 | NIST SP 800-218 SSDF 提供组织级安全软件开发实践与供应链风险框架。 | SSDF 不定义某个 App 加固产品的具体功能,也不替项目给出自动放行结论。 |
| Native 依赖验收应同时绑定解析图、APK 内 SO、文件摘要、候选身份和责任记录。 | 工程判断:这些字段把构建意图、交付字节和后续处置连接成可复核链路。 | 清单完整只能证明记录闭合,实际安全性、兼容性和性能仍需项目证据验证。 |
| 未知 SO、来源变化、摘要漂移和证明缺失应分别进入显式门禁状态。 | 工程判断:把不同缺口拆开,才能分配责任并避免用单一通过状态掩盖未决风险。 | 是否阻断发布取决于项目风险策略,本文不提供通用阈值或具体项目放行结论。 |
工程常见问题
只锁定 AI SDK 的 Maven 版本,能否完成 Native 供应链验收?
不能。版本坐标只覆盖声明和部分解析信息,还要核对传递依赖、来源仓库、制品摘要、APK 内每个 ABI 的 SO、供应方证明、漏洞处置责任和当前候选身份。
为什么版本号没有变化,SO 摘要变化仍要重新评审?
因为同一坐标可能来自不同镜像或重新构建的制品,打包插件也可能改变最终载荷。摘要变化说明交付字节发生变化,应重新核对来源、构建说明和回归证据。
Google Play SDK Index 没有收录某个 SDK,是否意味着不能使用?
不能这样推断。未收录只表示该数据源没有覆盖。团队仍需通过供应方材料、依赖解析、APK 二进制清单、权限与数据配置、证明和设备测试完成自己的评审。
有 SLSA provenance 就能证明 AI SDK 安全吗?
不能。provenance 用来记录并绑定构建相关声明,还要验证签名者和产物摘要。它不会自动证明没有漏洞、没有恶意行为,也不替代运行时和业务回归。
静态链接进一个 SO 的多个组件怎样登记?
把该 SO 作为组合件登记,要求供应方给出内部组件和版本清单,并由交付组合件的一方承担漏洞通知与修复责任。无法拆分时要明确证据限制,不能按文件名猜测。
申请软件加固评估前需要准备哪些 Native 供应链材料?
准备真实候选 APK 或 AAB、文件摘要、签名流程说明、依赖锁与解析报告、SDK 和 SO 对照表、目标 ABI、供应方证明、例外清单及设备回归范围,再从御盾中央平台提交申请。