app-builder-lib
Version:
electron-builder lib
122 lines • 4.91 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.computeData = computeData;
exports.hashFile = hashFile;
exports.hashFileContents = hashFileContents;
const builder_util_1 = require("builder-util");
const crypto_1 = require("crypto");
const fs_1 = require("fs");
const promises_1 = require("fs/promises");
const path = require("path");
const asar_1 = require("./asar");
async function computeData({ resourcesPath, resourcesRelativePath, resourcesDestinationPath, extraResourceMatchers }) {
const isAsar = (filepath) => filepath.endsWith(".asar");
const resources = await (0, promises_1.readdir)(resourcesPath);
const resourceAsars = resources.filter(isAsar).reduce((prev, filename) => ({
...prev,
[path.join(resourcesRelativePath, filename)]: path.join(resourcesPath, filename),
}), {});
const extraResources = await Promise.all((extraResourceMatchers !== null && extraResourceMatchers !== void 0 ? extraResourceMatchers : []).map(async (matcher) => {
const { from, to } = matcher;
const stat = await (0, builder_util_1.statOrNull)(from);
if (stat == null) {
builder_util_1.log.warn({ from }, `file source doesn't exist`);
return [];
}
if (stat.isFile()) {
return [{ from, to }];
}
if (matcher.isEmpty() || matcher.containsOnlyIgnore()) {
matcher.prependPattern("**/*");
}
const matcherFilter = matcher.createFilter();
const extraResourceMatches = await (0, builder_util_1.walk)(matcher.from, (file, stats) => matcherFilter(file, stats) || stats.isDirectory());
return extraResourceMatches.map(from => ({ from, to: matcher.to }));
}));
const extraResourceAsars = extraResources
.flat(1)
.filter(match => isAsar(match.from))
.reduce((prev, { to, from }) => {
const prefix = path.relative(resourcesDestinationPath, to);
return {
...prev,
[path.join(resourcesRelativePath, prefix, path.basename(from))]: from,
};
}, {});
// sort to produce constant result
const allAsars = [...Object.entries(resourceAsars), ...Object.entries(extraResourceAsars)].sort(([name1], [name2]) => name1.localeCompare(name2));
const hashes = await Promise.all(allAsars.map(async ([, from]) => hashHeader(from)));
const asarIntegrity = {};
for (let i = 0; i < allAsars.length; i++) {
const [asar] = allAsars[i];
asarIntegrity[asar] = hashes[i];
}
return asarIntegrity;
}
async function hashHeader(file) {
const hash = (0, crypto_1.createHash)("sha256");
const { header } = await (0, asar_1.readAsarHeader)(file);
hash.update(header);
return {
algorithm: "SHA256",
hash: hash.digest("hex"),
};
}
function hashFile(file, blockSize = 4 * 1024 * 1024) {
return new Promise((resolve, reject) => {
const hash = (0, crypto_1.createHash)("sha256");
const blocks = new Array();
let blockBytes = 0;
let blockHash = (0, crypto_1.createHash)("sha256");
function updateBlockHash(chunk) {
let off = 0;
while (off < chunk.length) {
const toHash = Math.min(blockSize - blockBytes, chunk.length - off);
blockHash.update(chunk.slice(off, off + toHash));
off += toHash;
blockBytes += toHash;
if (blockBytes === blockSize) {
blocks.push(blockHash.digest("hex"));
blockHash = (0, crypto_1.createHash)("sha256");
blockBytes = 0;
}
}
}
(0, fs_1.createReadStream)(file)
.on("data", it => {
// Note that `it` is a Buffer anyway so this cast is a no-op
updateBlockHash(Buffer.from(it));
hash.update(it);
})
.on("error", reject)
.on("end", () => {
if (blockBytes !== 0) {
blocks.push(blockHash.digest("hex"));
}
resolve({
algorithm: "SHA256",
hash: hash.digest("hex"),
blockSize,
blocks,
});
});
});
}
function hashFileContents(contents, blockSize = 4 * 1024 * 1024) {
const buffer = Buffer.from(contents);
const hash = (0, crypto_1.createHash)("sha256");
hash.update(buffer);
const blocks = new Array();
for (let off = 0; off < buffer.length; off += blockSize) {
const blockHash = (0, crypto_1.createHash)("sha256");
blockHash.update(buffer.slice(off, off + blockSize));
blocks.push(blockHash.digest("hex"));
}
return {
algorithm: "SHA256",
hash: hash.digest("hex"),
blockSize,
blocks,
};
}
//# sourceMappingURL=integrity.js.map
;