Skip to content

Commit c968e6f

Browse files
committed
fix(installer): add SHA256 checksum verification
- Generate SHA256 checksums for all release archives during CI build - Publish .sha256 files alongside release artifacts - Update install.sh to download and verify checksum before extraction - Update install.ps1 to download and verify checksum before extraction - Abort installation if checksum is missing or doesn't match Closes #87
1 parent 11d96e7 commit c968e6f

3 files changed

Lines changed: 136 additions & 2 deletions

File tree

.github/workflows/release.yml

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,38 @@ jobs:
209209
cd ..
210210
shell: bash
211211

212+
- name: Generate SHA256 checksum (Unix)
213+
if: matrix.goos != 'windows'
214+
run: |
215+
cd dist
216+
ARCHIVE_NAME="dtvem-${{ github.event.inputs.version }}-${{ matrix.asset_name_suffix }}.${{ matrix.archive_ext }}"
217+
if command -v sha256sum &> /dev/null; then
218+
sha256sum "$ARCHIVE_NAME" > "$ARCHIVE_NAME.sha256"
219+
else
220+
shasum -a 256 "$ARCHIVE_NAME" > "$ARCHIVE_NAME.sha256"
221+
fi
222+
echo "Generated checksum:"
223+
cat "$ARCHIVE_NAME.sha256"
224+
shell: bash
225+
226+
- name: Generate SHA256 checksum (Windows)
227+
if: matrix.goos == 'windows'
228+
run: |
229+
$archiveName = "dtvem-${{ github.event.inputs.version }}-${{ matrix.asset_name_suffix }}.${{ matrix.archive_ext }}"
230+
$archivePath = "dist/$archiveName"
231+
$hash = (Get-FileHash -Path $archivePath -Algorithm SHA256).Hash.ToLower()
232+
"$hash $archiveName" | Out-File -FilePath "dist/$archiveName.sha256" -Encoding ASCII -NoNewline
233+
Write-Host "Generated checksum:"
234+
Get-Content "dist/$archiveName.sha256"
235+
shell: pwsh
236+
212237
- name: Upload build artifacts
213238
uses: actions/upload-artifact@v4
214239
with:
215240
name: build-${{ matrix.asset_name_suffix }}
216-
path: dist/dtvem-${{ github.event.inputs.version }}-${{ matrix.asset_name_suffix }}.${{ matrix.archive_ext }}
241+
path: |
242+
dist/dtvem-${{ github.event.inputs.version }}-${{ matrix.asset_name_suffix }}.${{ matrix.archive_ext }}
243+
dist/dtvem-${{ github.event.inputs.version }}-${{ matrix.asset_name_suffix }}.${{ matrix.archive_ext }}.sha256
217244
retention-days: 1
218245

219246
- name: Upload install scripts (linux-amd64 only)
@@ -274,10 +301,15 @@ jobs:
274301
tag_name: v${{ github.event.inputs.version }}
275302
files: |
276303
artifacts/build-linux-amd64/dtvem-${{ github.event.inputs.version }}-linux-amd64.tar.gz
304+
artifacts/build-linux-amd64/dtvem-${{ github.event.inputs.version }}-linux-amd64.tar.gz.sha256
277305
artifacts/build-macos-amd64/dtvem-${{ github.event.inputs.version }}-macos-amd64.tar.gz
306+
artifacts/build-macos-amd64/dtvem-${{ github.event.inputs.version }}-macos-amd64.tar.gz.sha256
278307
artifacts/build-macos-arm64/dtvem-${{ github.event.inputs.version }}-macos-arm64.tar.gz
308+
artifacts/build-macos-arm64/dtvem-${{ github.event.inputs.version }}-macos-arm64.tar.gz.sha256
279309
artifacts/build-windows-amd64/dtvem-${{ github.event.inputs.version }}-windows-amd64.zip
310+
artifacts/build-windows-amd64/dtvem-${{ github.event.inputs.version }}-windows-amd64.zip.sha256
280311
artifacts/build-windows-arm64/dtvem-${{ github.event.inputs.version }}-windows-arm64.zip
312+
artifacts/build-windows-arm64/dtvem-${{ github.event.inputs.version }}-windows-arm64.zip.sha256
281313
install.sh
282314
install.ps1
283315
body: |
@@ -314,7 +346,8 @@ jobs:
314346
315347
## Checksums
316348
317-
See the assets below for SHA256 checksums.
349+
SHA256 checksums are provided for each archive (`.sha256` files).
350+
The installers automatically verify checksums before extraction.
318351
draft: false
319352
prerelease: false
320353
generate_release_notes: true # GitHub will auto-generate additional notes

install.ps1

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,34 @@ function Get-LatestVersion {
4646
}
4747
}
4848

