Apache CycloneDX Ant Library 0.2 进阶实战详解
基于 Ivy 多配置工程实测 Apache CycloneDX Ant Library 0.2 的进阶能力:conf/optionalconf/externalconf 与 templateComponent 的依赖解析行为、驱逐模块空指针的零驱逐配方、SOURCE_DATE_EPOCH 逐字节可复现构建、离线 schema 校验与质量门禁脚本、CI 接入示例,以及与 cyclonedx-maven-plugin 2.9.3 的同依赖集对比,命令与回显均实测通过。
Apache CycloneDX Ant Library 0.2 进阶实战详解
目标:在真实多配置 Ivy 工程上把 Apache CycloneDX Ant Library 0.2 的依赖解析、可复现构建、schema 校验门禁与 CI 接入逐一跑通,并用同一份依赖集与 cyclonedx-maven-plugin 2.9.3 正面对比;本文全部命令与回显均实测通过。
前置阅读:《Ubuntu 22.04 安装 Apache CycloneDX Ant Library 指南》《Apache CycloneDX Ant Library 0.2 快速入门与集成示例》《Apache CycloneDX Ant Library 0.2 常用 API 与任务详解》。本文不重复任务属性的默认值速查,聚焦工程化问题。
一、环境信息
主环境(与专栏前几篇一致):
- 操作系统:Ubuntu 22.04.5 LTS,x86_64
- JDK:OpenJDK 17.0.20.1
- 构建工具:Apache Ant 1.10.12、Apache Ivy 2.6.0(位于 antlib 依赖目录)
- 组件版本:ant-cyclonedx 0.2(
org.apache.ant:ant-cyclonedx:0.2),依赖目录/opt/cyclonedx-antlib/lib - 主实验工程:
/root/cdx-advanced(Ivy 多配置工程notes-service) - 辅助实验工程:
/root/cdx-lab(第二章 2.6 节的复现实验在此进行)
对比环境(第六章使用,一台独立的 Maven 环境):
- 操作系统:Ubuntu 24.04.5 LTS
- Maven:3.9.16,插件 cyclonedx-maven-plugin 2.9.3
- JDK:17.0.20.1(Eclipse Adoptium)
- 对比工程:
/root/mvn-app(com.example:notes-service-mvn:1.0.0)
对比环境确认(实时回显):
head -2 /etc/os-release
PRETTY_NAME="Ubuntu 24.04.5 LTS"
NAME="Ubuntu"
mvn -version | head -3
Apache Maven 3.9.16 (2bdd9fddda4b155ebf8000e807eb73fd829a51d5)
Maven home: /usr/share/maven
Java version: 17.0.20.1, vendor: Eclipse Adoptium, runtime: /opt/java/openjdk
校验工具链(第四、五章使用):
- Python 3.10.12 + jsonschema 3.2.0(实测
python3 -c "import jsonschema;print(jsonschema.__version__)"输出jsonschema 3.2.0) - CycloneDX 1.7 官方 schema 三件套,存于
/root/cdx-advanced/schemas/:bom-1.7.schema.json、spdx.schema.json、jsf-0.82.schema.json
💡 说明:jsonschema 版本建议锁定在 3.x。实测脚本使用了
RefResolver(3.x 的标准 API),4.x 起它被标记为弃用,锁定版本可以避免 CI 环境里脚本行为漂移。
二、Ivy 依赖解析深度
本章的主工程是一份三配置 Ivy 描述符(/root/cdx-advanced/ivy.xml,完整内容):
<?xml version="1.0" encoding="UTF-8"?>
<ivy-module version="2.0">
<info organisation="com.example" module="notes-service" revision="1.0.0"/>
<configurations>
<conf name="compile" description="compile classpath"/>
<conf name="test" description="test only"/>
<conf name="optional" description="optional extras"/>
</configurations>
<dependencies>
<dependency org="org.apache.commons" name="commons-configuration2" rev="2.10.1" conf="compile->default">
<exclude org="org.apache.commons" module="commons-text" conf="compile"/>
</dependency>
<dependency org="org.apache.commons" name="commons-text" rev="1.12.0" conf="compile->default"/>
<dependency org="com.google.code.gson" name="gson" rev="2.10.1" conf="optional->default"/>
<dependency org="junit" name="junit" rev="4.13.2" conf="test->default"/>
</dependencies>
</ivy-module>
注意 commons-configuration2 上的 <exclude ... commons-text> 与显式声明的 commons-text 1.12.0——这是 2.6 节「零驱逐配方」的一部分,先记住它的位置。解析 target(build.xml):
<target name="resolve">
<ivy:resolve/>
<ivy:retrieve pattern="lib/[artifact]-[revision].[ext]" conf="*"/>
</target>
执行后 lib/ 目录落盘 7 个 jar(实测清单):
ls lib/
commons-configuration2-2.10.1.jar
commons-lang3-3.14.0.jar
commons-logging-1.3.0.jar
commons-text-1.12.0.jar
gson-2.10.1.jar
hamcrest-core-1.3.jar
junit-4.13.2.jar
2.1 conf 选取决定组件范围
同一个 ivyModule,conf="compile" 与 conf="*" 的产物组件数实测对比:
| ivyModule conf | 产物 | components 数 | 组件清单 |
|---|---|---|---|
conf="compile" | notes-service-compile.json | 4 | commons-logging 1.3.0、commons-configuration2 2.10.1、commons-lang3 3.14.0、commons-text 1.12.0 |
conf="*" | notes-service-all.json | 7 | 上表 4 个 + gson 2.10.1、junit 4.13.2、hamcrest-core 1.3 |
结论:ivyModule 严格按所选 Ivy 配置取组件,测试依赖(test 配置)与可选依赖(optional 配置)只有把 conf 放宽到 * 才会进入 SBOM。SBOM 的完整性由 conf 决定,生产工程至少要把 compile 与运行期配置并进去,是否包含 test 依赖按审计口径决策。
2.2 optionalconf:给可选依赖打 scope 标记
sbom-optional target 使用 conf="*" optionalconf="optional":
<cdx:ivyModule conf="*" optionalconf="optional"/>
产物中 gson 组件实测带上了 "scope": "optional":
{"type": "library", "bom-ref": "pkg:maven/com.google.code.gson/gson@2.10.1?type=jar", "group": "com.google.code.gson", "name": "gson", "version": "2.10.1", "scope": "optional", "licenses": [{"license": {"id": "Apache-2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.txt"}}], "purl": "pkg:maven/com.google.code.gson/gson@2.10.1?type=jar", "externalReferences": [{"type": "website", "url": ""}, {"type": "license", "url": "https://www.apache.org/licenses/LICENSE-2.0.txt"}]}
其余 6 个组件不带 scope。optionalconf 的语义就是「这个 Ivy 配置里的依赖按 CycloneDX 的 optional scope 落盘」。
2.3 externalconf:外部配置的并入行为
sbom-external target 改用 conf="*" externalconf="test"。产物 components 数同样是 7,清单与 optionalconf 版本完全一致,唯一差异是 gson 不带 scope(实测组件列表中 gson ... scope=None,junit 与 hamcrest 照样进入 SBOM)。
实测对比两个产物文件大小:21042 字节(all,含 "scope": "optional")对 21016 字节(ext,无 scope),差值即一行 scope 字段。externalconf 只决定「并入谁」,不改变 scope 标记;想同时拿到「并入 + 打标」两个效果,conf="*" optionalconf="optional" 即可(optional 配置自然被并入)。
2.4 templateComponent:富化元数据而不动哈希
sbom-template target 给 gson 挂了模板:
<cdx:ivyModule conf="*">
<cdx:templateComponent name="gson" group="com.google.code.gson">
<cdx:license licenseId="MIT"/>
<cdx:externalReference type="website" url="https://github.com/google/gson"/>
</cdx:templateComponent>
</cdx:ivyModule>
产物中 gson 组件实测(哈希 8 个全部保留,此处省略):license 变为 MIT、website 外部引用变为真实地址,同时 2.2 节那个空 website 引用被替换掉了:
{"type": "library", "bom-ref": "pkg:maven/com.google.code.gson/gson@2.10.1?type=jar", "group": "com.google.code.gson", "name": "gson", "version": "2.10.1", "licenses": [{"license": {"id": "MIT"}}], "purl": "pkg:maven/com.google.code.gson/gson@2.10.1?type=jar", "externalReferences": [{"type": "website", "url": "https://github.com/google/gson"}]}
用法判断:Ivy 元数据缺什么就补什么(自定义 fork 版本的许可证、内部镜像地址等),哈希与版本信息仍由解析结果保证,模板不会覆盖它们。
2.5 哈希来源:retrieve 目录与 Ivy 缓存等价
sbom-noretrieve target 只 ivy:resolve 不 ivy:retrieve,直接生成 SBOM。把它的产物与 retrieve 版本逐组件比对(含全部哈希字节),实测结果:
compile==noretrieve components: True
即不 retrieve 也有哈希——antlib 回退到 Ivy 缓存目录(~/.ivy2/cache/)里取构件计算。工程含义:CI 上即使跳过 retrieve,SBOM 的哈希依然完整;但要注意 retrieve 目录与缓存不一致的罕见场景(手工换过 jar)时,两处来源可能给出不同哈希,保持构建流程单一来源最稳。
2.6 驱逐触发 NPE:复现与零驱逐配方
这是 Ivy 集成最深的坑。构造一个会发生版本仲裁驱逐的场景(/root/cdx-lab/ivy-cfg2.xml,只依赖 commons-configuration2 2.10.1,其传递依赖与显式依赖的 commons-text 版本在仲裁中互相驱逐),运行 ivyModule 时实测报错:
BUILD FAILED
/root/cdx-lab/build-cfg2.xml:80: java.lang.NullPointerException: Cannot invoke "org.apache.ivy.core.module.descriptor.ModuleDescriptor.getModuleRevisionId()" because "md" is null
只要 Ivy 解析报告里存在被驱逐(evicted)的模块,ivyModule 就会触发这个空指针。 排查时先确认是不是驱逐问题:把 Ivy 的 resolve 报告打开(ivy:resolve 后的 report 产物)看有没有 evicted 标记。
两种修复思路的实测结果:
- 思路一(失败):在描述符里显式声明与传递依赖一致的 commons-text 版本(
ivy-cfg2fix.xml,configuration2 2.10.1 + commons-text 1.12.0)。实测仍然同样报错——显式声明统一了最终版本,但被驱逐的旧候选仍留在解析报告里,NPE 照旧:
BUILD FAILED
/root/cdx-lab/build-cfg2fix.xml:80: java.lang.NullPointerException: Cannot invoke "org.apache.ivy.core.module.descriptor.ModuleDescriptor.getModuleRevisionId()" because "md" is null
- 思路二(成功):在引入传递冲突源的依赖上用
<exclude>把旧候选挡在解析报告之外,再显式声明需要的版本——即本章开头ivy.xml的写法(configuration2 上exclude commons-text+ 顶层显式commons-text 1.12.0)。实测sbom-compile构建成功,产物 4 个组件、全部 8 哈希。
配方模板(可直接抄):
<dependency org="org.apache.commons" name="commons-configuration2" rev="2.10.1" conf="compile->default">
<!-- 关键:把会引发版本仲裁驱逐的传递依赖挡在报告外 -->
<exclude org="org.apache.commons" module="commons-text" conf="compile"/>
</dependency>
<dependency org="org.apache.commons" name="commons-text" rev="1.12.0" conf="compile->default"/>
💡 技术拓展:这与第一章备忘的另一个 Ivy 集成坑同源——taskdef 各带 classpath 会把 Ivy 类加载两份导致
ClassCastException(原文与解法见《Apache CycloneDX Ant Library 0.2 常用 API 与任务详解》第七章 Q1)。Ivy 深度集成的两条铁律:全部 taskdef 去内嵌 classpath 改用ant -lib;解析报告里不许有驱逐模块。
三、可复现构建 SOURCE_DATE_EPOCH
CycloneDX SBOM 有两处「每次构建必变」的字段:随机 serialNumber 与当前时间 metadata.timestamp。antlib 0.2 支持通过环境变量 SOURCE_DATE_EPOCH(Unix 秒)把两者同时固定,这是 reproducible-builds.org 的通用约定。五组实测(工程 /root/cdx-advanced,target sbom-repro,注意按第一章惯例带 -lib):
对照组——不设 SDE:serial 随机(v4 形态)、timestamp 为当前时间:
urn:uuid:00a17322-4951-4838-afc3-1d21194b4555 2026-10-15T08:20:34Z
实验一——SOURCE_DATE_EPOCH=1760486400:
SOURCE_DATE_EPOCH=1760486400 ant -lib /opt/cyclonedx-antlib/lib sbom-repro 2>&1 | grep BUILD
BUILD SUCCESSFUL
python3 -c "import json;d=json.load(open('bom/notes-service-repro.json'));print(d['serialNumber'],d['metadata']['timestamp'])"
urn:uuid:e52aa000-0199-1000-b4d0-663096d8c180 2025-10-15T00:00:00Z
1760486400 正是 2025-10-15T00:00:00Z:timestamp 被固定为 SDE 时刻,serialNumber 变成 v1 形态(第 13 位是 1,时间信息编码进了 UUID 前段)。
实验二——同一 SDE 重复构建,逐字节比对:
md5sum bom/notes-service-repro.json
SOURCE_DATE_EPOCH=1760486400 ant -lib /opt/cyclonedx-antlib/lib sbom-repro >/dev/null 2>&1
md5sum bom/notes-service-repro.json
363269f734898944b2f04370c29dd2ad bom/notes-service-repro.json
363269f734898944b2f04370c29dd2ad bom/notes-service-repro.json
两次构建 md5 完全一致——SBOM 达到逐字节可复现(前提是构建输入不变,包括第二章的依赖解析结果)。
实验三——换 SDE 为 1760572800:
urn:uuid:ea50fc00-0199-1000-b4d0-663096d8c180 2025-10-16T00:00:00Z
timestamp 精确推进一天,serial 随之变化。结论汇总:
| 场景 | serialNumber | timestamp |
|---|---|---|
| 不设 SDE | v4 随机 | 当前时间 |
| 仅固定 serialNumber 属性(API 篇 3.4 节) | 固定 | 当前时间 |
| 设置 SDE | 由 SDE 派生固定 | SDE 时刻 |
💡 技术拓展:发布流水线里推荐 SDE 取「本次发布的时间戳常量」并入库(如
.sde-file或 CI 环境变量),配合固定 serialNumber 可以让同一版本源码在任何机器上产出同一份 SBOM,审计侧即可对 SBOM 做差分比对。
四、Schema 校验与质量门禁
生成 SBOM 之后,工程化流程必须回答两个问题:这份文件合法吗?内容达标吗?本章用两个脚本闭环:validate-sbom.py 管合法性,check-sbom.sh 管门禁。
4.1 离线校验脚本
先下载官方 1.7 schema 三件套(bom-1.7.schema.json 内部相对引用 spdx.schema.json 与 jsf-0.82.schema.json,三个都必须就位;jsf-0.82.schema.json 首次下载偶发得到 0 字节文件,下载后用 ls -la 核对大小,异常就重下):
cd /root/cdx-advanced && mkdir -p schemas && cd schemas
curl -fsSO https://cyclonedx.org/schema/bom-1.7.schema.json
curl -fsSO https://cyclonedx.org/schema/spdx.schema.json
curl -fsSO https://cyclonedx.org/schema/jsf-0.82.schema.json
/root/cdx-advanced/validate-sbom.py(完整内容):
#!/usr/bin/env python3
"""Validate a CycloneDX SBOM JSON file against the official 1.7 schema.
Usage: python3 validate-sbom.py <bom.json>
Exit code: 0 = valid, 1 = invalid, 2 = usage error.
"""
import json
import sys
from jsonschema import Draft7Validator, RefResolver
BASE = "http://cyclonedx.org/schema/"
FILES = ["bom-1.7.schema.json", "spdx.schema.json", "jsf-0.82.schema.json"]
def main():
if len(sys.argv) != 2:
print("usage: validate-sbom.py <bom.json>", file=sys.stderr)
return 2
path = sys.argv[1]
store = {}
for name in FILES:
schema = json.load(open("schemas/" + name))
store["file:///root/cdx-advanced/schemas/" + name] = schema
store[BASE + name] = schema
schema = store[BASE + "bom-1.7.schema.json"]
resolver = RefResolver(base_uri=BASE + "bom-1.7.schema.json",
referrer=schema, store=store)
validator = Draft7Validator(schema, resolver=resolver)
instance = json.load(open(path))
errors = sorted(validator.iter_errors(instance), key=lambda e: list(e.path))
if errors:
for err in errors:
where = "/".join(str(p) for p in err.path) or "<root>"
print("INVALID %s: %s" % (where, err.message))
return 1
print("%s: valid CycloneDX 1.7 SBOM" % path)
return 0
if __name__ == "__main__":
sys.exit(main())
一个必须注意的实现细节:bom-1.7.schema.json 声明的 $id 是 http://cyclonedx.org/schema/...,直接校验时相对引用会尝试走网络,离线环境实测会长时间挂起。脚本因此把三个 schema 预载进 RefResolver 的 store,并同时挂 file:// 与 http://cyclonedx.org/schema/ 两种键,实现完全离线校验。
4.2 通过与篡改实测
四个场景的实测结果(含退出码):
python3 validate-sbom.py bom/notes-service-compile.json; echo exit=$?
bom/notes-service-compile.json: valid CycloneDX 1.7 SBOM
exit=0
篡改一:把 bomFormat 改成 CycloneDX2:
INVALID bomFormat: 'CycloneDX2' is not one of ['CycloneDX']
exit=1
篡改二:追加未知字段 unknownField:
INVALID <root>: Additional properties are not allowed ('unknownField' was unexpected)
exit=1
篡改三(意外发现):把 specVersion 改成 "9.9":
/tmp/tamper2.json: valid CycloneDX 1.7 SBOM
exit=0
specVersion 篡改能通过——实测证明 1.7 schema 并未把 specVersion 做成枚举约束。schema 合法不等于内容正确,这正是 4.3 节门禁脚本要叠加业务断言(组件数下限)的原因:schema 管结构,门禁管内容。
4.3 门禁脚本
/root/cdx-advanced/check-sbom.sh(完整内容):
#!/bin/sh
# SBOM 质量门禁:生成 -> schema 校验 -> 组件数断言
# 用法: BOM_MIN=3 ./check-sbom.sh
set -e
BOM_MIN="${BOM_MIN:-3}"
ant -lib /opt/cyclonedx-antlib/lib sbom-compile
python3 validate-sbom.py bom/notes-service-compile.json
COUNT=$(python3 -c 'import json;print(len(json.load(open("bom/notes-service-compile.json"))["components"]))')
if [ "$COUNT" -lt "$BOM_MIN" ]; then
echo "SBOM 门禁不通过:组件数 $COUNT 低于阈值 $BOM_MIN" >&2
exit 1
fi
echo "SBOM 门禁通过:schema 合法,组件数 $COUNT(阈值 $BOM_MIN)"
两场景实测(退出码直接取脚本自身,不经过管道):
./check-sbom.sh > /tmp/gate-pass.log 2>&1; echo pass-exit=$?; tail -1 /tmp/gate-pass.log
pass-exit=0
SBOM 门禁通过:schema 合法,组件数 4(阈值 3)
BOM_MIN=99 ./check-sbom.sh > /tmp/gate-fail.log 2>&1; echo fail-exit=$?; tail -2 /tmp/gate-fail.log
fail-exit=1
SBOM 门禁不通过:组件数 4 低于阈值 99
退出码 0/1 齐备,可以直接被任何 CI 的失败判定消费。
五、CI 流水线接入
5.1 通用 Shell 门禁
把 4.3 节脚本搬上 CI 前,先盘点前置条件(全部为本文实测环境事实):
- JDK 17 + Ant(JDK 8 会因 SHA3 缺失失败,见安装指南 FAQ)。
/opt/cyclonedx-antlib/lib下的 antlib 与依赖 jar(安装指南方案 A 的拉取命令可原样进 CI)。- Python 3 + jsonschema(实测版本 3.2.0)+
schemas/三件套随仓库提交。 - 工程内
ivy.xml已套用零驱逐配方(第二章 2.6 节),否则 Ivy 集成会 NPE。
本地全流程一条命令即可复跑:
cd /root/cdx-advanced && ./check-sbom.sh
SBOM 门禁通过:schema 合法,组件数 4(阈值 3)
5.2 GitHub Actions 配置示例
以下为配置示例(YAML 托管结构按 GitHub 官方文档写法组织,其中的安装与校验命令均来自本章与安装指南的实测命令):
name: sbom-gate
on: [push]
jobs:
sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- name: Provision Antlib and dependencies
run: |
mkdir -p /opt/cyclonedx-antlib/fetch /opt/cyclonedx-antlib/lib
cp ci/fetch-pom.xml /opt/cyclonedx-antlib/fetch/pom.xml
cd /opt/cyclonedx-antlib/fetch
mvn -q -B dependency:copy-dependencies -DoutputDirectory=/opt/cyclonedx-antlib/lib
- name: Install validator dependency
run: pip3 install jsonschema==3.2.0
- name: SBOM quality gate
run: |
cd cdx-advanced
./check-sbom.sh
要点:setup-java 固定 temurin 17 满足 SHA3 要求;jsonschema==3.2.0 与实测版本对齐;门禁脚本自身的退出码即 GitHub Actions 的步骤失败判定,无需额外包装。
5.3 Jenkins 声明式流水线配置示例
pipeline {
agent any
tools {
jdk 'jdk-17'
}
stages {
stage('SBOM quality gate') {
steps {
sh 'cd cdx-advanced && ./check-sbom.sh'
}
}
}
post {
always {
archiveArtifacts artifacts: 'cdx-advanced/bom/*.json', allowEmptyArchive: true
}
}
}
sh 步骤同样消费脚本的 0/1 退出码;post 段把 SBOM 产物归档,供审计平台拉取。
六、与 cyclonedx-maven-plugin 对比实测
同一份业务依赖(commons-configuration2 2.10.1 + commons-text 1.12.0),在 Maven 环境用 cyclonedx-maven-plugin 2.9.3 生成 SBOM,与本文 Ivy 工程的 antlib 产物正面对比。对比工程的 pom.xml 不内嵌插件配置,直接用完整坐标调用(便于固定插件版本做可复现实验):
cd /root/mvn-app
mvn -B org.cyclonedx:cyclonedx-maven-plugin:2.9.3:makeBom
[INFO] BUILD SUCCESS
产物 target/bom.json 里的组件名清单(实测):
grep -o '"name" : "[^"]*"' target/bom.json | head -12
"name" : "cyclonedx-maven-plugin"
"name" : "notes-service-mvn"
"name" : "maven.goal"
"name" : "maven.scopes"
"name" : "commons-configuration2"
"name" : "commons-lang3"
"name" : "commons-logging"
"name" : "commons-text"
两侧对比结果(全部字段均实测):
| 维度 | Antlib 0.2(Ant + Ivy) | cyclonedx-maven-plugin 2.9.3(Maven) |
|---|---|---|
| 默认 specVersion | 1.7 | 1.6 |
| 尝试升到 1.7 | 默认即是 | -Dcyclonedx.schemaVersion=1.7 不生效,产物仍为 1.6(实测,无告警静默回退) |
| 同一依赖集组件数 | 4(commons-logging/configuration2/lang3/text) | 4(同上四个) |
| 工具组件 | ant-cyclonedx 0.2,publisher 为 The Apache Software Foundation,11 条外部引用 | cyclonedx-maven-plugin 2.9.3,author 为 OWASP Foundation |
| 额外元数据 | lifecycles | lifecycles + metadata.properties(maven.goal、maven.scopes) |
| serialNumber | v4 随机;可用属性固定或 SDE 派生(第三章) | v3 形态;两次构建实测值不同(a1582a49-3048-… 对 ab8fa3aa-3709-…),不能直接当可复现依据 |
| bom-ref 习惯 | Ivy 集成场景带 ?type=jar 限定符 | 与 Maven 坐标一致 |
补充实测两个细节:其一,对 Maven 侧尝试 -Dcyclonedx.schemaVersion=1.7,构建成功但产物 specVersion 仍是 1.6——在 2.9.3 上该属性对 1.7 的设定不生效;而 antlib 默认就输出 1.7,这是当前两代工具最直接的规范版本差异。其二,Maven 侧的 serialNumber 虽是 v3(名字派生)形态,但两次构建值不同,若需要可复现 SBOM,antlib 的 SDE 机制是目前实测下来更直接的路。
选型建议:构建体系已经是 Maven,选 maven-plugin(零集成成本、maven.goal/scopes 审计信息丰富);构建体系是 Ant/Ivy,或者需要 CycloneDX 1.7、SDE 可复现 SBOM,选本文的 antlib。两者并存的多模块仓库也可以并存使用——产物格式一致,审计平台无感。
七、参考链接
- CycloneDX 官方 schema(本文下载三件套的来源):https://cyclonedx.org/schema/bom-1.7.schema.json
- SOURCE_DATE_EPOCH 规范:https://reproducible-builds.org/docs/source-date-epoch/
- Apache Ivy 文档(配置与驱逐仲裁):https://ant.apache.org/ivy/
- CycloneDX Antlib 官方手册:https://ant.apache.org/antlibs/cyclonedx/manual/
- cyclonedx-maven-plugin 项目主页:https://cyclonedx.github.io/cyclonedx-maven-plugin/
- GitHub Actions setup-java:https://github.com/actions/setup-java
- Jenkins Pipeline 语法:https://www.jenkins.io/doc/book/pipeline/
- 专栏内前置阅读:《Ubuntu 22.04 安装 Apache CycloneDX Ant Library 指南》《CentOS 8 安装 Apache CycloneDX Ant Library 指南》《Apache CycloneDX Ant Library 0.2 快速入门与集成示例》《Apache CycloneDX Ant Library 0.2 常用 API 与任务详解》