diff --git a/.changeset/npm-sha256-verify.md b/.changeset/npm-sha256-verify.md new file mode 100644 index 00000000..8662599a --- /dev/null +++ b/.changeset/npm-sha256-verify.md @@ -0,0 +1,5 @@ +--- +"@googleworkspace/cli": patch +--- + +Verify SHA256 checksum of downloaded binary in npm postinstall script diff --git a/npm/install.js b/npm/install.js index 3b906605..614b44a7 100644 --- a/npm/install.js +++ b/npm/install.js @@ -2,6 +2,7 @@ "use strict"; +const crypto = require("crypto"); const fs = require("fs"); const path = require("path"); const os = require("os"); @@ -130,6 +131,23 @@ async function install() { console.error(`Downloading gws from ${url}`); await download(url, tmpFile); + // Verify SHA256 checksum + const sha256Url = `${url}.sha256`; + const sha256File = `${tmpFile}.sha256`; + console.error(`Verifying checksum from ${sha256Url}`); + await download(sha256Url, sha256File); + + const expectedHash = fs.readFileSync(sha256File, "utf8").trim().split(/\s+/)[0].toLowerCase(); + const fileBuffer = fs.readFileSync(tmpFile); + const actualHash = crypto.createHash("sha256").update(fileBuffer).digest("hex").toLowerCase(); + + if (actualHash !== expectedHash) { + throw new Error( + `SHA256 checksum mismatch!\n Expected: ${expectedHash}\n Actual: ${actualHash}\nThe downloaded binary may have been tampered with.`, + ); + } + console.error("Checksum verified ✓"); + console.error(`Extracting to ${INSTALL_DIR}`); extract(tmpFile, INSTALL_DIR);