app-builder-lib
Version:
electron-builder lib
105 lines • 5.58 kB
JavaScript
;
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