
dasel v3.3.1 упакован с Melange и поставляется как минимальный образ apko, с исправлением для CVE-2026-33320
Этот репозиторий упаковывает CLI-утилиту dasel (v3.3.1) как APK-пакет с помощью Melange, собирает минимальный образ контейнера с помощью apko и применяет исправление для CVE-2026-33320, оставаясь на кодовой базе v3.3.1 (патч, а не обновление версии).
Результат — образ размером ~4 МБ, содержащий только статический бинарник dasel — без оболочки, без менеджера пакетов, без libc — который запускается от непривилегированного пользователя.
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 # запускает собранный ОБРАЗ и проверяет реальное поведение + исправление CVE
README.md
melange и apko в PATH.Версии, использованные при разработке:
| Инструмент | Версия |
|---|---|
| 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-бомба "billion laughs" отклоняется защитой от расширения (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 устанавливает именно тот APK dasel, который melange build записал в packages/x86_64/, проверяя его нашим собственным ключом подписи, а не предварительно собранный пакет из внешнего источника. (Запускайте 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 — это отказ в обслуживании (CWE-674, неконтролируемая рекурсия) в YAML-читателе dasel: атака "billion laughs" через неограниченное раскрытие алиасов YAML. dasel реализовал собственный UnmarshalYAML и разрешал узлы-алиасы рекурсивно без ограничений, обходя встроенную защиту нижележащей библиотеки.
Мы переносим только исправление из основной ветки (коммит 282943b, выпущенный в v3.3.2) на исходный код v3.3.1 в виде patches/cve-2026-33320.patch. Оно ограничивает раскрытие лимитом глубины (32) и общим бюджетом (1000), возвращая ошибку вместо бесконечного раскрытия. Мы намеренно исключили несвязанные исправления ошибок, которые также были в v3.3.2, чтобы изменение было минимальным и проверяемым. Подробности см. в patches/NOTES.md.
git-checkout закрепляет expected-commit за SHA-коммитом v3.3.1, поэтому сборка завершится ошибкой, если тег когда-либо будет перенаправлен на другой код (безопасность цепочки поставок).CGO_ENABLED=0 (установлено в среде сборки рецепта), поэтому бинарник не имеет cgo и зависимостей от разделяемых библиотек — что позволяет образу поставляться без libc/оболочки/менеджера пакетов.packages: в apko содержит только dasel; образ устанавливает только наш локально собранный пакет (проверенный нашим melange.rsa.pub).nonroot) для эшелонированной защиты.x86_64 (архитектура машины разработки).docker в качестве исполнителя Melange (bubblewrap также подойдёт на нативном Linux)./etc/os-release); ничто в образе не требует его. Это намеренный выбор минимальности, легко обратимый добавлением wolfi-baselayout.go и busybox из Wolfi, а не фиксированные снимки, поэтому предполагается, что Wolfi продолжает предоставлять Go ≥ 1.25 (требование go.mod dasel). Исходный код dasel закреплён коммитом; фиксация инструментария сборки также сделала бы сборку полностью герметичной (см. ниже).go/busybox из Wolfi) и фиксированную дату сборки для полностью герметичной, побитово воспроизводимой сборки (исходный код уже закреплён коммитом).aarch64) для ARM-машин.melange build + melange test: пакет собирается, патч применяется чисто (все 7 блоков — 1 в parsing/yaml/yaml.go, 6 в parsing/yaml/yaml_reader.go), все тесты пакета зелёные, включая тест CVE-бомбы.apko build + tests/test.sh: образ собирается (~4 МБ содержимого), все 5 тестов образа зелёные.