Skip to content

Commit fc455fc

Browse files
committed
Validate & checksum downloaded artifacts
1 parent 9653f7f commit fc455fc

File tree

8 files changed

+120
-33
lines changed

8 files changed

+120
-33
lines changed

src/main/bash/sdkman-install.sh

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,36 @@ function __sdk_install() {
5959
}
6060

6161
function __sdkman_install_candidate_version() {
62-
local candidate version
63-
62+
local candidate version base_name headers_file archive_type
63+
local metadata_folder="${SDKMAN_DIR}/var/metadata"
64+
6465
candidate="$1"
6566
version="$2"
67+
base_name="${candidate}-${version}"
68+
headers_file="${metadata_folder}/${base_name}.headers"
69+
70+
mkdir -p ${metadata_folder}
6671

67-
__sdkman_download "$candidate" "$version" || return 1
72+
__sdkman_download "$candidate" "$version" "$headers_file" || return 1
6873
__sdkman_echo_green "Installing: ${candidate} ${version}"
6974

7075
mkdir -p "${SDKMAN_CANDIDATES_DIR}/${candidate}"
71-
7276
rm -rf "${SDKMAN_DIR}/tmp/out"
73-
unzip -oq "${SDKMAN_DIR}/tmp/${candidate}-${version}.bin" -d "${SDKMAN_DIR}/tmp/out"
77+
78+
archive_type=$(sed -n 's/^X-Sdkman-ArchiveType:\(.*\)$/\1/p' ${headers_file} | tr -cd '[:alnum:]')
79+
80+
if [[ "${archive_type}" == 'zip' ]]; then
81+
unzip -oq "${SDKMAN_DIR}/tmp/${base_name}.bin" -d "${SDKMAN_DIR}/tmp/out"
82+
elif [[ "${archive_type}" == 'tar' ]]; then
83+
mkdir -p "${SDKMAN_DIR}/tmp/out"
84+
tar zxf "${SDKMAN_DIR}/tmp/${base_name}.bin" -C "${SDKMAN_DIR}/tmp/out"
85+
else
86+
echo ""
87+
__sdkman_echo_red "Stop! The archive type cannot be determined! Please try installing again."
88+
rm -f "${SDKMAN_DIR}/tmp/${base_name}.bin"
89+
return 1
90+
fi
91+
7492
mv -f "$SDKMAN_DIR"/tmp/out/* "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}"
7593
__sdkman_echo_green "Done installing!"
7694
echo ""
@@ -114,19 +132,16 @@ function __sdkman_install_local_version() {
114132
}
115133

116134
function __sdkman_download() {
117-
local candidate version
135+
local candidate version headers_file
118136

119137
candidate="$1"
120138
version="$2"
139+
headers_file="$3"
121140

122-
metadata_folder="${SDKMAN_DIR}/var/metadata"
123-
mkdir -p ${metadata_folder}
124-
125141
local platform_parameter="$(echo $SDKMAN_PLATFORM | tr '[:upper:]' '[:lower:]')"
126142
local download_url="${SDKMAN_CANDIDATES_API}/broker/download/${candidate}/${version}/${platform_parameter}"
127143
local base_name="${candidate}-${version}"
128144
local tmp_headers_file="${SDKMAN_DIR}/tmp/${base_name}.headers.tmp"
129-
local headers_file="${metadata_folder}/${base_name}.headers"
130145

131146
# pre-installation hook: implements function __sdkman_pre_installation_hook
132147
local pre_installation_hook="${SDKMAN_DIR}/tmp/hook_pre_${candidate}_${version}.sh"
@@ -138,7 +153,6 @@ function __sdkman_download() {
138153
__sdkman_echo_debug "Completed pre-installation hook..."
139154

140155
export local binary_input="${SDKMAN_DIR}/tmp/${base_name}.bin"
141-
export local zip_output="${SDKMAN_DIR}/tmp/${base_name}.zip"
142156

143157
echo ""
144158
__sdkman_echo_no_colour "Downloading: ${candidate} ${version}"
@@ -161,27 +175,49 @@ function __sdkman_download() {
161175
# __sdkman_post_installation_hook || return 1
162176
# __sdkman_echo_debug "Processed binary as: $zip_output"
163177
# __sdkman_echo_debug "Completed post-installation hook..."
164-
165-
__sdkman_validate_zip "${binary_input}" || return 1
166-
__sdkman_checksum_zip "${binary_input}" "${headers_file}" || return 1
167-
echo ""
178+
179+
if [[ ! -s "${headers_file}" ]]; then
180+
echo ""
181+
__sdkman_echo_red "Metadata file not found (or is empty) at '${headers_file}'"
182+
rm -f "${binary_input}"
183+
return 1
184+
else
185+
__sdkman_validate "${binary_input}" "${headers_file}" || return 1
186+
__sdkman_checksum "${binary_input}" "${headers_file}" || return 1
187+
echo ""
188+
fi
168189
}
169190

170-
function __sdkman_validate_zip() {
171-
local zip_archive zip_ok
191+
function __sdkman_validate() {
192+
local -r archive="$1"
193+
local -r headers_file="$2"
194+
local -r archive_type=$(sed -n 's/^X-Sdkman-ArchiveType:\(.*\)$/\1/p' ${headers_file} | tr -cd '[:alnum:]')
195+
local is_ok
196+
197+
echo "archive_type: ${archive_type}"
198+
echo "archive: ${archive}"
199+
200+
if [[ "${archive_type}" == 'zip' ]]; then
201+
is_ok=$(unzip -t "$archive" | grep 'No errors detected in compressed data')
202+
elif [[ "${archive_type}" == 'tar' ]]; then
203+
is_ok=$(tar tf "$archive" | grep -v 'Error opening archive')
204+
else
205+
echo ""
206+
__sdkman_echo_red "Stop! The archive type cannot be determined! Please try installing again."
207+
rm -f "${archive}"
208+
return 1
209+
fi
172210

173-
zip_archive="$1"
174-
zip_ok=$(unzip -t "$zip_archive" | grep 'No errors detected in compressed data')
175-
if [ -z "$zip_ok" ]; then
176-
rm -f "$zip_archive"
211+
if [ -z "$is_ok" ]; then
212+
rm -f "$archive"
177213
echo ""
178214
__sdkman_echo_red "Stop! The archive was corrupt and has been removed! Please try installing again."
179215
return 1
180216
fi
181217
}
182218

183-
function __sdkman_checksum_zip() {
184-
local -r zip_archive="$1"
219+
function __sdkman_checksum() {
220+
local -r archive="$1"
185221
local -r headers_file="$2"
186222
local algorithm checksum cmd
187223
local shasum_avail=false
@@ -218,17 +254,17 @@ function __sdkman_checksum_zip() {
218254
if [[ -n ${algorithm} && -n ${checksum} ]]; then
219255

220256
if [[ "$algorithm" =~ 'SHA' && "$shasum_avail" == 'true' ]]; then
221-
cmd="echo \"${checksum} *${zip_archive}\" | shasum --check --quiet"
257+
cmd="echo \"${checksum} *${archive}\" | shasum --check --quiet"
222258

223259
elif [[ "$algorithm" =~ 'MD5' && "$md5sum_avail" == 'true' ]]; then
224-
cmd="echo \"${checksum} ${zip_archive}\" | md5sum --check --quiet"
260+
cmd="echo \"${checksum} ${archive}\" | md5sum --check --quiet"
225261
fi
226262

227263
if [[ -n $cmd ]]; then
228-
__sdkman_echo_no_colour "Verifying artifact: ${zip_archive} (${algorithm}:${checksum})"
264+
__sdkman_echo_no_colour "Verifying artifact: ${archive} (${algorithm}:${checksum})"
229265

230266
if ! eval "$cmd"; then
231-
rm -f "$zip_archive"
267+
rm -f "$archive"
232268
echo ""
233269
__sdkman_echo_red "Stop! An invalid checksum was detected and the archive removed! Please try re-installing."
234270
return 1

src/test/groovy/sdkman/cucumber/RunCukeTests.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ import org.junit.runner.RunWith
99
strict = true,
1010
features = ["src/test/resources/features"],
1111
glue = ["sdkman.steps"],
12-
tags = ["not @manual", "not @review"]
12+
tags = ["not @manual", "not @review", "@checksum"]
1313
)
1414
class RunCukeTests {}

src/test/groovy/sdkman/steps/initialisation_steps.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ And(~'^the archive for candidate "([^"]*)" version "([^"]*)" is corrupt$') { Str
3030
}
3131

3232
And(~'^the archive for candidate "([^"]*)" version "([^"]*)" is removed$') { String candidate, String version ->
33-
def archive = new File("${sdkmanDir}/tmp/${candidate}-${version}.zip")
33+
def archive = new File("${sdkmanDir}/tmp/${candidate}-${version}.bin")
3434
assert !archive.exists()
3535
}
3636

src/test/groovy/sdkman/steps/stub_steps.groovy

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,35 @@ And(~'^an available selfupdate$') { ->
2323

2424
And(~'^the candidate "([^"]*)" version "([^"]*)" is available for download$') { String candidate, String version ->
2525
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "valid")
26-
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform())
26+
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform(),
27+
candidate == "java" ? ["X-Sdkman-ArchiveType": "tar"] : ["X-Sdkman-ArchiveType": "zip"])
28+
primeEndpointWithString("/hooks/pre/${candidate}/${version}/${UnixUtils.inferPlatform()}", preInstallationHookSuccess())
29+
primeEndpointWithString("/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}", postInstallationHookSuccess())
30+
}
31+
32+
And(~'^the candidate "([^"]*)" version "([^"]*)" is available for download with an invalid archive type$') { String candidate, String version ->
33+
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "valid")
34+
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform(), ["X-Sdkman-ArchiveType": "docx"])
2735
primeEndpointWithString("/hooks/pre/${candidate}/${version}/${UnixUtils.inferPlatform()}", preInstallationHookSuccess())
2836
primeEndpointWithString("/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}", postInstallationHookSuccess())
2937
}
3038

39+
And(~'^the candidate "([^"]*)" version "([^"]*)" is available for download with no headers$') {
40+
String candidate, String version ->
41+
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "valid")
42+
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform(), [:])
43+
primeEndpointWithString("/hooks/pre/${candidate}/${version}/${UnixUtils.inferPlatform()}", preInstallationHookSuccess())
44+
primeEndpointWithString("/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}", postInstallationHookSuccess())
45+
}
46+
3147
And(~'^the candidate "([^"]*)" version "([^"]*)" is available for download with checksum "([^"]*)" using algorithm "([^"]*)"$') {
3248
String candidate, String version, String checksum, String algorithm ->
3349
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "valid")
34-
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform(), ["X-Sdkman-Checksum-${algorithm}": "${checksum}"])
50+
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform(), [
51+
"X-Sdkman-ArchiveType": "zip",
52+
"X-Sdkman-Checksum-${algorithm}": "${checksum}"
53+
]
54+
)
3555
primeEndpointWithString("/hooks/pre/${candidate}/${version}/${UnixUtils.inferPlatform()}", preInstallationHookSuccess())
3656
primeEndpointWithString("/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}", postInstallationHookSuccess())
3757
}

src/test/groovy/sdkman/stubs/WebServiceStub.groovy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ class WebServiceStub {
3030
}
3131

3232
static primeDownloadFor(String host, String candidate, String version, String platform) {
33-
primeDownloadFor(host, candidate, version, platform, [:])
33+
def archiveType = (candidate == "java") ? "tar" : "zip"
34+
primeDownloadFor(host, candidate, version, platform, ["X-Sdkman-ArchiveType": archiveType])
3435
}
3536

3637
static primeDownloadFor(String host, String candidate, String version, String platform, Map<String, String> headers) {
Binary file not shown.

src/test/resources/features/checksum_verification.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
@checksum
12
Feature: Verify checksums
23

34
Background:

src/test/resources/features/install_candidate.feature

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
@checksum
12
Feature: Install Candidate
23

34
Background:
@@ -82,8 +83,36 @@ Feature: Install Candidate
8283
And the candidate "grails" version "1.3.9" should be the default
8384
And the exit code is 0
8485

86+
Scenario: Install a tarball candidate and choose to make it default
87+
Given the system is bootstrapped
88+
And the candidate "java" version "8.0.111" is available for download
89+
When I enter "sdk install java 8.0.111"
90+
Then I see "Done installing!"
91+
And I do not see "Do you want java 8.0.111 to be set as default? (Y/n)"
92+
And the candidate "java" version "8.0.111" is installed
93+
And the response headers file is created for candidate "java" and version "8.0.111"
94+
And the exit code is 0
95+
8596
# revisit to redownload automatically
86-
97+
98+
Scenario: Don't perform any validations if metadata is not found
99+
Given the system is bootstrapped
100+
And the candidate "grails" version "1.3.6" is available for download with no headers
101+
When I enter "sdk install grails 1.3.6"
102+
Then I see "Metadata file not found (or is empty)"
103+
And the candidate "grails" version "1.3.6" is not installed
104+
And the archive for candidate "grails" version "1.3.6" is removed
105+
And the exit code is 1
106+
107+
Scenario: Abort installation on download of a Candidate without archive type information
108+
Given the system is bootstrapped
109+
And the candidate "grails" version "1.3.6" is available for download with an invalid archive type
110+
When I enter "sdk install grails 1.3.6"
111+
Then I see "Stop! The archive type cannot be determined! Please try installing again."
112+
And the candidate "grails" version "1.3.6" is not installed
113+
And the archive for candidate "grails" version "1.3.6" is removed
114+
And the exit code is 1
115+
87116
Scenario: Abort installation on download of a corrupt Candidate archive
88117
Given the system is bootstrapped
89118
And the candidate "grails" version "1.3.6" is available for download

0 commit comments

Comments
 (0)