From 810944da14ffc97d8fb2b5d78d7c8d09ad514226 Mon Sep 17 00:00:00 2001 From: Ayushman Chhabra <14110965+ayushmanchhabra@users.noreply.github.com> Date: Thu, 15 Aug 2024 14:39:22 +0530 Subject: [PATCH] feat(get): verify sha256 checksum (#1201) --- README.md | 1 - src/get/index.js | 9 +++++++- src/get/verify.js | 48 ++++++++++++++++++++++++++++++++++++++++++ src/get/verify.test.js | 15 +++++++++++++ 4 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 src/get/verify.js create mode 100644 src/get/verify.test.js diff --git a/README.md b/README.md index ee4f3542e..5fef79bb9 100644 --- a/README.md +++ b/README.md @@ -386,7 +386,6 @@ nwbuild({ ### Chores - chore(docs): don't store JSDoc definitions in `typedef`s - get's hard to understand during development. -- chore(get): verify sha checksum for downloads - chore: annotate file paths as `fs.PathLike` instead of `string`. - chore(bld): factor out core build step - chore(bld): factor out linux config diff --git a/src/get/index.js b/src/get/index.js index 7423ab523..8b74795b3 100644 --- a/src/get/index.js +++ b/src/get/index.js @@ -6,6 +6,7 @@ import decompress from "./decompress.js"; import ffmpeg from "./ffmpeg.js"; import node from "./node.js"; import nw from "./nw.js"; +import verify from './verify.js'; import util from "../util.js"; @@ -24,7 +25,7 @@ import util from "../util.js"; /** * Get binaries. - * + * * @deprecated since v4.6.4. This logic will be ported over to `nwjs/npm-installer` repo and removed in the next major release (v5.0). * * @async @@ -133,6 +134,12 @@ async function get(options) { await decompress(ffmpegFilePath, options.cacheDir); + await verify( + `${options.downloadUrl}/v${options.version}/SHASUMS256.txt`, + `${options.cacheDir}/shasum/${options.version}.txt`, + options.cacheDir, + ); + /** * Platform dependant file name of FFmpeg binary. * diff --git a/src/get/verify.js b/src/get/verify.js new file mode 100644 index 000000000..2378c1b80 --- /dev/null +++ b/src/get/verify.js @@ -0,0 +1,48 @@ +import crypto from 'node:crypto'; +import fs from 'node:fs'; +import path from 'node:path'; + +import request from './request.js'; + +import util from '../util.js'; + +/** + * Verify the SHA256 checksum of downloaded artifacts. + * + * @param {string} shaUrl - URL to get the shasum text file from. + * @param {string} shaOut - File path to shasum text file. + * @param {string} cacheDir - File path to cache directory. + * @throws {Error} + * @returns {Promise} + */ +export default async function verify(shaUrl, shaOut, cacheDir) { + const shaOutExists = await util.fileExists(shaOut); + + if (shaOutExists === false) { + /* Create directory if does not exist. */ + await fs.promises.mkdir(path.dirname(shaOut), { recursive: true }); + + /* Download SHASUM text file. */ + await request(shaUrl, shaOut); + } + + /* Read SHASUM text file */ + const shasum = await fs.promises.readFile(shaOut, { encoding: 'utf-8' }); + const shasums = shasum.trim().split('\n'); + for await (const line of shasums) { + const [storedSha, filePath] = line.split(' '); + const relativeFilePath = path.resolve(cacheDir, filePath); + const relativefilePathExists = await util.fileExists(relativeFilePath); + if (relativefilePathExists) { + const fileBuffer = await fs.promises.readFile(relativeFilePath); + const hash = crypto.createHash('sha256'); + hash.update(fileBuffer); + const generatedSha = hash.digest('hex'); + if (storedSha !== generatedSha) { + throw new Error('SHA256 checksums do not match.'); + } + } + } + + return true; +} diff --git a/src/get/verify.test.js b/src/get/verify.test.js new file mode 100644 index 000000000..24539b06d --- /dev/null +++ b/src/get/verify.test.js @@ -0,0 +1,15 @@ +import { describe, expect, it } from "vitest"; + +import verify from "./verify.js"; + +describe("get/verify", function () { + + it("verify shasums", async function () { + const status = await verify( + 'https://dl.nwjs.io/v0.90.0/SHASUMS256.txt', + './node_modules/nw/shasum/0.90.0.txt', + './node_modules/nw' + ); + expect(status).toBe(true); + }, Infinity); +});