mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
fix(install): verify release archive checksum before install (#38)
The first-run installers (install.sh, install.ps1) downloaded and extracted the bsk release archive with no integrity check, even though the in-app `bsk update` path treats a sha256 checksum as mandatory (update.rs bails with "does not include a sha256 checksum; cannot safely auto-update"). The release version.json already carries a per-asset sha256 (render-version-json.mjs renders with --dist), so the installers can verify the same way. Both installers now fetch version.json once (pinned and latest), read `assets[<platform>].sha256`, and verify the downloaded archive's sha256 before extracting: - install.sh: POSIX sed to parse the field (no jq dependency), and sha256sum/shasum to compute the digest. - install.ps1: Invoke-RestMethod + Get-FileHash. A checksum mismatch is fatal (fail-closed, matching `bsk update`). A missing manifest/checksum, or the absence of a sha256 tool, only skips verification with a warning, so the first-run bootstrap still works on minimal/offline machines. Validation: `sh -n` / `bash -n`; functional test of the sed extraction against a realistic version.json and of compute_sha256 against sha256sum. The installers are not exercised by CI (no shell harness). Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
+37
-16
@@ -53,25 +53,12 @@ function Get-PlatformTriple {
|
||||
return @{
|
||||
ArchId = $archId
|
||||
TargetTriple = $windowsArch
|
||||
PlatformKey = "windows-$archId"
|
||||
}
|
||||
}
|
||||
|
||||
# ── Version resolution ────────────────────────────────────────────────────────
|
||||
|
||||
function Get-LatestVersion {
|
||||
$versionJsonUrl = "${GitHub}/releases/latest/download/version.json"
|
||||
Write-Log "fetching latest version from ${versionJsonUrl}"
|
||||
|
||||
try {
|
||||
$version = (Invoke-RestMethod -Uri $versionJsonUrl).version
|
||||
if (-not $version) { Write-Die "could not parse version from version.json" }
|
||||
return $version
|
||||
}
|
||||
catch {
|
||||
Write-Die "failed to fetch version.json: $_"
|
||||
}
|
||||
}
|
||||
|
||||
# ── PATH helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
function Add-ToUserPath {
|
||||
@@ -132,17 +119,40 @@ function Main {
|
||||
|
||||
if ($env:BSK_VERSION) {
|
||||
$version = $env:BSK_VERSION -replace '^v', ''
|
||||
$tag = "cli-v${version}"
|
||||
$manifestUrl = "${GitHub}/releases/download/${tag}/version.json"
|
||||
Write-Log "using pinned version ${version}"
|
||||
# Best-effort manifest fetch for the checksum (missing manifest
|
||||
# only skips verification; a mismatch is fatal below).
|
||||
try { $manifest = Invoke-RestMethod -Uri $manifestUrl } catch { $manifest = $null }
|
||||
}
|
||||
else {
|
||||
$version = Get-LatestVersion
|
||||
$manifestUrl = "${GitHub}/releases/latest/download/version.json"
|
||||
Write-Log "fetching latest version from ${manifestUrl}"
|
||||
$manifest = Invoke-RestMethod -Uri $manifestUrl
|
||||
$version = $manifest.version
|
||||
if (-not $version) { Write-Die "could not parse version from version.json" }
|
||||
$tag = "cli-v${version}"
|
||||
Write-Log "latest version is ${version}"
|
||||
}
|
||||
|
||||
$tag = "cli-v${version}"
|
||||
$archiveName = "bsk-v${version}-$($platform.TargetTriple).zip"
|
||||
$downloadUrl = "${GitHub}/releases/download/${tag}/${archiveName}"
|
||||
|
||||
$expectedSha = $null
|
||||
$platformKey = $platform.PlatformKey
|
||||
if ($manifest -and $manifest.assets) {
|
||||
$expectedSha = $manifest.assets.$platformKey.sha256
|
||||
}
|
||||
if (-not $expectedSha) {
|
||||
if (-not $manifest) {
|
||||
Write-Log "warning: could not fetch version.json; skipping checksum verification"
|
||||
}
|
||||
else {
|
||||
Write-Log "warning: no checksum published for $($platform.PlatformKey); skipping checksum verification"
|
||||
}
|
||||
}
|
||||
|
||||
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())
|
||||
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
|
||||
|
||||
@@ -152,6 +162,17 @@ function Main {
|
||||
Write-Log "downloading ${downloadUrl}"
|
||||
Invoke-WebRequest -Uri $downloadUrl -OutFile $archivePath -UseBasicParsing -ErrorAction Stop
|
||||
|
||||
if ($expectedSha) {
|
||||
Write-Log "verifying checksum"
|
||||
$actualSha = (Get-FileHash -Algorithm SHA256 -Path $archivePath).Hash
|
||||
if ($actualSha -ieq $expectedSha) {
|
||||
Write-Log "checksum OK"
|
||||
}
|
||||
else {
|
||||
Write-Die "checksum mismatch: expected $expectedSha, got $actualSha"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Log "extracting ${archiveName}"
|
||||
Expand-Archive -Path $archivePath -DestinationPath $tempDir -Force
|
||||
|
||||
|
||||
+58
-9
@@ -56,13 +56,25 @@ detect_platform() {
|
||||
esac
|
||||
}
|
||||
|
||||
fetch_latest_version() {
|
||||
version_json="${GITHUB}/releases/latest/download/version.json"
|
||||
log "fetching latest version from ${version_json}"
|
||||
json="$(curl -fsSL "$version_json")"
|
||||
version="$(printf '%s' "$json" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1)"
|
||||
[ -n "$version" ] || die "could not parse version.json"
|
||||
printf '%s' "$version"
|
||||
# Extract the per-platform `assets[<platform_key>].sha256` hex from a
|
||||
# version.json manifest. POSIX sed only (no jq dependency) — matches the
|
||||
# platform's object block then the sha256 field inside it.
|
||||
extract_asset_sha256() {
|
||||
json="$1"
|
||||
key="$2"
|
||||
printf '%s\n' "$json" | sed -n '/"'"$key"'":[[:space:]]*{/,/}/ s/.*"sha256"[[:space:]]*:[[:space:]]*"\([0-9a-fA-F]\{1,\}\)".*/\1/p' | head -n 1
|
||||
}
|
||||
|
||||
# Print the lowercase sha256 of a file. Returns non-zero (and prints
|
||||
# nothing) when neither sha256sum nor shasum is available.
|
||||
compute_sha256() {
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
sha256sum "$1" | sed 's/[[:space:]].*//'
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
shasum -a 256 "$1" | sed 's/[[:space:]].*//'
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
path_contains_dir() {
|
||||
@@ -115,24 +127,61 @@ main() {
|
||||
|
||||
detect_platform
|
||||
|
||||
# Resolve the version + manifest once. The manifest also carries the
|
||||
# per-asset sha256 we use to verify the download (mirrors `bsk update`,
|
||||
# which refuses to auto-update a release without a checksum).
|
||||
manifest_json=""
|
||||
if [ -n "${BSK_VERSION:-}" ]; then
|
||||
version="${BSK_VERSION#v}"
|
||||
tag="cli-v${version}"
|
||||
manifest_url="${GITHUB}/releases/download/${tag}/version.json"
|
||||
log "using pinned version ${version}"
|
||||
manifest_json="$(curl -fsSL "$manifest_url" 2>/dev/null || true)"
|
||||
else
|
||||
version="$(fetch_latest_version)"
|
||||
manifest_url="${GITHUB}/releases/latest/download/version.json"
|
||||
log "fetching latest version from ${manifest_url}"
|
||||
manifest_json="$(curl -fsSL "$manifest_url")" || die "could not fetch version.json"
|
||||
version="$(printf '%s' "$manifest_json" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1)"
|
||||
[ -n "$version" ] || die "could not parse version from version.json"
|
||||
tag="cli-v${version}"
|
||||
log "latest version is ${version}"
|
||||
fi
|
||||
|
||||
tag="cli-v${version}"
|
||||
archive="bsk-v${version}-${triple}.tar.gz"
|
||||
download_url="${GITHUB}/releases/download/${tag}/${archive}"
|
||||
|
||||
# Best-effort checksum: a missing manifest/checksum only skips
|
||||
# verification (does not block the install), but a *mismatch* is fatal.
|
||||
expected_sha=""
|
||||
if [ -n "$manifest_json" ]; then
|
||||
expected_sha="$(extract_asset_sha256 "$manifest_json" "$platform_key")"
|
||||
else
|
||||
log "warning: could not fetch version.json; skipping checksum verification"
|
||||
fi
|
||||
if [ -z "$expected_sha" ] && [ -n "$manifest_json" ]; then
|
||||
log "warning: no checksum published for ${platform_key}; skipping checksum verification"
|
||||
fi
|
||||
|
||||
tmp_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmp_dir"' EXIT INT TERM
|
||||
|
||||
log "downloading ${download_url}"
|
||||
curl -fsSL "$download_url" -o "${tmp_dir}/${archive}"
|
||||
|
||||
if [ -n "$expected_sha" ]; then
|
||||
log "verifying checksum"
|
||||
if actual_sha="$(compute_sha256 "${tmp_dir}/${archive}")"; then
|
||||
expected_lower="$(printf '%s' "$expected_sha" | tr 'A-F' 'a-z')"
|
||||
if [ "$actual_sha" = "$expected_lower" ]; then
|
||||
log "checksum OK"
|
||||
else
|
||||
die "checksum mismatch: expected ${expected_lower}, got ${actual_sha}"
|
||||
fi
|
||||
else
|
||||
log "warning: no sha256 tool (sha256sum/shasum) found; skipping checksum verification"
|
||||
fi
|
||||
fi
|
||||
|
||||
log "extracting ${archive}"
|
||||
tar -xzf "${tmp_dir}/${archive}" -C "$tmp_dir"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user