From e9bd334b7faedc5eb940d61b37b569e9cd71e413 Mon Sep 17 00:00:00 2001 From: kite Date: Tue, 16 Jun 2026 16:15:14 +0800 Subject: [PATCH] fix: enforce mandatory checksum verification and HTTPS-only registry access Previously, checksum failures in install.js were silently downgraded to warnings, allowing unverified binaries to be installed. Similarly, update.js accepted HTTP registries and skipped verification when no matching platform entry was found. Now both scripts treat checksum verification as mandatory when configured: all failure paths (download error, compute error, mismatch, missing platform entry) abort the operation and clean up downloaded files. update.js also drops the http module and silently skips version checks for non-HTTPS registries. --- scripts/install.js | 61 ++++++++++++++++++++++++++-------------------- scripts/update.js | 53 ++++++++++++++++++++++++---------------- 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/scripts/install.js b/scripts/install.js index d54b8fc..4c59b97 100644 --- a/scripts/install.js +++ b/scripts/install.js @@ -189,37 +189,46 @@ async function main() { } if (config.checksumPattern) { + const checksumUrl = buildUrl(config.checksumPattern, vars); + info("Verifying checksum..."); + let shaContent; try { - const checksumUrl = buildUrl(config.checksumPattern, vars); - info("Verifying checksum..."); - const shaContent = await downloadText(checksumUrl); - const actualSha = await computeChecksum(binaryDest); + shaContent = await downloadText(checksumUrl); + } catch (e) { + try { fs.unlinkSync(binaryDest); } catch (_) {} + throw new Error(`Failed to download checksum from ${checksumUrl}: ${e.message}`); + } + let actualSha; + try { + actualSha = await computeChecksum(binaryDest); + } catch (e) { + try { fs.unlinkSync(binaryDest); } catch (_) {} + throw new Error(`Failed to compute checksum for ${binaryDest}: ${e.message}`); + } - let verified = false; - for (const line of shaContent.split("\n")) { - const trimmed = line.trim(); - if (trimmed.includes(`-${os}-${arch}`)) { - const expectedSha = trimmed.split(/\s+/)[0].toLowerCase(); - if (expectedSha) { - if (actualSha !== expectedSha) { - throw new Error( - `Checksum mismatch! Expected: ${expectedSha}, Got: ${actualSha}` - ); - } - info("Checksum verified."); - verified = true; - break; + let verified = false; + for (const line of shaContent.split("\n")) { + const trimmed = line.trim(); + if (trimmed.includes(`-${os}-${arch}`)) { + const expectedSha = trimmed.split(/\s+/)[0].toLowerCase(); + if (expectedSha) { + if (actualSha !== expectedSha) { + try { fs.unlinkSync(binaryDest); } catch (_) {} + throw new Error( + `Checksum mismatch! Expected: ${expectedSha}, Got: ${actualSha}` + ); } + info("Checksum verified."); + verified = true; + break; } } - if (!verified) { - warn("No matching checksum entry found; skipping verification."); - } - } catch (e) { - if (e.message.includes("mismatch")) { - throw e; - } - warn(`Could not verify checksum: ${e.message}`); + } + if (!verified) { + try { fs.unlinkSync(binaryDest); } catch (_) {} + throw new Error( + `No matching checksum entry for ${os}-${arch} in ${checksumUrl}` + ); } } diff --git a/scripts/update.js b/scripts/update.js index 9b3a634..8cb407a 100644 --- a/scripts/update.js +++ b/scripts/update.js @@ -4,7 +4,6 @@ const fs = require("fs"); const path = require("path"); const os = require("os"); -const http = require("http"); const https = require("https"); const { spawnSync } = require("child_process"); @@ -85,14 +84,14 @@ function fetchLatestVersion(pkg) { if (!pkgName) return Promise.resolve(null); const encodedName = pkgName.replace(/\//g, "%2F"); const url = `${registry.replace(/\/$/, "")}/${encodedName}/latest`; - const client = url.startsWith("https") ? https : http; + if (!url.startsWith("https://")) return Promise.resolve(null); return new Promise((resolve) => { const options = { headers: { "User-Agent": "ocr-updater", Accept: "application/json" }, timeout: 15000, }; - const req = client + const req = https .get(url, options, (res) => { if (res.statusCode !== 200) { res.resume(); @@ -173,26 +172,38 @@ async function main() { } if (config.checksumPattern) { + const checksumUrl = buildUrl(config.checksumPattern, vars); + let shaContent; try { - const checksumUrl = buildUrl(config.checksumPattern, vars); - const shaContent = await downloadText(checksumUrl); - const actualSha = await computeChecksum(tempPath); - - let verified = false; - for (const line of shaContent.split("\n")) { - const trimmed = line.trim(); - if (trimmed.includes(`-${platform}-${arch}`)) { - const expectedSha = trimmed.split(/\s+/)[0].toLowerCase(); - if (expectedSha && actualSha !== expectedSha) { - fs.unlinkSync(tempPath); - return; - } - verified = true; - break; - } - } + shaContent = await downloadText(checksumUrl); } catch (_) { - // checksum fetch failed, continue with the download + fs.unlinkSync(tempPath); + return; + } + let actualSha; + try { + actualSha = await computeChecksum(tempPath); + } catch (_) { + fs.unlinkSync(tempPath); + return; + } + + let verified = false; + for (const line of shaContent.split("\n")) { + const trimmed = line.trim(); + if (trimmed.includes(`-${platform}-${arch}`)) { + const expectedSha = trimmed.split(/\s+/)[0].toLowerCase(); + if (expectedSha && actualSha !== expectedSha) { + fs.unlinkSync(tempPath); + return; + } + verified = true; + break; + } + } + if (!verified) { + fs.unlinkSync(tempPath); + return; } }