diff --git a/.depot/workflows/build.yaml b/.depot/workflows/build.yaml index c5c5a8c..9e11087 100644 --- a/.depot/workflows/build.yaml +++ b/.depot/workflows/build.yaml @@ -19,9 +19,6 @@ permissions: contents: read packages: write id-token: write -env: - IMAGE_NAME: ghcr.io/getarcaneapp/tools - PUBLISH_PLATFORMS: linux/amd64,linux/386,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x jobs: validate: if: ${{ github.repository_owner != 'getarcaneapp' && github.actor != @@ -30,18 +27,19 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v6 + - name: Install just + uses: extractions/setup-just@v3 + - name: Install yq + run: | + sudo curl -fsSL -o /usr/local/bin/yq \ + https://github.com/mikefarah/yq/releases/download/v4.45.1/yq_linux_amd64 + sudo chmod +x /usr/local/bin/yq - name: Set up Depot CLI uses: depot/setup-action@v1 - name: Build validation image - uses: depot/build-push-action@v1 - with: - project: np622krb2x - context: . - load: true - platforms: linux/amd64 - tags: arcane-toolbox:ci + run: just build-ci - name: Validate runtime contract - run: ./scripts/validate.sh arcane-toolbox:ci + run: just validate publish: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') needs: validate @@ -49,9 +47,26 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v6 + - name: Install just + uses: extractions/setup-just@v3 + - name: Install yq + run: | + sudo curl -fsSL -o /usr/local/bin/yq \ + https://github.com/mikefarah/yq/releases/download/v4.45.1/yq_linux_amd64 + sudo chmod +x /usr/local/bin/yq + - name: Resolve build inputs from build.yaml + id: cfg + run: | + just prepare + { + echo "alpine=$(yq -r '.versions.alpine' build.yaml)" + echo "trivy=$(yq -r '.versions.trivy' build.yaml)" + echo "busybox=$(yq -r '.versions.busybox' build.yaml)" + echo "platforms=$(yq -r '.platforms.publish | join(",")' build.yaml)" + echo "image=$(yq -r '.image.name' build.yaml)" + } >> "$GITHUB_OUTPUT" - name: Set up Depot CLI uses: depot/setup-action@v1 - - name: Install cosign uses: sigstore/cosign-installer@v4.1.1 - name: Log in to GHCR @@ -64,7 +79,9 @@ jobs: id: meta uses: docker/metadata-action@v6 with: - images: ${{ env.IMAGE_NAME }} + images: ${{ steps.cfg.outputs.image }} + flavor: | + latest=true tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} @@ -77,26 +94,31 @@ jobs: project: np622krb2x context: . file: ./Dockerfile - platforms: ${{ env.PUBLISH_PLATFORMS }} + platforms: ${{ steps.cfg.outputs.platforms }} push: true + build-args: | + ALPINE_VERSION=${{ steps.cfg.outputs.alpine }} + TRIVY_VERSION=${{ steps.cfg.outputs.trivy }} + BUSYBOX_VERSION=${{ steps.cfg.outputs.busybox }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - name: Sign published image - run: cosign sign --yes --key env://COSIGN_PRIVATE_KEY "${IMAGE_NAME}@${{ steps.build.outputs.digest }}" + run: cosign sign --yes --key env://COSIGN_PRIVATE_KEY "${IMAGE}@${{ steps.build.outputs.digest }}" env: + IMAGE: ${{ steps.cfg.outputs.image }} COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }} COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} - name: Generate image digest attestation uses: actions/attest@v4 with: - subject-name: ${{ env.IMAGE_NAME }} + subject-name: ${{ steps.cfg.outputs.image }} subject-digest: ${{ steps.build.outputs.digest }} push-to-registry: true - name: Record published digest run: | { - echo "Published image: ${{ env.IMAGE_NAME }}" - echo "Published platforms: ${{ env.PUBLISH_PLATFORMS }}" + echo "Published image: ${{ steps.cfg.outputs.image }}" + echo "Published platforms: ${{ steps.cfg.outputs.platforms }}" echo "Published digest: ${{ steps.build.outputs.digest }}" - echo "Published attestation: ${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}" + echo "Published attestation: ${{ steps.cfg.outputs.image }}@${{ steps.build.outputs.digest }}" } >> "$GITHUB_STEP_SUMMARY" diff --git a/.dockerignore b/.dockerignore index 35bdd69..a79e52b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,4 +3,3 @@ ./scripts/ LICENSE README.md -third_party diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 3542ed6..b1186a4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -16,10 +16,6 @@ permissions: packages: write id-token: write -env: - IMAGE_NAME: ghcr.io/getarcaneapp/tools - PUBLISH_PLATFORMS: linux/amd64,linux/386,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x - jobs: validate: if: ${{ github.repository_owner != 'getarcaneapp' && github.actor != @@ -29,19 +25,23 @@ jobs: - name: Check out repository uses: actions/checkout@v6 + - name: Install just + uses: extractions/setup-just@v3 + + - name: Install yq + run: | + sudo curl -fsSL -o /usr/local/bin/yq \ + https://github.com/mikefarah/yq/releases/download/v4.45.1/yq_linux_amd64 + sudo chmod +x /usr/local/bin/yq + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Build validation image - uses: docker/build-push-action@v7 - with: - context: . - load: true - platforms: linux/amd64 - tags: arcane-toolbox:ci + run: just build-ci - name: Validate runtime contract - run: ./scripts/validate.sh arcane-toolbox:ci + run: just validate publish: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') @@ -51,6 +51,27 @@ jobs: - name: Check out repository uses: actions/checkout@v6 + - name: Install just + uses: extractions/setup-just@v3 + + - name: Install yq + run: | + sudo curl -fsSL -o /usr/local/bin/yq \ + https://github.com/mikefarah/yq/releases/download/v4.45.1/yq_linux_amd64 + sudo chmod +x /usr/local/bin/yq + + - name: Resolve build inputs from build.yaml + id: cfg + run: | + just prepare + { + echo "alpine=$(yq -r '.versions.alpine' build.yaml)" + echo "trivy=$(yq -r '.versions.trivy' build.yaml)" + echo "busybox=$(yq -r '.versions.busybox' build.yaml)" + echo "platforms=$(yq -r '.platforms.publish | join(",")' build.yaml)" + echo "image=$(yq -r '.image.name' build.yaml)" + } >> "$GITHUB_OUTPUT" + - name: Set up Depot CLI uses: depot/setup-action@v1 @@ -68,7 +89,9 @@ jobs: id: meta uses: docker/metadata-action@v6 with: - images: ${{ env.IMAGE_NAME }} + images: ${{ steps.cfg.outputs.image }} + flavor: | + latest=true tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} @@ -82,30 +105,35 @@ jobs: project: np622krb2x context: . file: ./Dockerfile - platforms: ${{ env.PUBLISH_PLATFORMS }} + platforms: ${{ steps.cfg.outputs.platforms }} push: true + build-args: | + ALPINE_VERSION=${{ steps.cfg.outputs.alpine }} + TRIVY_VERSION=${{ steps.cfg.outputs.trivy }} + BUSYBOX_VERSION=${{ steps.cfg.outputs.busybox }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - name: Sign published image - run: cosign sign --yes --key env://COSIGN_PRIVATE_KEY "${IMAGE_NAME}@${{ + run: cosign sign --yes --key env://COSIGN_PRIVATE_KEY "${IMAGE}@${{ steps.build.outputs.digest }}" env: + IMAGE: ${{ steps.cfg.outputs.image }} COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }} COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} - name: Generate image digest attestation uses: actions/attest@v4 with: - subject-name: ${{ env.IMAGE_NAME }} + subject-name: ${{ steps.cfg.outputs.image }} subject-digest: ${{ steps.build.outputs.digest }} push-to-registry: true - name: Record published digest run: | { - echo "Published image: ${{ env.IMAGE_NAME }}" - echo "Published platforms: ${{ env.PUBLISH_PLATFORMS }}" + echo "Published image: ${{ steps.cfg.outputs.image }}" + echo "Published platforms: ${{ steps.cfg.outputs.platforms }}" echo "Published digest: ${{ steps.build.outputs.digest }}" - echo "Published attestation: ${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}" + echo "Published attestation: ${{ steps.cfg.outputs.image }}@${{ steps.build.outputs.digest }}" } >> "$GITHUB_STEP_SUMMARY" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..849ddff --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/Dockerfile b/Dockerfile index 2dcd10f..b16fff1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ RUN apk add --no-cache ca-certificates curl tar WORKDIR /work -COPY checksums/trivy_0.70.0_checksums.txt /checksums/trivy_checksums.txt +COPY checksums/trivy.txt /checksums/trivy_checksums.txt RUN case "${TARGETARCH}/${TARGETVARIANT}" in \ amd64/*) trivy_arch='64bit' ;; \ @@ -38,8 +38,9 @@ RUN apk add --no-cache build-base bzip2 curl linux-headers perl WORKDIR /work -COPY checksums/busybox-1.37.0.tar.bz2.sha256 /checksums/busybox.tar.bz2.sha256 -COPY busybox.config /tmp/busybox.config +COPY checksums/busybox.sha256 /checksums/busybox.tar.bz2.sha256 +COPY dist/busybox.config /tmp/busybox.config +COPY dist/applets.txt /tmp/applets.txt RUN curl -fsSLO "https://busybox.net/downloads/busybox-${BUSYBOX_VERSION}.tar.bz2" && \ sha256sum -c /checksums/busybox.tar.bz2.sha256 && \ @@ -62,20 +63,10 @@ RUN make -j"$(getconf _NPROCESSORS_ONLN)" RUN install -Dm755 busybox /out/bin/busybox && \ mkdir -p /out/tmp /out/root/.cache && \ - ln -s /bin/busybox /out/bin/sh && \ - ln -s /bin/busybox /out/bin/sleep && \ - ln -s /bin/busybox /out/bin/find && \ - ln -s /bin/busybox /out/bin/gzip && \ - ln -s /bin/busybox /out/bin/stat && \ - ln -s /bin/busybox /out/bin/readlink && \ - ln -s /bin/busybox /out/bin/head && \ - ln -s /bin/busybox /out/bin/rm && \ - ln -s /bin/busybox /out/bin/mkdir && \ - ln -s /bin/busybox /out/bin/mv && \ - ln -s /bin/busybox /out/bin/rmdir && \ - ln -s /bin/busybox /out/bin/mktemp && \ - ln -s /bin/busybox /out/bin/tar && \ - ln -s /bin/busybox /out/bin/test && \ + while IFS= read -r applet; do \ + [ -n "${applet}" ] || continue; \ + ln -s /bin/busybox "/out/bin/${applet}"; \ + done < /tmp/applets.txt && \ chmod 1777 /out/tmp FROM scratch diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..41ca485 --- /dev/null +++ b/Justfile @@ -0,0 +1,88 @@ +# Justfile — orchestrates the arcane-toolbox build from build.yaml. +# Prereqs: just, yq (github.com/mikefarah/yq, v4+), docker (with buildx). + +set shell := ["bash", "-eu", "-o", "pipefail", "-c"] + +config := "build.yaml" + +# Resolved at startup so a broken build.yaml fails before any work runs. +alpine_version := `yq -r '.versions.alpine' build.yaml` +trivy_version := `yq -r '.versions.trivy' build.yaml` +busybox_version := `yq -r '.versions.busybox' build.yaml` +image_name := `yq -r '.image.name' build.yaml` +local_tag := `yq -r '.image.local_tag' build.yaml` +ci_tag := `yq -r '.image.ci_tag' build.yaml` +validate_plat := `yq -r '.platforms.validate' build.yaml` +publish_plats := `yq -r '.platforms.publish | join(",")' build.yaml` + +default: list + +list: + @just --list + +versions: + @echo "alpine: {{alpine_version}}" + @echo "trivy: {{trivy_version}}" + @echo "busybox: {{busybox_version}}" + @echo "image: {{image_name}}" + @echo "local_tag: {{local_tag}}" + @echo "ci_tag: {{ci_tag}}" + @echo "validate: {{validate_plat}}" + @echo "publish: {{publish_plats}}" + +# Materialize YAML-derived inputs the Dockerfile expects. +prepare: manifest + mkdir -p dist + yq -r '.busybox.config[] | . + "=y"' {{config}} > dist/busybox.config + yq -r '.busybox.applets[]' {{config}} > dist/applets.txt + +# Regenerate checksums/manifest.md from build.yaml. +manifest: + @printf '# Third-Party Manifest\n\nGenerated from `build.yaml`; run `just prepare` to regenerate.\n\nThird-party binaries shipped in the final runtime image.\n\n| Binary | Version | Source | Checksum | License |\n|---|---|---|---|---|\n| Trivy | %s | | [trivy.txt](trivy.txt) | Apache-2.0 |\n| BusyBox | %s | | [busybox.sha256](busybox.sha256) | GPL-2.0-only |\n\nThe CA certificate bundle is copied from Alpine %s during the build and is not\ntreated as a separately versioned executable binary.\n' \ + '{{trivy_version}}' '{{trivy_version}}' \ + '{{busybox_version}}' '{{busybox_version}}' \ + '{{alpine_version}}' \ + > checksums/manifest.md + +# Build for the validate platform, load into local docker as local_tag. +build: prepare + docker buildx build \ + --load \ + --platform {{validate_plat}} \ + --build-arg ALPINE_VERSION={{alpine_version}} \ + --build-arg TRIVY_VERSION={{trivy_version}} \ + --build-arg BUSYBOX_VERSION={{busybox_version}} \ + -t {{local_tag}} \ + . + +# Build the CI validation image. Tag is parameterized so the workflow can +# pass arcane-toolbox:ci explicitly. +build-ci tag=ci_tag: prepare + docker buildx build \ + --load \ + --platform {{validate_plat}} \ + --build-arg ALPINE_VERSION={{alpine_version}} \ + --build-arg TRIVY_VERSION={{trivy_version}} \ + --build-arg BUSYBOX_VERSION={{busybox_version}} \ + -t {{tag}} \ + . + +# Run the runtime contract checks against an already-built image. +validate tag=ci_tag: + ./scripts/validate.sh {{tag}} + +# Multi-platform build + push via Depot CLI. Workstation convenience — +# CI uses depot/build-push-action so it can hand the digest to cosign/attest. +publish tags: prepare + depot build \ + --project np622krb2x \ + --platform {{publish_plats}} \ + --build-arg ALPINE_VERSION={{alpine_version}} \ + --build-arg TRIVY_VERSION={{trivy_version}} \ + --build-arg BUSYBOX_VERSION={{busybox_version}} \ + $(printf -- '--tag %s ' {{tags}}) \ + --push \ + . + +clean: + rm -rf dist diff --git a/README.md b/README.md index 28437f3..7a3126b 100644 --- a/README.md +++ b/README.md @@ -31,4 +31,30 @@ The final image is built `FROM scratch` and contains only: ## Versions and provenance Pinned versions, source URLs, and checksum verification details are tracked in -`third_party/manifest.md` and the `checksums/` directory. +[`checksums/manifest.md`](checksums/manifest.md) (generated from `build.yaml` +by `just prepare`) alongside the per-binary checksum files in `checksums/`. + +## Building + +The build is driven by `build.yaml` (versions, target platforms, BusyBox +config flags, applet symlinks) and orchestrated by a `Justfile`. + +Prereqs: + +- [`just`](https://just.systems/) +- [`yq`](https://github.com/mikefarah/yq) v4+ +- `docker` with `buildx` + +Common recipes: + +```sh +just # list recipes +just versions # print resolved values from build.yaml +just prepare # render dist/busybox.config and dist/applets.txt from YAML +just build # build image for the local platform, load as arcane-toolbox:dev +just validate # run runtime-contract checks against arcane-toolbox:ci +just clean # remove dist/ +``` + +To bump a pinned version, edit `build.yaml` and update the matching file in +`checksums/` (`checksums/trivy.txt` or `checksums/busybox.sha256`). diff --git a/build.yaml b/build.yaml new file mode 100644 index 0000000..489ec7b --- /dev/null +++ b/build.yaml @@ -0,0 +1,71 @@ +# build.yaml — declarative inputs for the arcane-toolbox image build. +# Edited by humans; consumed by the Justfile via yq (Mike Farah's Go yq, v4+). + +image: + name: ghcr.io/getarcaneapp/tools + local_tag: ghcr.io/getarcaneapp/tools:dev + ci_tag: ghcr.io/getarcaneapp/tools:ci + +versions: + alpine: "3.23" + trivy: "0.70.0" + busybox: "1.37.0" + +platforms: + validate: linux/amd64 + publish: + - linux/amd64 + - linux/386 + - linux/arm/v7 + - linux/arm64 + - linux/ppc64le + - linux/s390x + +busybox: + # CONFIG_* keys forced to =y in the BusyBox .config. The Justfile appends + # =y when generating dist/busybox.config — encoding values directly here + # would invite YAML truthy/falsy coercion (`y` -> true). + config: + - CONFIG_STATIC + - CONFIG_LFS + - CONFIG_ASH + - CONFIG_SH_IS_ASH + - CONFIG_ASH_PRINTF + - CONFIG_FEATURE_FANCY_HEAD + - CONFIG_FEATURE_FANCY_SLEEP + - CONFIG_FEATURE_FIND_MAXDEPTH + - CONFIG_FEATURE_SEAMLESS_GZ + - CONFIG_FEATURE_STAT_FORMAT + - CONFIG_FEATURE_TAR_CREATE + - CONFIG_FEATURE_TAR_GNU_EXTENSIONS + - CONFIG_FIND + - CONFIG_GZIP + - CONFIG_HEAD + - CONFIG_MKDIR + - CONFIG_MKTEMP + - CONFIG_MV + - CONFIG_READLINK + - CONFIG_RM + - CONFIG_RMDIR + - CONFIG_SLEEP + - CONFIG_STAT + - CONFIG_TAR + - CONFIG_TEST + + # Names symlinked to /bin/busybox in the final image. Also asserted by + # validate.sh (each must exist as /bin/ and be executable). + applets: + - sh + - sleep + - find + - gzip + - stat + - readlink + - head + - rm + - mkdir + - mv + - rmdir + - mktemp + - tar + - test diff --git a/busybox.config b/busybox.config deleted file mode 100644 index 9af1712..0000000 --- a/busybox.config +++ /dev/null @@ -1,25 +0,0 @@ -CONFIG_STATIC=y -CONFIG_LFS=y -CONFIG_ASH=y -CONFIG_SH_IS_ASH=y -CONFIG_ASH_PRINTF=y -CONFIG_FEATURE_FANCY_HEAD=y -CONFIG_FEATURE_FANCY_SLEEP=y -CONFIG_FEATURE_FIND_MAXDEPTH=y -CONFIG_FEATURE_SEAMLESS_GZ=y -CONFIG_FEATURE_STAT_FORMAT=y -CONFIG_FEATURE_TAR_CREATE=y -CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y -CONFIG_FIND=y -CONFIG_GZIP=y -CONFIG_HEAD=y -CONFIG_MKDIR=y -CONFIG_MKTEMP=y -CONFIG_MV=y -CONFIG_READLINK=y -CONFIG_RM=y -CONFIG_RMDIR=y -CONFIG_SLEEP=y -CONFIG_STAT=y -CONFIG_TAR=y -CONFIG_TEST=y diff --git a/checksums/busybox-1.37.0.tar.bz2.sha256 b/checksums/busybox.sha256 similarity index 100% rename from checksums/busybox-1.37.0.tar.bz2.sha256 rename to checksums/busybox.sha256 diff --git a/checksums/manifest.md b/checksums/manifest.md new file mode 100644 index 0000000..a09c3b1 --- /dev/null +++ b/checksums/manifest.md @@ -0,0 +1,13 @@ +# Third-Party Manifest + +Generated from `build.yaml`; run `just prepare` to regenerate. + +Third-party binaries shipped in the final runtime image. + +| Binary | Version | Source | Checksum | License | +|---|---|---|---|---| +| Trivy | 0.70.0 | | [trivy.txt](trivy.txt) | Apache-2.0 | +| BusyBox | 1.37.0 | | [busybox.sha256](busybox.sha256) | GPL-2.0-only | + +The CA certificate bundle is copied from Alpine 3.23 during the build and is not +treated as a separately versioned executable binary. diff --git a/checksums/trivy_0.70.0_checksums.txt b/checksums/trivy.txt similarity index 100% rename from checksums/trivy_0.70.0_checksums.txt rename to checksums/trivy.txt diff --git a/scripts/validate.sh b/scripts/validate.sh index 16bf0b7..420d379 100755 --- a/scripts/validate.sh +++ b/scripts/validate.sh @@ -3,6 +3,15 @@ set -eu IMAGE_REF="${1:-arcane-toolbox:dev}" +CONFIG_FILE="${CONFIG_FILE:-build.yaml}" + +if ! command -v yq >/dev/null 2>&1; then + echo "validate.sh: yq is required (github.com/mikefarah/yq v4+)" >&2 + exit 2 +fi + +HELPERS="$(yq -r '.busybox.applets[]' "$CONFIG_FILE" | tr '\n' ' ')" +export HELPERS run_check() { check_name="$1" @@ -13,8 +22,8 @@ run_check() { } run_check "required helper commands are on PATH" \ - docker run --rm "$IMAGE_REF" sh -ceu ' - for helper in sh sleep find stat readlink head rm mkdir mv rmdir mktemp tar test; do + docker run --rm -e HELPERS "$IMAGE_REF" sh -ceu ' + for helper in $HELPERS; do test -x "/bin/${helper}" done test -x /usr/local/bin/trivy diff --git a/third_party/manifest.md b/third_party/manifest.md deleted file mode 100644 index 7d12330..0000000 --- a/third_party/manifest.md +++ /dev/null @@ -1,12 +0,0 @@ -# Third-Party Manifest - -This repository ships the following third-party binaries in the final runtime -image. - -| Binary | Version | Upstream source | Verification source | License | Notes | -| ------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------- | -| Trivy | 0.70.0 | https://github.com/aquasecurity/trivy/releases/download/v0.70.0/trivy_0.70.0_Linux-32bit.tar.gz, https://github.com/aquasecurity/trivy/releases/download/v0.70.0/trivy_0.70.0_Linux-64bit.tar.gz, https://github.com/aquasecurity/trivy/releases/download/v0.70.0/trivy_0.70.0_Linux-ARM.tar.gz, https://github.com/aquasecurity/trivy/releases/download/v0.70.0/trivy_0.70.0_Linux-ARM64.tar.gz, https://github.com/aquasecurity/trivy/releases/download/v0.70.0/trivy_0.70.0_Linux-PPC64LE.tar.gz, and https://github.com/aquasecurity/trivy/releases/download/v0.70.0/trivy_0.70.0_Linux-s390x.tar.gz | [checksums/trivy_0.70.0_checksums.txt](/Users/kmendell/code/getarcaneapp/tools/checksums/trivy_0.70.0_checksums.txt) copied from the official release checksum file | Apache-2.0 | Installed to `/usr/local/bin/trivy` in the final image for the published Linux target platforms | -| BusyBox | 1.37.0 | https://busybox.net/downloads/busybox-1.37.0.tar.bz2 | [checksums/busybox-1.37.0.tar.bz2.sha256](/Users/kmendell/code/getarcaneapp/tools/checksums/busybox-1.37.0.tar.bz2.sha256) copied from the official upstream checksum file | GPL-2.0-only | Built from verified source with `CONFIG_STATIC=y`; `/bin/busybox` provides the Arcane helper applets | - -The CA certificate bundle is copied from Alpine's `ca-certificates` package -during the build and is not treated as a separately versioned executable binary.