mirror of
https://github.com/cozystack/cozystack.git
synced 2026-10-26 04:57:09 +09:00
No release carried a signature or an SBOM, so users could verify none of the ISO, disk images, kernel, initramfs, manifests and cozypkg binaries they download, and the OpenSSF Scorecard Signed-Releases check and CLOMonitor's signed_releases and sbom checks failed. hack/upload-assets.sh now hands its asset list to the new hack/sign-release-assets.sh before the first upload. That script writes cozystack-source-sbom.spdx.json with syft over the committed tree and the cozypkg binaries, cozystack-checksums.txt with the SHA-256 of every asset and the SBOM, and cozystack-checksums.txt.sigstore.json with a keyless cosign signature over the checksums. The SBOM does not describe the container images or the Talos boot images, which is what its name says; image SBOMs are #2937. The script verifies the bundle against the identities of tags.yaml on a v* tag and promote-rc.yaml on main before returning, so a bad signature fails the job with nothing uploaded, and hack/upload-assets.bats pins the upload list and that order. The two jobs that call make upload_assets get id-token: write, cosign v3.1.3 from the SHA-pinned cosign-installer, and syft from its release tarball checked against a committed SHA-256, because anchore/sbom-action/download-syft runs an install script fetched at run time; hack/ci-tool-installs-contract.bats now covers both. docs/release.md describes the files, what the SBOM covers, the minimum cosign version to verify a download with, what the certificate identity proves, and that promote-rc.yaml is dispatched from main only. Refs #4759 Assisted-by: LLM Signed-off-by: Marian Koreniuk <moriarti@cp.if.ua>
177 lines
5.3 KiB
Bash
177 lines
5.3 KiB
Bash
#!/usr/bin/env bats
|
|
# -----------------------------------------------------------------------------
|
|
# Unit tests for hack/upload-assets.sh.
|
|
#
|
|
# gh, syft and cosign are replaced by stubs on PATH, and the real
|
|
# hack/sign-release-assets.sh runs between them, so the tests hold what the
|
|
# upload script owns: which files it uploads, in which order, that the
|
|
# checksums name what it uploads, and that nothing is uploaded when signing
|
|
# fails.
|
|
#
|
|
# Both scripts take paths relative to the repository root (hack/..., and
|
|
# _out/assets for the release assets), so each test runs from a throwaway git
|
|
# repository that commits copies of them, with placeholder assets.
|
|
#
|
|
# Expected failures are written as `if CMD; then ... exit 1; fi`, never as a
|
|
# negated command: see hack/bats-no-negated-assert.bats.
|
|
# -----------------------------------------------------------------------------
|
|
|
|
load test_helper
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BATS_TEST_FILENAME:-$0}")/.." && pwd)"
|
|
|
|
# The fixture's release assets in upload order, with two cozypkg tarballs where
|
|
# a release has one per platform.
|
|
assets() {
|
|
printf '%s\n' \
|
|
cozystack-crds.yaml \
|
|
cozystack-operator-talos.yaml \
|
|
cozystack-operator-generic.yaml \
|
|
cozystack-operator-hosted.yaml
|
|
for arch in amd64 arm64; do
|
|
printf '%s\n' \
|
|
"metal-$arch.iso" \
|
|
"metal-$arch.raw.xz" \
|
|
"nocloud-$arch.raw.xz" \
|
|
"kernel-$arch" \
|
|
"initramfs-metal-$arch.xz"
|
|
done
|
|
printf '%s\n' \
|
|
cozypkg-darwin-arm64.tar.gz \
|
|
cozypkg-linux-amd64.tar.gz \
|
|
cozypkg-checksums.txt \
|
|
openapi.json
|
|
}
|
|
|
|
# What hack/upload-assets.sh uploads: the assets, then the three files
|
|
# hack/sign-release-assets.sh writes.
|
|
expected_uploads() {
|
|
assets
|
|
printf '%s\n' \
|
|
cozystack-source-sbom.spdx.json \
|
|
cozystack-checksums.txt \
|
|
cozystack-checksums.txt.sigstore.json
|
|
}
|
|
|
|
# A repository (current directory) with both scripts committed, every asset
|
|
# under _out/assets, and the stubs. gh records one line per call in gh-calls;
|
|
# COSIGN_VERIFY_EXIT sets the exit status of `cosign verify-blob`.
|
|
make_fixture() {
|
|
work="$(mktemp -d)"
|
|
mkdir -p "$work/src/hack" "$work/src/_out/assets" "$work/stub" "$work/pkg"
|
|
cp "$REPO_ROOT/hack/upload-assets.sh" "$REPO_ROOT/hack/sign-release-assets.sh" "$work/src/hack/"
|
|
cd "$work/src"
|
|
git init -q .
|
|
git config user.email ci@example.invalid
|
|
git config user.name CI
|
|
git config commit.gpgsign false
|
|
git config core.hooksPath /dev/null
|
|
git add hack
|
|
git commit -q -m seed
|
|
|
|
echo binary > "$work/pkg/cozypkg"
|
|
for name in $(assets); do
|
|
case $name in
|
|
cozypkg-*.tar.gz) tar -czf "_out/assets/$name" -C "$work/pkg" cozypkg ;;
|
|
*) echo "$name" > "_out/assets/$name" ;;
|
|
esac
|
|
done
|
|
|
|
cat > "$work/stub/gh" <<'STUB'
|
|
#!/bin/sh
|
|
echo "$*" >> "$STUB_LOG/gh-calls"
|
|
STUB
|
|
cat > "$work/stub/syft" <<'STUB'
|
|
#!/bin/sh
|
|
for arg in "$@"; do
|
|
case $arg in
|
|
spdx-json=*) echo '{"spdxVersion":"SPDX-2.3"}' > "${arg#spdx-json=}" ;;
|
|
esac
|
|
done
|
|
STUB
|
|
cat > "$work/stub/cosign" <<'STUB'
|
|
#!/bin/sh
|
|
case $1 in
|
|
sign-blob)
|
|
while [ "$#" -gt 0 ]; do
|
|
if [ "$1" = --bundle ]; then echo '{}' > "$2"; fi
|
|
shift
|
|
done
|
|
;;
|
|
verify-blob) exit "${COSIGN_VERIFY_EXIT:-0}" ;;
|
|
esac
|
|
STUB
|
|
chmod +x "$work/stub/gh" "$work/stub/syft" "$work/stub/cosign"
|
|
export PATH="$work/stub:$PATH" STUB_LOG="$work" VERSION=v1.2.3
|
|
}
|
|
|
|
# The status is returned explicitly: cozytest.sh appends `return 0` before a
|
|
# closing brace at column zero, which would turn a failed run into a success.
|
|
upload() {
|
|
hack/upload-assets.sh
|
|
return $?
|
|
}
|
|
|
|
# Last line of a test body rather than a trap, which the runners forbid.
|
|
cleanup() {
|
|
cd /
|
|
rm -rf "$work"
|
|
}
|
|
|
|
@test "uploads every asset, then the SBOM, the checksums and the bundle" {
|
|
make_fixture
|
|
upload
|
|
|
|
expected="$(expected_uploads | sed 's|^|release upload --clobber v1.2.3 _out/assets/|')"
|
|
if [ "$(cat "$work/gh-calls")" != "$expected" ]; then
|
|
printf 'gh was called with:\n%s\nexpected:\n%s\n' "$(cat "$work/gh-calls")" "$expected" >&2
|
|
exit 1
|
|
fi
|
|
|
|
cleanup
|
|
}
|
|
|
|
@test "the checksums name every upload but the checksums and the bundle" {
|
|
make_fixture
|
|
upload
|
|
|
|
names="$(awk '{ print $2 }' _out/assets/cozystack-checksums.txt)"
|
|
expected="$(assets; echo cozystack-source-sbom.spdx.json)"
|
|
if [ "$names" != "$expected" ]; then
|
|
printf 'checksums name:\n%s\nexpected:\n%s\n' "$names" "$expected" >&2
|
|
exit 1
|
|
fi
|
|
|
|
cleanup
|
|
}
|
|
|
|
@test "uploads nothing when the signature does not verify" {
|
|
make_fixture
|
|
export COSIGN_VERIFY_EXIT=1
|
|
if upload; then
|
|
echo "expected a failed verification to fail the upload" >&2
|
|
exit 1
|
|
fi
|
|
if [ -e "$work/gh-calls" ]; then
|
|
echo "expected no gh call; got:" >&2
|
|
cat "$work/gh-calls" >&2
|
|
exit 1
|
|
fi
|
|
cleanup
|
|
}
|
|
|
|
@test "uploads nothing when an asset is missing" {
|
|
make_fixture
|
|
rm _out/assets/nocloud-arm64.raw.xz
|
|
if upload; then
|
|
echo "expected a missing asset to fail the upload" >&2
|
|
exit 1
|
|
fi
|
|
if [ -e "$work/gh-calls" ]; then
|
|
echo "expected no gh call; got:" >&2
|
|
cat "$work/gh-calls" >&2
|
|
exit 1
|
|
fi
|
|
cleanup
|
|
}
|