diff --git a/.github/actions/dependencies/install-shellcheck/action.yml b/.github/actions/dependencies/install-shellcheck/action.yml index eecd97c..946453d 100644 --- a/.github/actions/dependencies/install-shellcheck/action.yml +++ b/.github/actions/dependencies/install-shellcheck/action.yml @@ -21,3 +21,4 @@ runs: env: VERSION: ${{ inputs.version }} INPUTS_ARCHITECTURE: ${{ inputs.architecture }} + GITHUB_TOKEN: ${{ github.token }} # avoids API rate limits when looking up the asset digest diff --git a/.github/actions/dependencies/install-shellcheck/setup_shellcheck.sh b/.github/actions/dependencies/install-shellcheck/setup_shellcheck.sh index aa0e230..662f736 100755 --- a/.github/actions/dependencies/install-shellcheck/setup_shellcheck.sh +++ b/.github/actions/dependencies/install-shellcheck/setup_shellcheck.sh @@ -10,7 +10,22 @@ if [ "$ARCH" == "arm64" ]; then ARCH="aarch64" fi -curl -fL --retry 3 -o shellcheck.tar.xz "https://github.com/koalaman/shellcheck/releases/download/v${VERSION}/shellcheck-v${VERSION}.linux.${ARCH}.tar.xz" +FILE="shellcheck-v${VERSION}.linux.${ARCH}.tar.xz" +curl -fL --retry 3 -o shellcheck.tar.xz "https://github.com/koalaman/shellcheck/releases/download/v${VERSION}/${FILE}" + +# ShellCheck publishes no checksum file, so use the SHA-256 digest GitHub records for the release asset. +# GitHub records digests only for assets uploaded since mid-2025 (ShellCheck v0.11.0 and newer). +AUTH=() +if [ -n "${GITHUB_TOKEN:-}" ]; then + AUTH=(-H "Authorization: Bearer ${GITHUB_TOKEN}") +fi +EXPECTED=$(curl -fsSL --retry 3 "${AUTH[@]}" "https://api.github.com/repos/koalaman/shellcheck/releases/tags/v${VERSION}" \ + | jq -r --arg file "${FILE}" '.assets[] | select(.name == $file) | .digest // empty' | sed 's/^sha256://') +if [ -z "${EXPECTED}" ]; then + echo "::error::No SHA-256 digest published for ${FILE}; use ShellCheck v0.11.0 or newer" + exit 1 +fi +echo "${EXPECTED} shellcheck.tar.xz" | sha256sum -c - tar xf shellcheck.tar.xz -C /tmp --strip-components 1 rm -f shellcheck.tar.xz chmod +x /tmp/shellcheck diff --git a/.github/actions/dependencies/install-syft/install_syft.sh b/.github/actions/dependencies/install-syft/install_syft.sh index 8d47f1d..fc2ca8f 100755 --- a/.github/actions/dependencies/install-syft/install_syft.sh +++ b/.github/actions/dependencies/install-syft/install_syft.sh @@ -6,9 +6,12 @@ if [ -z "$ARCH" ]; then ARCH="amd64" fi -curl -fL --retry 3 -o syft.tar.gz "https://github.com/anchore/syft/releases/download/v${VERSION}/syft_${VERSION}_linux_${ARCH}.tar.gz" -tar xf syft.tar.gz -C /tmp -rm -f syft.tar.gz +BASE_URL="https://github.com/anchore/syft/releases/download/v${VERSION}" +FILE="syft_${VERSION}_linux_${ARCH}.tar.gz" +curl -fL --retry 3 -o "${FILE}" "${BASE_URL}/${FILE}" +curl -fsSL --retry 3 "${BASE_URL}/syft_${VERSION}_checksums.txt" | grep " ${FILE}$" | sha256sum -c - +tar xf "${FILE}" -C /tmp +rm -f "${FILE}" chmod +x /tmp/syft sudo mv /tmp/syft /usr/bin syft version diff --git a/.github/actions/dependencies/install-yq/install_yq.sh b/.github/actions/dependencies/install-yq/install_yq.sh index 1f6ecec..e1e745a 100755 --- a/.github/actions/dependencies/install-yq/install_yq.sh +++ b/.github/actions/dependencies/install-yq/install_yq.sh @@ -6,7 +6,13 @@ if [ -z "$ARCH" ]; then ARCH="amd64" fi -curl -fL --retry 3 "https://github.com/mikefarah/yq/releases/download/${VERSION}/yq_linux_${ARCH}" -o yq +BASE_URL="https://github.com/mikefarah/yq/releases/download/${VERSION}" +FILE="yq_linux_${ARCH}" +curl -fL --retry 3 "${BASE_URL}/${FILE}" -o yq +# The checksums file lists many hashes per file; checksums_hashes_order gives their order +SHA256_INDEX=$(curl -fsSL --retry 3 "${BASE_URL}/checksums_hashes_order" | grep -n '^SHA-256$' | cut -d: -f1) +EXPECTED=$(curl -fsSL --retry 3 "${BASE_URL}/checksums" | awk -v file="${FILE}" -v column="$((SHA256_INDEX + 1))" '$1 == file { print $column }') +echo "${EXPECTED} yq" | sha256sum -c - chmod +x yq sudo mv yq /usr/bin/ yq --version diff --git a/.github/actions/dependencies/setup-kind/setup-kind.sh b/.github/actions/dependencies/setup-kind/setup-kind.sh index bf98c57..c4cec54 100755 --- a/.github/actions/dependencies/setup-kind/setup-kind.sh +++ b/.github/actions/dependencies/setup-kind/setup-kind.sh @@ -45,7 +45,9 @@ function install_kubectl { if [ "${TEST_KUBECTL_VERSION:-latest}" = "latest" ]; then TEST_KUBECTL_VERSION=$(curl -fsSL --retry 3 https://dl.k8s.io/release/stable.txt) fi - curl -fL --retry 3 -o kubectl "https://dl.k8s.io/release/${TEST_KUBECTL_VERSION}/bin/linux/${ARCH}/kubectl" + KUBECTL_URL="https://dl.k8s.io/release/${TEST_KUBECTL_VERSION}/bin/linux/${ARCH}/kubectl" + curl -fL --retry 3 -o kubectl "${KUBECTL_URL}" + echo "$(curl -fsSL --retry 3 "${KUBECTL_URL}.sha256") kubectl" | sha256sum -c - chmod +x kubectl sudo mv kubectl /usr/local/bin kubectl version --client @@ -69,6 +71,7 @@ function install_kubernetes_provisioner { KIND_URL=https://github.com/kubernetes-sigs/kind/releases/download/${KIND_VERSION}/kind-linux-${ARCH} curl -fL --retry 3 -o kind "${KIND_URL}" + echo "$(curl -fsSL --retry 3 "${KIND_URL}.sha256sum" | awk '{ print $1 }') kind" | sha256sum -c - chmod +x kind # Move the binary to a globally accessible location diff --git a/.github/actions/dependencies/setup-minikube/setup-minikube.sh b/.github/actions/dependencies/setup-minikube/setup-minikube.sh index 3c40ef0..152d367 100755 --- a/.github/actions/dependencies/setup-minikube/setup-minikube.sh +++ b/.github/actions/dependencies/setup-minikube/setup-minikube.sh @@ -25,7 +25,9 @@ function install_kubectl { if [ "${TEST_KUBECTL_VERSION:-latest}" = "latest" ]; then TEST_KUBECTL_VERSION=$(curl -fsSL --retry 3 https://dl.k8s.io/release/stable.txt) fi - curl -fL --retry 3 -o kubectl "https://dl.k8s.io/release/${TEST_KUBECTL_VERSION}/bin/linux/${ARCH}/kubectl" + KUBECTL_URL="https://dl.k8s.io/release/${TEST_KUBECTL_VERSION}/bin/linux/${ARCH}/kubectl" + curl -fL --retry 3 -o kubectl "${KUBECTL_URL}" + echo "$(curl -fsSL --retry 3 "${KUBECTL_URL}.sha256") kubectl" | sha256sum -c - chmod +x kubectl sudo cp kubectl /usr/local/bin kubectl version --client @@ -54,6 +56,7 @@ if [ "$TEST_CLUSTER" = "minikube" ]; then fi curl -fL --retry 3 -o minikube "${TEST_MINIKUBE_URL}" + echo "$(curl -fsSL --retry 3 "${TEST_MINIKUBE_URL}.sha256") minikube" | sha256sum -c - chmod +x minikube sudo cp minikube /usr/local/bin minikube version diff --git a/.github/workflows/test-dependencies.yml b/.github/workflows/test-dependencies.yml index 0388db5..14384a7 100644 --- a/.github/workflows/test-dependencies.yml +++ b/.github/workflows/test-dependencies.yml @@ -83,9 +83,9 @@ jobs: matrix: architecture: - 'amd64' + # Older releases have no SHA-256 digest on GitHub, so the install action rejects them version: - '0.11.0' - - '0.10.0' steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: diff --git a/README.md b/README.md index 1c46143..b28e01b 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Actions for installing tools and setting up Kubernetes clusters. | `dependencies/setup-java` | Installs Java and Maven | `javaVersion` (21), `mavenVersion` (3.9.9), `javaDistro` (temurin) | | `dependencies/install-helm` | Installs Helm and helm-unittest plugin | `helmVersion` (v3.20.0), `helmUnitTestVersion` (v1.0.3) | | `dependencies/install-yq` | Installs yq YAML processor | `version` (v4.6.3), `architecture` (amd64) | -| `dependencies/install-shellcheck` | Installs ShellCheck linter | `version` (0.11.0), `architecture` (amd64) | +| `dependencies/install-shellcheck` | Installs ShellCheck linter | `version` (0.11.0, minimum 0.11.0), `architecture` (amd64) | | `dependencies/install-syft` | Installs Syft SBOM generation tool | `version` (1.20.0), `architecture` (amd64) | | `dependencies/install-oras` | Installs ORAS CLI for OCI artifacts | `version` (1.3.6) | | `dependencies/install-ascii-doctor` | Installs Ascii Doctor tool | `rubyVersion` (3.2) | @@ -48,6 +48,9 @@ Actions for building, testing, and releasing Strimzi components. | `build/push-oci-artifact` | Pushes a file to an OCI registry (GHCR by default) via ORAS | `file`, `name`, `tag` (required), `mediaType` (application/x-tar), `containerRegistry` (ghcr.io), `containerOrg` (repo owner). Outputs: `image` (full OCI ref with digest) | | `build/pull-oci-artifact` | Pulls an OCI artifact from a registry via ORAS | `image` (required), `outputDir` (.) | +> [!NOTE] +> Dependency actions verify the SHA-256 checksum of every binary they download and fail on a mismatch. + > [!IMPORTANT] > Build actions do **not** install their own dependencies (Java, yq, Helm, Docker, Shellcheck, Syft, etc.). > Callers must install the required dependencies using the appropriate dependency actions **before** invoking a build action.