From d1e8ca3a455ee4981f278398e15e088aacd529b9 Mon Sep 17 00:00:00 2001 From: Nicolas Rigaudiere Date: Fri, 13 Feb 2026 16:59:47 +0100 Subject: [PATCH 1/3] fix: make changeset public --- .changeset/config.json | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.changeset/config.json b/.changeset/config.json index 165b772..d0a5686 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,11 +1,20 @@ { "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json", - "changelog": ["@changesets/changelog-github", { "repo": "lightpanda-io/node-packages" }], + "changelog": [ + "@changesets/changelog-github", + { + "repo": "lightpanda-io/node-packages" + } + ], "commit": true, - "fixed": [["@lightpanda/browser"]], + "fixed": [ + [ + "@lightpanda/browser" + ] + ], "linked": [], - "access": "restricted", + "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", "ignore": [] -} +} \ No newline at end of file From 6394f2741fa104da9ee3d63be55478bfa9d7993c Mon Sep 17 00:00:00 2001 From: Nicolas Rigaudiere Date: Mon, 23 Feb 2026 14:38:12 +0100 Subject: [PATCH 2/3] feat: add checksum verification --- packages/browser/src/download.ts | 45 +++++++++++++++++++++++++++++--- packages/browser/src/utils.ts | 23 ++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/packages/browser/src/download.ts b/packages/browser/src/download.ts index 75b0281..3110e8e 100644 --- a/packages/browser/src/download.ts +++ b/packages/browser/src/download.ts @@ -16,7 +16,17 @@ import { constants, chmodSync, createWriteStream, existsSync, mkdirSync } from 'node:fs' import https from 'node:https' import { arch, exit, platform } from 'node:process' -import { DEFAULT_CACHE_FOLDER, DEFAULT_EXECUTABLE_PATH, USER_EXECUTABLE_PATH } from './utils' +import { + DEFAULT_CACHE_FOLDER, + DEFAULT_EXECUTABLE_PATH, + USER_EXECUTABLE_PATH, + checksumFile, +} from './utils' + +type GH_ASSET = { + name: string + digest: string +} const PLATFORMS = { darwin: { @@ -66,6 +76,25 @@ export const download = async (): Promise => { return new Promise((resolve, reject) => get(url, resolve, reject)) } + const getGithubHash = async (path: string) => { + try { + const f = await fetch(path) + const data = await f.json() + + const asset: GH_ASSET = data.assets.find( + (a: GH_ASSET) => a.name === `lightpanda-${platformPath}`, + ) + + if (asset) { + return asset.digest + } + + return '' + } catch (e) { + throw new Error(e) + } + } + if (platformPath) { if (USER_EXECUTABLE_PATH) { console.info('$LIGHTPANDA_EXECUTABLE_PATH found, skipping binary download…') @@ -73,11 +102,21 @@ export const download = async (): Promise => { } try { - console.info('⏳ Downloading latest version of Lightpanda browser…') - + console.info('⏳ Downloading latest version of Lightpanda browser…', '\n') await downloadBinary( `https://github.com/lightpanda-io/browser/releases/download/nightly/lightpanda-${platformPath}`, ) + + console.info('🔐 Getting and comparing checksums…', '\n') + const ghChecksum = await getGithubHash( + 'https://api.github.com/repos/lightpanda-io/browser/releases/tags/nightly', + ) + const lpChecksum = await checksumFile(DEFAULT_EXECUTABLE_PATH) + + if (ghChecksum !== lpChecksum) { + throw new Error("🚫 Checksums don't match!") + } + chmodSync(DEFAULT_EXECUTABLE_PATH, constants.S_IRWXU) console.info('✅ Done!') diff --git a/packages/browser/src/utils.ts b/packages/browser/src/utils.ts index f2cffbd..b38a958 100644 --- a/packages/browser/src/utils.ts +++ b/packages/browser/src/utils.ts @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import crypto, { type BinaryToTextEncoding } from 'node:crypto' +import fs from 'node:fs' import os from 'node:os' export const DEFAULT_CACHE_FOLDER = `${os.homedir()}/.cache/lightpanda-node` @@ -61,3 +64,23 @@ export const validatePort = (port: number): void => { export const getExecutablePath = () => { return USER_EXECUTABLE_PATH ?? DEFAULT_EXECUTABLE_PATH } + +/** + * Get checksum from file + */ +export const checksumFile = ( + filePath: string, + algorithm = 'sha256', + encoding: BinaryToTextEncoding = 'hex', +) => { + return new Promise((resolve, reject) => { + fs.readFile(filePath, (err, data) => { + if (err) { + reject(err) + } + + const hash = crypto.createHash(algorithm).update(data).digest(encoding) + resolve(`${algorithm}:${hash}`) + }) + }) +} From d83f4ff45c041884acd135e542f4fe4f224bee98 Mon Sep 17 00:00:00 2001 From: Nicolas Rigaudiere Date: Mon, 23 Feb 2026 14:41:22 +0100 Subject: [PATCH 3/3] chore: add const for gh url --- packages/browser/src/download.ts | 5 ++--- packages/browser/src/utils.ts | 3 +++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/browser/src/download.ts b/packages/browser/src/download.ts index 3110e8e..a158fd3 100644 --- a/packages/browser/src/download.ts +++ b/packages/browser/src/download.ts @@ -19,6 +19,7 @@ import { arch, exit, platform } from 'node:process' import { DEFAULT_CACHE_FOLDER, DEFAULT_EXECUTABLE_PATH, + GITHUB_RELEASE_DATA_URL, USER_EXECUTABLE_PATH, checksumFile, } from './utils' @@ -108,9 +109,7 @@ export const download = async (): Promise => { ) console.info('🔐 Getting and comparing checksums…', '\n') - const ghChecksum = await getGithubHash( - 'https://api.github.com/repos/lightpanda-io/browser/releases/tags/nightly', - ) + const ghChecksum = await getGithubHash(GITHUB_RELEASE_DATA_URL) const lpChecksum = await checksumFile(DEFAULT_EXECUTABLE_PATH) if (ghChecksum !== lpChecksum) { diff --git a/packages/browser/src/utils.ts b/packages/browser/src/utils.ts index b38a958..06404ae 100644 --- a/packages/browser/src/utils.ts +++ b/packages/browser/src/utils.ts @@ -24,6 +24,9 @@ export const BINARY_NAME = 'lightpanda' export const USER_EXECUTABLE_PATH = process.env.LIGHTPANDA_EXECUTABLE_PATH export const DEFAULT_EXECUTABLE_PATH = `${DEFAULT_CACHE_FOLDER}/${BINARY_NAME}` +export const GITHUB_RELEASE_DATA_URL = + 'https://api.github.com/repos/lightpanda-io/browser/releases/tags/nightly' + /** * Validate a URL structure * @param {string} url URL to validate