@@ -51,11 +51,23 @@ install() {
5151 exit 1
5252 fi
5353
54+ _install_tmp_dir=" $( mktemp -d) "
55+ trap ' rm -rf "$_install_tmp_dir"' EXIT
56+ local sums_file=" ${_install_tmp_dir} /SHA256SUMS"
57+
58+ local sums_url=" https://github.com/gruntwork-io/terragrunt/releases/download/v${version} /SHA256SUMS"
59+ if ! curl -sL -o " ${sums_file} " " ${sums_url} " ; then
60+ echo " Warning: Failed to download SHA256SUMS"
61+ rm -f " ${bin_path} "
62+ exit 1
63+ fi
64+
65+ # Checksum verification
5466 if [[ " ${ASDF_TERRAGRUNT_SKIP_CHECKSUM:- " false" } " != " true" ]]; then
5567 local expected_checksum
5668 expected_checksum=$( _sha256 " ${bin_path} " | awk ' {print $1}' )
5769 local checksum
58- checksum=" $( curl -sL " https://github.com/gruntwork-io/terragrunt/releases/download/v ${version} /SHA256SUMS " | grep " terragrunt_${platform} _${arch} $" | awk ' {print $1}' ) "
70+ checksum=" $( grep " terragrunt_${platform} _${arch} $" " ${sums_file} " | awk ' {print $1}' ) "
5971
6072 if [[ " ${expected_checksum} " != " ${checksum} " ]]; then
6173 echo " Checksum for terragrunt did not match"
@@ -66,6 +78,27 @@ install() {
6678 fi
6779 fi
6880
81+ # Signature verification
82+ if [[ " ${ASDF_TERRAGRUNT_SKIP_SIGNATURE:- " false" } " == " true" ]]; then
83+ echo " Skipping signature verification due to ASDF_TERRAGRUNT_SKIP_SIGNATURE=true"
84+
85+ chmod +x " ${bin_path} "
86+ return
87+ fi
88+
89+ if ! _supports_signature_verification " ${version} " ; then
90+ echo " Skipping signature verification: not available for versions older than v${MIN_SIGNED_VERSION} "
91+
92+ chmod +x " ${bin_path} "
93+ return
94+ fi
95+
96+ if ! verify_signature " ${version} " " ${sums_file} " ; then
97+ echo " Signature verification failed. Set ASDF_TERRAGRUNT_SKIP_SIGNATURE=true to skip."
98+ rm -f " ${bin_path} "
99+ exit 1
100+ fi
101+
69102 chmod +x " ${bin_path} "
70103}
71104
@@ -80,4 +113,179 @@ _sha256() {
80113 fi
81114}
82115
116+ # Compare two semantic versions. Returns 0 if $1 >= $2, 1 otherwise.
117+ _version_gte () {
118+ local version=$1
119+ local min_version=$2
120+
121+ # Use sort -V if available, otherwise fall back to manual comparison
122+ if echo | sort -V > /dev/null 2>&1 ; then
123+ local sorted_first
124+ sorted_first=$( printf ' %s\n%s' " $min_version " " $version " | sort -V | head -n1)
125+ [[ " $sorted_first " == " $min_version " ]]
126+ else
127+ # Manual version comparison for systems without sort -V (e.g., macOS)
128+ local i
129+
130+ IFS=' .' read -ra v1 <<< " $version"
131+ IFS=' .' read -ra v2 <<< " $min_version"
132+
133+ for (( i = 0 ; i < ${# v2[@]} ; i++ )) ; do
134+ local n1=${v1[i]:- 0}
135+ local n2=${v2[i]:- 0}
136+ if (( n1 > n2 )) ; then
137+ return 0
138+ elif (( n1 < n2 )) ; then
139+ return 1
140+ fi
141+ done
142+
143+ return 0
144+ fi
145+ }
146+
147+ # Minimum version that has signed release assets
148+ MIN_SIGNED_VERSION=" 0.98.0"
149+
150+ _supports_signature_verification () {
151+ local version=$1
152+ _version_gte " $version " " $MIN_SIGNED_VERSION "
153+ }
154+
155+ _gpg_available () {
156+ command -v gpg > /dev/null 2>&1
157+ }
158+
159+ _cosign_available () {
160+ command -v cosign > /dev/null 2>&1
161+ }
162+
163+ _import_gpg_key () {
164+ local key_url=" https://gruntwork.io/.well-known/pgp-key.txt"
165+
166+ # Check if we already have the Gruntwork key imported
167+ if gpg --list-keys " Gruntwork" > /dev/null 2>&1 ; then
168+ return 0
169+ fi
170+
171+ echo " Importing Gruntwork GPG public key..."
172+ if ! curl -sL " ${key_url} " | gpg --import 2> /dev/null; then
173+ echo " Warning: Failed to import Gruntwork GPG key from ${key_url} "
174+ return 1
175+ fi
176+ return 0
177+ }
178+
179+ _verify_gpg_signature () {
180+ local version=$1
181+ local sums_file=$2
182+
183+ local sig_url=" https://github.com/gruntwork-io/terragrunt/releases/download/v${version} /SHA256SUMS.gpgsig"
184+ local sig_file=" ${sums_file} .gpgsig"
185+
186+ echo " Downloading GPG signature file..."
187+
188+ if ! curl -sL -o " ${sig_file} " " ${sig_url} " ; then
189+ echo " Warning: Failed to download SHA256SUMS.gpgsig"
190+ return 1
191+ fi
192+
193+ if ! _import_gpg_key; then
194+ return 1
195+ fi
196+
197+ echo " Verifying GPG signature..."
198+ if gpg --verify " ${sig_file} " " ${sums_file} " 2> /dev/null; then
199+ echo " GPG signature verification successful"
200+ return 0
201+ else
202+ echo " GPG signature verification failed!"
203+ return 1
204+ fi
205+ }
206+
207+ _verify_cosign_signature () {
208+ local version=$1
209+ local sums_file=$2
210+
211+ local sig_url=" https://github.com/gruntwork-io/terragrunt/releases/download/v${version} /SHA256SUMS.sig"
212+ local cert_url=" https://github.com/gruntwork-io/terragrunt/releases/download/v${version} /SHA256SUMS.pem"
213+
214+ local sig_file=" ${sums_file} .sig"
215+ local cert_file=" ${sums_file} .pem"
216+
217+ echo " Downloading Cosign signature files..."
218+
219+ if ! curl -sL -o " ${sig_file} " " ${sig_url} " ; then
220+ echo " Warning: Failed to download SHA256SUMS.sig"
221+ return 1
222+ fi
223+
224+ if ! curl -sL -o " ${cert_file} " " ${cert_url} " ; then
225+ echo " Warning: Failed to download SHA256SUMS.pem"
226+ return 1
227+ fi
228+
229+ echo " Verifying Cosign signature..."
230+ if cosign verify-blob " ${sums_file} " \
231+ --signature " ${sig_file} " \
232+ --certificate " ${cert_file} " \
233+ --certificate-oidc-issuer " https://token.actions.githubusercontent.com" \
234+ --certificate-identity-regexp " github.com/gruntwork-io/terragrunt" 2> /dev/null; then
235+ echo " Cosign signature verification successful"
236+ return 0
237+ else
238+ echo " Cosign signature verification failed!"
239+ return 1
240+ fi
241+ }
242+
243+ verify_signature () {
244+ local version=$1
245+ local sums_file=$2
246+
247+ local method=" ${ASDF_TERRAGRUNT_SIGNATURE_METHOD:- auto} "
248+
249+ case " ${method} " in
250+ gpg)
251+ if ! _gpg_available; then
252+ echo " Error: GPG verification requested but gpg is not installed"
253+ return 1
254+ fi
255+ _verify_gpg_signature " ${version} " " ${sums_file} "
256+ return $?
257+ ;;
258+ cosign)
259+ if ! _cosign_available; then
260+ echo " Error: Cosign verification requested but cosign is not installed"
261+ return 1
262+ fi
263+ _verify_cosign_signature " ${version} " " ${sums_file} "
264+ return $?
265+ ;;
266+ auto)
267+ # Try GPG first, then Cosign
268+ if _gpg_available; then
269+ echo " Using GPG for signature verification"
270+ _verify_gpg_signature " ${version} " " ${sums_file} "
271+ return $?
272+ fi
273+
274+ if _cosign_available; then
275+ echo " Using Cosign for signature verification"
276+ _verify_cosign_signature " ${version} " " ${sums_file} "
277+ return $?
278+ fi
279+
280+ echo " Warning: Neither gpg nor cosign is available for signature verification"
281+ echo " Install gpg or cosign to enable signature verification, or set ASDF_TERRAGRUNT_SKIP_SIGNATURE=true to skip"
282+ return 1
283+ ;;
284+ * )
285+ echo " Error: Unknown signature method '${method} '. Use 'gpg', 'cosign', or 'auto'"
286+ return 1
287+ ;;
288+ esac
289+ }
290+
83291install " ${ASDF_INSTALL_TYPE} " " ${ASDF_INSTALL_VERSION} " " ${ASDF_INSTALL_PATH} "
0 commit comments