UNPKG

app-builder-lib

Version:
105 lines 5.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.download = download; exports.getBinFromCustomLoc = getBinFromCustomLoc; exports.getBinFromUrl = getBinFromUrl; exports.getBin = getBin; const fs = require("fs/promises"); const builder_util_1 = require("builder-util"); const dynamicImport_1 = require("./util/dynamicImport"); const filename_1 = require("builder-util/out/filename"); const path = require("path"); const electronGet_1 = require("./util/electronGet"); const versionToPromise = new Map(); async function download(url, output, checksum) { const filenameWithExt = path.basename(new URL(url).pathname); if (checksum == null) { // Without a checksum, a download intercepted via a rogue mirror, tampered CDN // edge, or DNS spoofing can substitute a malicious payload undetected. // Callers should supply a checksum whenever possible. builder_util_1.log.warn({ url }, "downloading without an integrity checksum — the download is not verified against a known-good hash"); } const { downloadArtifact, ElectronDownloadCacheMode } = await (0, dynamicImport_1.dynamicImport)("@electron/get"); const downloadedFile = await downloadArtifact({ version: "9.9.9", artifactName: filenameWithExt, cacheRoot: path.resolve((0, electronGet_1.getCacheDirectory)({ allowEnvVarOverride: true }), "downloads"), cacheMode: ElectronDownloadCacheMode.ReadWrite, ...(checksum != null ? { checksums: { [filenameWithExt]: checksum } } : { unsafelyDisableChecksums: true }), mirrorOptions: { resolveAssetURL: async () => Promise.resolve(url) }, isGeneric: true, }); await fs.copyFile(downloadedFile, output); } function getBinFromCustomLoc(name, version, binariesLocUrl, checksum) { const dirName = `${name}-${version}`; return getBin(dirName, binariesLocUrl, checksum); } /** * Validates a binary-download custom-directory value read from an environment * variable. These values are interpolated directly into a download URL; a * malicious value (e.g. set via a postinstall hook in node_modules) could * redirect tool downloads to an attacker-controlled server. * * Allowed: relative path components such as "v1.0.0-custom" or "my-mirror/nsis". * Rejected: anything containing "://", ".." (traversal), or a leading "/" (which * could make the resulting URL protocol-relative or change the host). */ function validateBinaryCustomDir(envVarName, value) { if (value.includes("://") || value.includes("..") || value.startsWith("/")) { throw new Error(`${envVarName} must be a safe relative path component (e.g. "v1.0.0-custom"). Values containing "://", "..", or a leading "/" are not allowed. Got: "${value}"`); } return value; } function getBinFromUrl(releaseName, filenameWithExt, checksum, githubOrgRepo = "electron-userland/electron-builder-binaries") { if (/[/\\]|^\.\./.test(filenameWithExt) || filenameWithExt.includes("..")) { throw new Error(`getBinFromUrl: unsafe filenameWithExt "${filenameWithExt}" — must be a plain filename with no path separators or traversal sequences`); } let url; const allowHttp = process.env["ELECTRON_BUILDER_BINARIES_ALLOW_HTTP"] === "true"; const overrideUrl = (0, builder_util_1.parseValidEnvVarUrl)("ELECTRON_BUILDER_BINARIES_DOWNLOAD_OVERRIDE_URL", allowHttp); if (overrideUrl != null) { url = overrideUrl + "/" + filenameWithExt; } else { const baseUrl = (0, electronGet_1.getBinariesMirrorUrl)(githubOrgRepo); // Any of these env vars can redirect downloads to a custom release directory. // Validate each one before interpolating into the URL. const CUSTOM_DIR_ENV_VARS = [ "NPM_CONFIG_ELECTRON_BUILDER_BINARIES_CUSTOM_DIR", "npm_config_electron_builder_binaries_custom_dir", "npm_package_config_electron_builder_binaries_custom_dir", "ELECTRON_BUILDER_BINARIES_CUSTOM_DIR", ]; const customDirEntry = CUSTOM_DIR_ENV_VARS.map(name => ({ name, value: process.env[name] })).find(e => e.value != null); const middleUrl = customDirEntry != null ? validateBinaryCustomDir(customDirEntry.name, customDirEntry.value) : releaseName; url = `${baseUrl}${middleUrl}/${filenameWithExt}`; } const cacheKey = `${releaseName}-${path.basename(filenameWithExt, path.extname(filenameWithExt))}`; return getBin(cacheKey, url, checksum); } function getBin(cacheKey, url, checksum) { var _a; const cacheName = (0, filename_1.sanitizeFileName)(`${(_a = process.env.ELECTRON_BUILDER_CACHE) !== null && _a !== void 0 ? _a : ""}${cacheKey}`); let promise = versionToPromise.get(cacheName); if (promise != null) { return promise; } if (url == null) { throw new Error(`getBin("${cacheKey}"): a download URL is required. ` + `The no-URL legacy path (e.g. winCodeSign "0.0.0") is no longer used — ` + `it now downloads from winCodeSign-2.6.0 automatically.`); } const filenameWithExt = path.basename(url); const overrideUrl = url.substring(0, url.lastIndexOf("/")); const releaseName = path.basename(overrideUrl); promise = (0, electronGet_1.downloadBuilderToolset)({ releaseName, filenameWithExt, checksums: checksum != null ? { [filenameWithExt]: checksum } : undefined, overrideUrl, }); versionToPromise.set(cacheName, promise); return promise; } //# sourceMappingURL=binDownload.js.map