
dasel v3.3.1 は Melange でパッケージ化され、最小限の apko イメージとして配布され、CVE-2026-33320 にパッチが適用されています。
このリポジトリは、dasel CLI(v3.3.1)を Melange で APK としてパッケージ化し、apko で最小限のコンテナイメージを構築し、CVE-2026-33320 の修正を v3.3.1 コードベースに適用したものです(バージョンアップではなくパッチ適用)。
結果として得られるのは、静的 dasel バイナリのみを含む約 4 MB のイメージで、シェルもパッケージマネージャも libc も含まず、非 root ユーザーで実行されます。
melange/
dasel.yaml # melange レシピ: v3.3.1 を取得し、パッチを適用し、.apk をビルド
apko/
dasel.yaml # apko レシピ: ローカルの .apk から最小限のイメージを組み立てる
patches/
cve-2026-33320.patch # CVE 修正(upstream 282943b の v3.3.1 への最小バックポート)
NOTES.md # CVE の内容、重大度、修正の仕組み
tests/
test.sh # ビルドした IMAGE を実行し、実際の動作と CVE 修正を検証
README.md
PATH に melange と apko が存在すること。開発時に使用したバージョン:
| Tool | Version |
|---|---|
| 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 変換、そして billion-laughs 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 を、自分の署名鍵で検証してインストールし、プリビルドされた upstream パッケージではないものを使用します。(相対パスが解決されるよう、リポジトリルートから 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 エイリアス展開による "billion laughs" 攻撃です。dasel は独自の UnmarshalYAML を実装しており、エイリアスノードを制限なしに再帰的に解決していたため、基盤となるライブラリの組み込み保護を回避していました。
私たちは、v3.3.1 ソースに対して upstream の修正(コミット 282943b、v3.3.2 でリリース)のみを patches/cve-2026-33320.patch としてバックポートしています。これは展開を**深さ制限(32)と共有予算(1000)**で制限し、無制限に展開する代わりにエラーを返します。v3.3.2 で同時にリリースされた無関係なバグ修正は意図的に除外し、変更を最小限で監査可能なものにしています。詳細は patches/NOTES.md を参照してください。
git-checkout は expected-commit を v3.3.1 コミット SHA に固定しているため、タグが別のコードを指すように変更された場合、ビルドは失敗します(サプライチェーン安全性)。CGO_ENABLED=0(レシピのビルド環境で設定)でビルドされるため、バイナリには cgo も共有ライブラリ依存もありません — これにより、イメージに libc/シェル/パッケージマネージャを含めないことが可能になります。packages: リストは dasel のみで、イメージはローカルビルドされたパッケージだけをインストールします(melange.rsa.pub で検証)。nonroot)で実行され、多層防御を実現します。x86_64 のみを対象にビルド(開発マシンのアーキテクチャ)。docker を使用(ネイティブ 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 ハンク — parsing/yaml/yaml.go に 1、parsing/yaml/yaml_reader.go に 6)、CVE 爆弾テストを含むすべてのパッケージテストが成功。apko build + tests/test.sh: イメージはビルドされ(約 4 MB のコンテンツ)、5 つのイメージテストすべてが成功。