本仓库将 dasel CLI(v3.3.1)打包为
使用 Melange 构建的 APK,使用 apko 构建一个最小容器镜像,并应用针对
CVE-2026-33320 的修复,同时保持在 v3.3.1 代码库上(补丁方式,而非版本升级)。
结果是一个约 4 MB 的镜像,仅包含静态的 dasel 二进制文件——没有 shell、没有包
管理器、没有 libc——并且以非 root 用户运行。
melange/
dasel.yaml # melange 配方:拉取 v3.3.1,应用补丁,构建 .apk
apko/
dasel.yaml # apko 配方:从本地 .apk 组装一个最小镜像
patches/
cve-2026-33320.patch # CVE 修复(上游 282943b 提交到 v3.3.1 的最小反向移植)
NOTES.md # 该 CVE 是什么、严重性以及修复原理
tests/
test.sh # 运行构建好的 IMAGE 并断言实际行为 + CVE 修复
README.md
PATH 中包含 melange 和 apko。开发时使用的版本:
| 工具 | 版本 |
|---|---|
| melange | 0.50.8 |
| apko | 1.2.14 |
| Docker | 29.5.2 |
以下所有命令均从仓库根目录运行。
melange keygen
生成 melange.rsa(私钥)和 melange.rsa.pub(公钥)。两者均被 gitignore——密钥由
复现构建的人重新生成,绝不提交。
melange build melange/dasel.yaml \
--source-dir patches \
--signing-key melange.rsa \
--arch x86_64 \
--runner docker
--source-dir patches 使 patches/cve-2026-33320.patch 在构建沙箱内可用,
配方中的 patch 步骤会应用它。输出:packages/x86_64/dasel-3.3.1-r0.apk
(已签名)和 packages/x86_64/APKINDEX.tar.gz。
melange test 将新构建的 .apk 安装到干净环境中,并运行配方的
test: 块。它需要 Wolfi 仓库(用于 busybox)和本地 packages/ 仓库(用于
dasel),每个都需要各自的签名密钥:
melange test melange/dasel.yaml dasel \
--arch x86_64 --runner docker \
--repository-append https://packages.wolfi.dev/os \
--keyring-append https://packages.wolfi.dev/os/wolfi-signing.rsa.pub \
--repository-append "$(pwd)/packages" \
--keyring-append melange.rsa.pub
断言:dasel version 报告 3.3.1;一次真实的 JSON 查询;一次 JSON→YAML 转换;以及
一个十亿笑 YAML 炸弹被扩展防护 拒绝(CVE-2026-33320 已修复)。
apko build apko/dasel.yaml dasel:test dasel.tar --arch x86_64
docker load < dasel.tar
输出:dasel.tar(一个可加载的 OCI 镜像)以及一份 SBOM(SPDX JSON)。apko 会将
架构附加到标签上,因此加载的镜像是 dasel:test-amd64。
镜像如何消费本地构建的软件包(关键约束)。 apko/dasel.yaml
将 ./packages 列为仓库,将 ./melange.rsa.pub 列为密钥环。因此 apko 安装的
是 melange build 写入 packages/x86_64/ 的那个确切 dasel APK——使用我们自己的
签名密钥验证——而不是预构建的上游软件包。(在仓库根目录运行 apko build,
以便这些相对路径能够解析。)这就是将软件包构建与镜像构建连接起来的关键。
./tests/test.sh
通过 docker run 运行镜像并断言(5 项检查):dasel 存在且报告 v3.3.1,一次
嵌套 JSON 查询,一次 JSON→YAML 转换,一次数组索引查询,以及 YAML 炸弹被拒绝。脚本
在任何检查失败时以非零状态退出(因此可用于 CI 门禁)。使用
IMAGE=<tag> ./tests/test.sh 覆盖标签。
CVE-2026-33320 是 dasel YAML 读取器中的一个拒绝服务漏洞(CWE-674,不受控制的递归):
通过不受限制的 YAML 别名扩展发起“十亿笑”攻击。dasel 自己实现了
UnmarshalYAML,并在没有任何限制的情况下递归解析别名节点,绕过了底层
库的内置防护。
我们仅将上游修复(提交 282943b,随 v3.3.2 发布)反向移植到 v3.3.1
源码,作为 patches/cve-2026-33320.patch。它通过 深度限制(32) 和共享的
预算(1000) 来限制扩展,出错时返回错误而不是无限制地扩展。我们刻意
排除了 v3.3.2 中同时发布的其他无关 bug 修复,使改动保持最小且可审计。详见
patches/NOTES.md。
git-checkout 将 expected-commit 固定到 v3.3.1 的提交 SHA,因此
如果标签日后被重新指向不同代码,构建将失败(供应链安全)。CGO_ENABLED=0 构建(在配方的构建环境中设置),因此
二进制没有 cgo 且没有共享库依赖——这正是镜像可以不携带
libc/shell/包管理器发布的原因。packages: 列表只有 dasel;镜像只安装我们
本地构建的软件包(使用我们的 melange.rsa.pub 验证)。nonroot)运行,实现纵深防御。x86_64 构建(开发机的架构)。docker 作为 Melange 的运行器(在原生 Linux 上 bubblewrap 也可以)。/etc/os-release);镜像中没有任何
内容需要它。这是一个刻意的极简选择,可以通过添加 wolfi-baselayout 轻松还原。go 和 busybox,而不是固定的快照,因此假设
Wolfi 持续提供 Go ≥ 1.25(dasel 的 go.mod 要求)。dasel 源码按提交固定;
同时固定构建工具链将使构建完全密封(见下文)。go/busybox 快照)和固定的构建日期,以实现
完全密封、逐位可复现的构建(源码已经按提交固定)。aarch64)。melange build + melange test:软件包构建成功,补丁干净应用(全部 7 个 hunk——1 个在
parsing/yaml/yaml.go,6 个在 parsing/yaml/yaml_reader.go),所有软件包测试通过,包括
CVE 炸弹测试。apko build + tests/test.sh:镜像构建成功(约 4 MB 内容),全部 5 项镜像测试通过。