Skip to content

Commit 882ba69

Browse files
committed
Verify binary checksum against GH release checksums
1 parent f76e49f commit 882ba69

1 file changed

Lines changed: 146 additions & 3 deletions

File tree

hooks/command

Lines changed: 146 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,23 @@ need_cmd() {
6666
fi
6767
}
6868

69+
# Detect platform-appropriate SHA256 command
70+
# Returns: Sets RETVAL to command string, returns 0 on success, 1 if no command found
71+
get_sha256_cmd() {
72+
if check_cmd sha256sum; then
73+
RETVAL="sha256sum"
74+
return 0
75+
elif check_cmd shasum; then
76+
RETVAL="shasum -a 256"
77+
return 0
78+
elif check_cmd sha256; then
79+
RETVAL="sha256"
80+
return 0
81+
else
82+
return 1
83+
fi
84+
}
85+
6986
# This wraps curl or wget.
7087
# Try curl first, if not installed, use wget instead.
7188
downloader() {
@@ -111,6 +128,92 @@ download_with_retry() {
111128
return 1
112129
}
113130

131+
# Download checksums.txt from GitHub release
132+
# Parameters: $1 = version (e.g., "v1.8.0"), $2 = destination file path
133+
# Returns: 0 on success, 1 on failure
134+
download_checksums() {
135+
local _version="$1"
136+
local _dest="$2"
137+
local _repo="https://github.com/buildkite-plugins/monorepo-diff-buildkite-plugin"
138+
local _url="${_repo}/releases/download/${_version}/checksums.txt"
139+
140+
if ! downloader "$_url" "$_dest"; then
141+
return 1
142+
fi
143+
144+
return 0
145+
}
146+
147+
# Verify binary against checksums.txt
148+
# Parameters: $1 = path to binary file, $2 = version (e.g., "v1.8.0"), $3 = architecture (e.g., "darwin_amd64")
149+
# Returns: 0 if valid, 1 if invalid
150+
verify_checksum() {
151+
local _binary_path="$1"
152+
local _version="$2"
153+
local _arch="$3"
154+
local _verify_enabled="${BUILDKITE_PLUGIN_MONOREPO_DIFF_VERIFY_CHECKSUM:-false}"
155+
156+
# Check if verification is disabled
157+
if [[ "$_verify_enabled" == "false" ]]; then
158+
return 0
159+
fi
160+
161+
# Detect SHA256 command
162+
if ! get_sha256_cmd; then
163+
say "Warning: No SHA256 command found (sha256sum, shasum, or sha256), skipping verification"
164+
return 0
165+
fi
166+
local _sha256_cmd="$RETVAL"
167+
168+
# Download checksums.txt to temporary file
169+
local _checksums_file
170+
_checksums_file=$(mktemp)
171+
172+
if ! download_checksums "$_version" "$_checksums_file"; then
173+
say "Warning: Could not download checksums.txt, skipping verification"
174+
rm -f "$_checksums_file"
175+
return 0
176+
fi
177+
178+
# Get expected checksum from checksums.txt
179+
local _binary_name="monorepo-diff-buildkite-plugin_${_arch}"
180+
local _expected_checksum
181+
_expected_checksum=$(grep "${_binary_name}" "$_checksums_file" | awk '{print $1}')
182+
183+
if [[ -z "$_expected_checksum" ]]; then
184+
say "Warning: Checksum not found in checksums.txt for ${_binary_name}, skipping verification"
185+
rm -f "$_checksums_file"
186+
return 0
187+
fi
188+
189+
# Calculate actual checksum
190+
local _actual_checksum
191+
if [[ "$_sha256_cmd" == "sha256" ]]; then
192+
# BSD format: SHA256 (file) = hash
193+
_actual_checksum=$($_sha256_cmd "$_binary_path" | awk '{print $4}')
194+
else
195+
# GNU/shasum format: hash file
196+
_actual_checksum=$($_sha256_cmd "$_binary_path" | awk '{print $1}')
197+
fi
198+
199+
# Clean up temporary file
200+
rm -f "$_checksums_file"
201+
202+
# Compare checksums
203+
if [[ "$_actual_checksum" != "$_expected_checksum" ]]; then
204+
red=$(tput setaf 1 2>/dev/null || echo '')
205+
reset=$(tput sgr0 2>/dev/null || echo '')
206+
say "${red}ERROR${reset}: Checksum verification failed for $_binary_path" >&2
207+
say "Expected: $_expected_checksum" >&2
208+
say "Actual: $_actual_checksum" >&2
209+
say "This may indicate a corrupted download or a security issue." >&2
210+
return 1
211+
fi
212+
213+
say "Checksum verification passed"
214+
return 0
215+
}
216+
114217
get_latest_version() {
115218
local _repo="https://api.github.com/repos/buildkite-plugins/monorepo-diff-buildkite-plugin"
116219
local _version=""
@@ -121,7 +224,7 @@ get_latest_version() {
121224
_version=$(wget -qO- "${_repo}/releases/latest" | grep -oE '"tag_name": "v[0-9]+\.[0-9]+\.[0-9]+"' | cut -d'"' -f4)
122225
fi
123226

124-
if [[ "$_version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then
227+
if [[ "$_version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
125228
echo "${_version}"
126229
fi
127230
}
@@ -140,7 +243,7 @@ get_version() {
140243
_version=${BASH_REMATCH[1]}
141244
fi
142245

143-
if [[ "$_version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then
246+
if [[ "$_version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
144247
true
145248
else
146249
_version=""
@@ -160,17 +263,27 @@ download_binary_and_run() {
160263
local _binary_version=""
161264
local test_mode="${BUILDKITE_PLUGIN_MONOREPO_DIFF_BUILDKITE_PLUGIN_TEST_MODE:-false}"
162265
local _url=""
266+
local _recovery_mode=false
163267

164268
if check_cmd "${executable}"; then
165269
_binary_version=$(get_binary_version)
166270
if [[ -z "$_binary_version" ]]; then
167271
say "Warning: Could not determine binary version, will download fresh copy"
272+
else
273+
# Before reusing cached binary, verify its checksum
274+
if ! verify_checksum "${executable}" "${_binary_version}" "${_arch}"; then
275+
say "Cached binary failed checksum verification, attempting recovery..."
276+
rm -f "${executable}"
277+
rm -f "${executable_version_file}"
278+
_binary_version=""
279+
_recovery_mode=true
280+
fi
168281
fi
169282
fi
170283

171284
if [[ "$test_mode" == "true" ]]; then
172285
true
173-
elif [[ "$_specified_version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then
286+
elif [[ "$_specified_version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
174287
if [[ -z "$_binary_version" ]] || [[ "$_binary_version" != "$_specified_version" ]]; then
175288
_url=${_repo}/releases/download/${_specified_version}/monorepo-diff-buildkite-plugin_${_arch}
176289
_binary_version="${_specified_version}"
@@ -194,6 +307,21 @@ download_binary_and_run() {
194307
exit 1
195308
fi
196309
echo "${_binary_version}" > "${executable_version_file}"
310+
311+
# After successful download, verify checksum
312+
if ! verify_checksum "${executable}" "${_binary_version}" "${_arch}"; then
313+
rm -f "${executable}"
314+
if [[ "$_recovery_mode" == "true" ]]; then
315+
err "Recovery download also failed checksum verification. This may indicate a problem with the release artifacts."
316+
else
317+
err "Downloaded binary failed checksum verification and was deleted"
318+
fi
319+
fi
320+
321+
# If this was a recovery download, verify it succeeded
322+
if [[ "$_recovery_mode" == "true" ]]; then
323+
say "Binary recovery successful"
324+
fi
197325
fi
198326

199327
chmod +x "${executable}"
@@ -211,6 +339,21 @@ run_preinstalled_binary() {
211339
fi
212340
fi
213341

342+
# Best-effort verification for preinstalled binaries (non-blocking)
343+
# Try to detect version and verify, but don't fail if we can't
344+
local _binary_version
345+
_binary_version=$(get_binary_version)
346+
if [[ -n "$_binary_version" ]]; then
347+
get_architecture || true
348+
local _arch="$RETVAL"
349+
if [[ -n "$_arch" ]]; then
350+
# Attempt verification but don't fail if it doesn't work
351+
if ! verify_checksum "${_executable}" "${_binary_version}" "${_arch}" 2>/dev/null; then
352+
say "Warning: Could not verify checksum for preinstalled binary (version: ${_binary_version})"
353+
fi
354+
fi
355+
fi
356+
214357
${_executable} "$@"
215358
}
216359

0 commit comments

Comments
 (0)