From 6d43530ff6d8e4a00099e9ee45e302b6f0b92611 Mon Sep 17 00:00:00 2001 From: RocketQuack <202538874+Rocket-Quack@users.noreply.github.com> Date: Wed, 8 Apr 2026 16:44:28 +0200 Subject: [PATCH] fix(easy-docker): pin and verify gum fallback release --- scripts/easy-docker/README.md | 3 +- scripts/easy-docker/config/gum-checksums.tsv | 7 + scripts/easy-docker/lib/install/gum/assets.sh | 137 +++++++++++++++--- scripts/easy-docker/lib/install/gum/ensure.sh | 2 +- .../lib/install/gum/github_release.sh | 29 +++- 5 files changed, 153 insertions(+), 25 deletions(-) create mode 100644 scripts/easy-docker/config/gum-checksums.tsv diff --git a/scripts/easy-docker/README.md b/scripts/easy-docker/README.md index 53b32396..5b33bb9b 100644 --- a/scripts/easy-docker/README.md +++ b/scripts/easy-docker/README.md @@ -16,7 +16,8 @@ bash easy-docker.sh - Docker Desktop includes Compose v2 by default; on Linux Engine-only setups you may need the `docker-compose-plugin` package - Docker daemon must be running before the TUI starts - Required docker commands are validated (`docker ps/exec/inspect/cp` and `docker compose config/up/down/logs/exec/pull/ps`) -- If package manager installation for `gum` fails, the script can use a GitHub binary fallback +- If package manager installation for `gum` fails, the script can use a pinned GitHub binary fallback +- The GitHub fallback is pinned to `gum` `v0.17.0` and verifies SHA256 checksums from `scripts/easy-docker/config/gum-checksums.tsv` ## Options diff --git a/scripts/easy-docker/config/gum-checksums.tsv b/scripts/easy-docker/config/gum-checksums.tsv new file mode 100644 index 00000000..1babfb01 --- /dev/null +++ b/scripts/easy-docker/config/gum-checksums.tsv @@ -0,0 +1,7 @@ +# version asset_name sha256 +0.17.0 gum_0.17.0_Darwin_arm64.tar.gz e2a4b8596efa05821d8c58d0c1afbcd7ad1699ba69c689cc3ff23a4a99c8b237 +0.17.0 gum_0.17.0_Darwin_x86_64.tar.gz cd66576aeebe6cd19c771863c7e8d696e0e1d5387d1e7075666baa67c2052e53 +0.17.0 gum_0.17.0_Linux_arm64.tar.gz b0b9ed95cbf7c8b7073f17b9591811f5c001e33c7cfd066ca83ce8a07c576f9c +0.17.0 gum_0.17.0_Linux_armv7.tar.gz 25711c2fbc6887cde79ed586972834121a04955968808dd688c688381ac50ab2 +0.17.0 gum_0.17.0_Linux_x86_64.tar.gz 69ee169bd6387331928864e94d47ed01ef649fbfe875baed1bbf27b5377a6fdb +0.17.0 gum_0.17.0_Windows_x86_64.zip b2be80531c6babc8d4e0e6ca95773d58118a2e1582ae006aace08dbc55503072 diff --git a/scripts/easy-docker/lib/install/gum/assets.sh b/scripts/easy-docker/lib/install/gum/assets.sh index 8cfd9f00..977b5975 100755 --- a/scripts/easy-docker/lib/install/gum/assets.sh +++ b/scripts/easy-docker/lib/install/gum/assets.sh @@ -1,5 +1,122 @@ #!/usr/bin/env bash +get_gum_checksums_path() { + local gum_lib_dir="" + + gum_lib_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + printf '%s/../../../config/gum-checksums.tsv\n' "${gum_lib_dir}" +} + +get_pinned_gum_version() { + local checksums_path="" + local release_version="" + + checksums_path="$(get_gum_checksums_path)" + if [ ! -f "${checksums_path}" ]; then + return 1 + fi + + release_version="$( + awk -F '\t' ' + /^[[:space:]]*#/ { next } + NF < 3 { next } + { + print $1 + exit + } + ' "${checksums_path}" + )" + if [ -z "${release_version}" ]; then + return 1 + fi + + printf '%s\n' "${release_version}" +} + +get_pinned_gum_asset_checksum() { + local release_version="${1}" + local asset_name="${2}" + local checksums_path="" + local expected_checksum="" + + checksums_path="$(get_gum_checksums_path)" + if [ ! -f "${checksums_path}" ]; then + return 1 + fi + + expected_checksum="$( + awk -F '\t' -v release_version="${release_version}" -v asset_name="${asset_name}" ' + /^[[:space:]]*#/ { next } + NF < 3 { next } + $1 == release_version && $2 == asset_name { + print $3 + exit + } + ' "${checksums_path}" + )" + if [ -z "${expected_checksum}" ]; then + return 1 + fi + + printf '%s\n' "${expected_checksum}" +} + +sha256_verification_available() { + command_exists sha256sum || + command_exists shasum || + command_exists openssl || + command_exists certutil +} + +compute_file_sha256() { + local file_path="${1}" + local hash_input_path="${file_path}" + + if command_exists sha256sum; then + sha256sum "${file_path}" | awk '{print tolower($1)}' + return $? + fi + + if command_exists shasum; then + shasum -a 256 "${file_path}" | awk '{print tolower($1)}' + return $? + fi + + if command_exists openssl; then + openssl dgst -sha256 -r "${file_path}" | awk '{print tolower($1)}' + return $? + fi + + if command_exists certutil; then + if command_exists cygpath; then + hash_input_path="$(cygpath -w "${file_path}" 2>/dev/null || printf '%s' "${file_path}")" + fi + + certutil -hashfile "${hash_input_path}" SHA256 2>/dev/null | + awk 'NR == 2 { gsub(/ /, "", $0); print tolower($0); exit }' + return $? + fi + + return 1 +} + +verify_file_sha256() { + local file_path="${1}" + local expected_checksum="${2}" + local actual_checksum="" + + actual_checksum="$(compute_file_sha256 "${file_path}" || true)" + if [ -z "${actual_checksum}" ]; then + return 1 + fi + + if [ "${actual_checksum}" != "$(printf '%s' "${expected_checksum}" | tr '[:upper:]' '[:lower:]')" ]; then + return 1 + fi + + return 0 +} + get_gum_asset_candidates() { local release_version="${1}" local gum_os="${2}" @@ -60,23 +177,3 @@ find_gum_binary() { return 1 } - -fetch_latest_gum_release_version() { - local api_payload="" - local tag_name="" - - api_payload="$(curl -fsSL "https://api.github.com/repos/charmbracelet/gum/releases/latest")" || return 1 - - if command_exists jq; then - tag_name="$(printf '%s' "${api_payload}" | jq -r '.tag_name // empty')" - else - tag_name="$(printf '%s' "${api_payload}" | sed -n 's/.*"tag_name":[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1)" - fi - - tag_name="${tag_name#v}" - if [ -z "${tag_name}" ]; then - return 1 - fi - - printf '%s\n' "${tag_name}" -} diff --git a/scripts/easy-docker/lib/install/gum/ensure.sh b/scripts/easy-docker/lib/install/gum/ensure.sh index b7587a26..e69e2feb 100755 --- a/scripts/easy-docker/lib/install/gum/ensure.sh +++ b/scripts/easy-docker/lib/install/gum/ensure.sh @@ -46,7 +46,7 @@ ensure_gum() { fi if should_use_github_fallback; then - echo "Trying GitHub release fallback..." + echo "Trying pinned GitHub release fallback..." if install_gum_from_github_release; then hash -r fi diff --git a/scripts/easy-docker/lib/install/gum/github_release.sh b/scripts/easy-docker/lib/install/gum/github_release.sh index d157afd0..8ad33d09 100755 --- a/scripts/easy-docker/lib/install/gum/github_release.sh +++ b/scripts/easy-docker/lib/install/gum/github_release.sh @@ -10,6 +10,7 @@ cleanup_gum_tmp_dir() { install_gum_from_github_release() { local release_version="" + local checksums_path="" local asset_name="" local asset_path="" local download_url="" @@ -20,6 +21,7 @@ install_gum_from_github_release() { local target_binary_name="gum" local gum_os="" local gum_arch="" + local expected_checksum="" if ! command_exists curl; then echo "curl is required for the GitHub fallback." @@ -31,9 +33,20 @@ install_gum_from_github_release() { return 1 fi - release_version="$(fetch_latest_gum_release_version || true)" + release_version="$(get_pinned_gum_version || true)" if [ -z "${release_version}" ]; then - echo "Could not determine latest gum release version." + echo "Could not determine the pinned gum release version." + return 1 + fi + + checksums_path="$(get_gum_checksums_path)" + if [ ! -f "${checksums_path}" ]; then + echo "Pinned gum checksum file is missing: ${checksums_path}" + return 1 + fi + + if ! sha256_verification_available; then + echo "A SHA256 verification tool is required for the GitHub fallback." return 1 fi @@ -45,6 +58,11 @@ install_gum_from_github_release() { extract_dir="${tmp_dir}/extract" while IFS= read -r asset_name; do + expected_checksum="$(get_pinned_gum_asset_checksum "${release_version}" "${asset_name}" || true)" + if [ -z "${expected_checksum}" ]; then + continue + fi + asset_path="${tmp_dir}/${asset_name}" download_url="https://github.com/charmbracelet/gum/releases/download/v${release_version}/${asset_name}" @@ -52,6 +70,11 @@ install_gum_from_github_release() { continue fi + if ! verify_file_sha256 "${asset_path}" "${expected_checksum}"; then + echo "Checksum verification failed for ${asset_name}." + continue + fi + rm -rf "${extract_dir}" mkdir -p "${extract_dir}" @@ -67,7 +90,7 @@ install_gum_from_github_release() { if [ -z "${gum_binary_path}" ]; then cleanup_gum_tmp_dir "${tmp_dir}" - echo "No compatible gum binary was found in GitHub release assets." + echo "No compatible, verified gum binary was found in the pinned GitHub release assets." return 1 fi