49+
function Test-Checksum {
50+
param(
51+
[string]$FilePath,
52+
[string]$ChecksumPath
53+
)
54+
55+
if (-not (Test-Path $ChecksumPath)) {
56+
Write-Error-Custom "Checksum file not found: $ChecksumPath"
57+
return $false
58+
}
59+
60+
# Read expected hash from checksum file (format: "hash filename")
61+
$checksumContent = Get-Content $ChecksumPath -Raw
62+
$expectedHash = ($checksumContent -split '\s+')[0].ToLower()
63+
64+
# Calculate actual hash
65+
$actualHash = (Get-FileHash -Path $FilePath -Algorithm SHA256).Hash.ToLower()
66+
67+
if ($expectedHash -ne $actualHash) {
68+
Write-Error-Custom "Checksum verification failed!"
69+
Write-Error-Custom "Expected: $expectedHash"
70+
Write-Error-Custom "Actual: $actualHash"
71+
return $false
72+
}
73+
74+
return $true
75+
}
76+
4977
function Main {
5078
Write-Host ""
5179
Write-Host "========================================" -ForegroundColor Blue
@@ -105,6 +133,27 @@ function Main {
105133
exit 1
106134
}
107135

136+
# Download and verify checksum
137+
$CHECKSUM_URL = "$DOWNLOAD_URL.sha256"
138+
$CHECKSUM_PATH = Join-Path $TMP_DIR "$ARCHIVE_NAME.sha256"
139+
140+
Write-Info "Downloading checksum..."
141+
try {
142+
Invoke-WebRequest -Uri $CHECKSUM_URL -OutFile $CHECKSUM_PATH -UseBasicParsing
143+
}
144+
catch {
145+
Write-Error-Custom "Failed to download checksum file: $_"
146+
Write-Error-Custom "URL: $CHECKSUM_URL"
147+
exit 1
148+
}
149+
150+
Write-Info "Verifying checksum..."
151+
if (-not (Test-Checksum -FilePath $ARCHIVE_PATH -ChecksumPath $CHECKSUM_PATH)) {
152+
Write-Error-Custom "Archive integrity check failed - aborting installation"
153+
exit 1
154+
}
155+
Write-Success "Checksum verified"
156+
108157
# Extract archive
109158
Write-Info "Extracting archive..."
110159
Expand-Archive -Path $ARCHIVE_PATH -DestinationPath $TMP_DIR -Force

install.sh

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,40 @@ download() {
9292
fi
9393
}
9494

95+
# Verify SHA256 checksum
96+
verify_checksum() {
97+
local file=$1
98+
local checksum_file=$2
99+
100+
if [ ! -f "$checksum_file" ]; then
101+
error "Checksum file not found: $checksum_file"
102+
return 1
103+
fi
104+
105+
# Extract expected hash from checksum file (format: "hash filename")
106+
local expected_hash=$(awk '{print $1}' "$checksum_file")
107+
108+
# Calculate actual hash
109+
local actual_hash
110+
if command -v sha256sum &> /dev/null; then
111+
actual_hash=$(sha256sum "$file" | awk '{print $1}')
112+
elif command -v shasum &> /dev/null; then
113+
actual_hash=$(shasum -a 256 "$file" | awk '{print $1}')
114+
else
115+
warning "Neither sha256sum nor shasum found - skipping checksum verification"
116+
return 0
117+
fi
118+
119+
if [ "$expected_hash" != "$actual_hash" ]; then
120+
error "Checksum verification failed!"
121+
error "Expected: $expected_hash"
122+
error "Actual: $actual_hash"
123+
return 1
124+
fi
125+
126+
return 0
127+
}
128+
95129
main() {
96130
echo ""
97131
echo -e "${BLUE}========================================${NC}"
@@ -152,6 +186,24 @@ main() {
152186

153187
success "Downloaded successfully"
154188

189+
# Download and verify checksum
190+
CHECKSUM_URL="${DOWNLOAD_URL}.sha256"
191+
CHECKSUM_PATH="$TMP_DIR/${ARCHIVE_NAME}.sha256"
192+
193+
info "Downloading checksum..."
194+
if ! download "$CHECKSUM_URL" "$CHECKSUM_PATH"; then
195+
error "Failed to download checksum file"
196+
error "URL: $CHECKSUM_URL"
197+
exit 1
198+
fi
199+
200+
info "Verifying checksum..."
201+
if ! verify_checksum "$ARCHIVE_PATH" "$CHECKSUM_PATH"; then
202+
error "Archive integrity check failed - aborting installation"
203+
exit 1
204+
fi
205+
success "Checksum verified"
206+
155207
# Extract archive
156208
info "Extracting archive..."
157209
tar -xzf "$ARCHIVE_PATH" -C "$TMP_DIR"

0 commit comments

Comments
 (0)