From 6f630927ca949d8bdcde06e4eafaa63ce3636d5a Mon Sep 17 00:00:00 2001 From: Mike Maietta Date: Sun, 4 Jul 2021 01:34:15 -0700 Subject: [PATCH] fix: updating electron-osx-sign (#6021) * fix: migrating to electron-osx-sign package to sync with upstream. Fixes: #6010 & #5190 * cast to any --- .github/pr-labeler.yml | 6 +- .../app-builder-lib/electron-osx-sign/LICENSE | 23 - .../default.entitlements.darwin.inherit.plist | 6 - .../default.entitlements.darwin.plist | 6 - .../default.entitlements.mas.inherit.plist | 10 - .../default.entitlements.mas.plist | 8 - .../app-builder-lib/electron-osx-sign/flat.js | 149 ------- .../electron-osx-sign/index.d.ts | 41 -- .../electron-osx-sign/index.js | 56 --- .../app-builder-lib/electron-osx-sign/sign.js | 421 ------------------ .../electron-osx-sign/util-entitlements.js | 91 ---- .../electron-osx-sign/util-identities.js | 55 --- .../util-provisioning-profiles.js | 178 -------- .../app-builder-lib/electron-osx-sign/util.js | 243 ---------- packages/app-builder-lib/package.json | 1 + .../src/asar/unpackDetector.ts | 4 +- .../src/codeSign/macCodeSign.ts | 2 +- packages/app-builder-lib/src/macPackager.ts | 20 +- .../windowsExecutableCodeSignatureVerifier.ts | 3 +- pnpm-lock.yaml | 46 +- test/package.json | 1 + test/src/helpers/CheckingPackager.ts | 2 +- 22 files changed, 71 insertions(+), 1301 deletions(-) delete mode 100644 packages/app-builder-lib/electron-osx-sign/LICENSE delete mode 100644 packages/app-builder-lib/electron-osx-sign/default.entitlements.darwin.inherit.plist delete mode 100644 packages/app-builder-lib/electron-osx-sign/default.entitlements.darwin.plist delete mode 100755 packages/app-builder-lib/electron-osx-sign/default.entitlements.mas.inherit.plist delete mode 100755 packages/app-builder-lib/electron-osx-sign/default.entitlements.mas.plist delete mode 100644 packages/app-builder-lib/electron-osx-sign/flat.js delete mode 100644 packages/app-builder-lib/electron-osx-sign/index.d.ts delete mode 100644 packages/app-builder-lib/electron-osx-sign/index.js delete mode 100644 packages/app-builder-lib/electron-osx-sign/sign.js delete mode 100644 packages/app-builder-lib/electron-osx-sign/util-entitlements.js delete mode 100644 packages/app-builder-lib/electron-osx-sign/util-identities.js delete mode 100644 packages/app-builder-lib/electron-osx-sign/util-provisioning-profiles.js delete mode 100644 packages/app-builder-lib/electron-osx-sign/util.js diff --git a/.github/pr-labeler.yml b/.github/pr-labeler.yml index 0cb36313820..11e4a56c326 100644 --- a/.github/pr-labeler.yml +++ b/.github/pr-labeler.yml @@ -5,11 +5,11 @@ builder-util: - packages/builder-util-runtime/**/* mac: - packages/dmg-builder/**/* -- packages/**/mac*.ts +- '**/mac*.ts' linux: -- packages/**/linux*.ts +- '**/linux*.ts' windows: -- packages/**/win*.ts +- '**/win*.ts' squirrel.windows: - packages/electron-builder-squirrel-windows/**/* appimage: diff --git a/packages/app-builder-lib/electron-osx-sign/LICENSE b/packages/app-builder-lib/electron-osx-sign/LICENSE deleted file mode 100644 index ce0af629fc6..00000000000 --- a/packages/app-builder-lib/electron-osx-sign/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2015-2016 Zhuo Lu, Jason Hinkle, et al. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/app-builder-lib/electron-osx-sign/default.entitlements.darwin.inherit.plist b/packages/app-builder-lib/electron-osx-sign/default.entitlements.darwin.inherit.plist deleted file mode 100644 index 1da3936a909..00000000000 --- a/packages/app-builder-lib/electron-osx-sign/default.entitlements.darwin.inherit.plist +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/app-builder-lib/electron-osx-sign/default.entitlements.darwin.plist b/packages/app-builder-lib/electron-osx-sign/default.entitlements.darwin.plist deleted file mode 100644 index 1da3936a909..00000000000 --- a/packages/app-builder-lib/electron-osx-sign/default.entitlements.darwin.plist +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/app-builder-lib/electron-osx-sign/default.entitlements.mas.inherit.plist b/packages/app-builder-lib/electron-osx-sign/default.entitlements.mas.inherit.plist deleted file mode 100755 index d8dc69e8089..00000000000 --- a/packages/app-builder-lib/electron-osx-sign/default.entitlements.mas.inherit.plist +++ /dev/null @@ -1,10 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.inherit - - - diff --git a/packages/app-builder-lib/electron-osx-sign/default.entitlements.mas.plist b/packages/app-builder-lib/electron-osx-sign/default.entitlements.mas.plist deleted file mode 100755 index 8e31f755ad1..00000000000 --- a/packages/app-builder-lib/electron-osx-sign/default.entitlements.mas.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/packages/app-builder-lib/electron-osx-sign/flat.js b/packages/app-builder-lib/electron-osx-sign/flat.js deleted file mode 100644 index 69db84ec9fc..00000000000 --- a/packages/app-builder-lib/electron-osx-sign/flat.js +++ /dev/null @@ -1,149 +0,0 @@ -/** - * @module flat - */ - -'use strict' - -const path = require('path') - -const util = require('./util') -const debuglog = util.debuglog -const debugwarn = util.debugwarn -const execFileAsync = util.execFileAsync -const validateOptsAppAsync = util.validateOptsAppAsync -const validateOptsPlatformAsync = util.validateOptsPlatformAsync -const Identity = require('./util-identities').findIdentitiesAsync -const findIdentitiesAsync = require('./util-identities').findIdentitiesAsync - -/** - * This function returns a promise validating all options passed in opts. - * @function - * @param {Object} opts - Options. - * @returns {Promise} Promise. - */ -function validateFlatOptsAsync (opts) { - if (opts.pkg) { - if (typeof opts.pkg !== 'string') return Promise.reject(new Error('`pkg` must be a string.')) - if (path.extname(opts.pkg) !== '.pkg') return Promise.reject(new Error('Extension of output package must be `.pkg`.')) - } else { - debugwarn('No `pkg` passed in arguments, will fallback to default inferred from the given application.') - opts.pkg = path.join(path.dirname(opts.app), path.basename(opts.app, '.app') + '.pkg') - } - - if (opts.install) { - if (typeof opts.install !== 'string') return Promise.reject(new Error('`install` must be a string.')) - } else { - debugwarn('No `install` passed in arguments, will fallback to default `/Applications`.') - opts.install = '/Applications' - } - - return Promise.all([ - validateOptsAppAsync(opts), - validateOptsPlatformAsync(opts), - ]) -} - -/** - * This function returns a promise flattening the application. - * @function - * @param {Object} opts - Options. - * @returns {Promise} Promise. - */ -function flatApplicationAsync (opts) { - const args = [ - '--component', opts.app, opts.install, - '--sign', opts.identity.name, - opts.pkg - ] - if (opts.keychain) { - args.unshift('--keychain', opts.keychain) - } - if (opts.scripts) { - args.unshift('--scripts', opts.scripts) - } - - debuglog('Flattening... ' + opts.app) - return execFileAsync('productbuild', args) - .thenReturn(undefined) -} - -/** - * This function is exported and returns a promise flattening the application. - * @function - * @param {Object} opts - Options. - * @returns {Promise} Promise. - */ -module.exports.flatAsync = function (opts) { - return validateFlatOptsAsync(opts) - .then(function () { - let promise - if (opts.identity) { - debuglog('`identity` passed in arguments.') - if (opts['identity-validation'] === false || opts.identity instanceof Identity) { - return Promise.resolve() - } - promise = findIdentitiesAsync(opts, opts.identity) - } else { - debugwarn('No `identity` passed in arguments...') - if (opts.platform === 'mas') { - debuglog('Finding `3rd Party Mac Developer Installer` certificate for flattening app distribution in the Mac App Store...') - promise = findIdentitiesAsync(opts, '3rd Party Mac Developer Installer:') - } else { - debuglog('Finding `Developer ID Application` certificate for distribution outside the Mac App Store...') - promise = findIdentitiesAsync(opts, 'Developer ID Installer:') - } - } - return promise - .then(function (identities) { - if (identities.length > 0) { - // Provisioning profile(s) found - if (identities.length > 1) { - debugwarn('Multiple identities found, will use the first discovered.') - } else { - debuglog('Found 1 identity.') - } - opts.identity = identities[0] - } else { - // No identity found - return Promise.reject(new Error('No identity found for signing.')) - } - }) - }) - .then(function () { - // Pre-flat operations - }) - .then(function () { - debuglog('Flattening application...', '\n', - '> Application:', opts.app, '\n', - '> Package output:', opts.pkg, '\n', - '> Install path:', opts.install, '\n', - '> Identity:', opts.identity, '\n', - '> Scripts:', opts.scripts) - return flatApplicationAsync(opts) - }) - .then(function () { - // Post-flat operations - debuglog('Application flattened.') - }) -} - -/** - * This function is exported with normal callback implementation. - * @function - * @param {Object} opts - Options. - * @param {RequestCallback} cb - Callback. - */ -module.exports.flat = function (opts, cb) { - module.exports.flatAsync(opts) - .then(function () { - debuglog('Application flattened, saved to: ' + opts.app) - if (cb) cb() - }) - .catch(function (err) { - debuglog('Flat failed:') - if (err.message) debuglog(err.message) - else if (err.stack) debuglog(err.stack) - else debuglog(err) - if (cb) cb(err) - }) -} diff --git a/packages/app-builder-lib/electron-osx-sign/index.d.ts b/packages/app-builder-lib/electron-osx-sign/index.d.ts deleted file mode 100644 index 326909a5371..00000000000 --- a/packages/app-builder-lib/electron-osx-sign/index.d.ts +++ /dev/null @@ -1,41 +0,0 @@ -interface BaseSignOptions { - app: string; - identity?: string; - platform?: string; - keychain?: string; -} - -interface SignOptions extends BaseSignOptions { - binaries?: string[]; - entitlements?: string; - 'entitlements-inherit'?: string; - 'entitlements-loginhelper'?: string; - 'gatekeeper-assess'?: boolean; - hardenedRuntime?: boolean; - 'identity-validation'?: boolean; - ignore?: string; - 'pre-auto-entitlements'?: boolean; - 'pre-embed-provisioning-profile'?: boolean; - 'provisioning-profile'?: string; - 'requirements'?: string; - 'signature-size'?: number; - type?: string; - version?: string; -} - -export function sign(opts: SignOptions, callback: (error: Error) => void): void; - -export function signAsync(opts: SignOptions): Promise; - -interface FlatOptions extends BaseSignOptions { - 'identity-validation'?: boolean; - install?: string; - pkg?: string; - scripts?: string; -} - -export function flat(opts: FlatOptions, callback: (error: Error) => void): void; - -export function flatAsync(opts: FlatOptions): Promise; - -export function getFilePathIfBinarySync(filePath: string): string | undefined \ No newline at end of file diff --git a/packages/app-builder-lib/electron-osx-sign/index.js b/packages/app-builder-lib/electron-osx-sign/index.js deleted file mode 100644 index f3c4172bc76..00000000000 --- a/packages/app-builder-lib/electron-osx-sign/index.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @module electron-osx-sign - */ - -'use strict' - -const sign = require('./sign') -const flat = require('./flat') -const util = require('./util') - -/** - * This function is a normal callback implementation. - * @param {Object} opts - Options. - * @param {RequestCallback} cb - Callback. - */ -module.exports = sign.sign // Aliasing - -/** - * This function is a normal callback implementation. - * @function - * @param {Object} opts - Options. - * @param {RequestCallback} cb - Callback. - */ -module.exports.sign = sign.sign - -/** - * This function returns a promise signing the application. - * @function - * @param {mixed} opts - Options. - * @returns {Promise} Promise. - */ -module.exports.signAsync = sign.signAsync - -/** - * This function is exported with normal callback implementation. - * @function - * @param {Object} opts - Options. - * @param {RequestCallback} cb - Callback. - */ -module.exports.flat = flat.flat - -/** - * This function is exported and returns a promise flattening the application. - * @function - * @param {Object} opts - Options. - * @returns {Promise} - Promise. - */ -module.exports.flatAsync = flat.flatAsync - -/** - * This function is exported and returns a filepath if detected to be a binary (sync). - * @function - * @param {string} filepath - Filepath - * @returns {string | undefined} - Filepath if binary, otherwise undefined - */ -module.exports.getFilePathIfBinarySync = util.getFilePathIfBinarySync diff --git a/packages/app-builder-lib/electron-osx-sign/sign.js b/packages/app-builder-lib/electron-osx-sign/sign.js deleted file mode 100644 index 78fcb910a1a..00000000000 --- a/packages/app-builder-lib/electron-osx-sign/sign.js +++ /dev/null @@ -1,421 +0,0 @@ -/** - * @module sign - */ - -'use strict' - -const path = require('path') -const fs = require('fs') -const semver = require('semver') - -const util = require('./util') -const debuglog = util.debuglog -const debugwarn = util.debugwarn -const getAppContentsPath = util.getAppContentsPath -const execFileAsync = util.execFileAsync -const validateOptsAppAsync = util.validateOptsAppAsync -const validateOptsPlatformAsync = util.validateOptsPlatformAsync -const walkAsync = util.walkAsync -const Identity = require('./util-identities').Identity -const findIdentitiesAsync = require('./util-identities').findIdentitiesAsync -const ProvisioningProfile = require('./util-provisioning-profiles').ProvisioningProfile -const preEmbedProvisioningProfile = require('./util-provisioning-profiles').preEmbedProvisioningProfile -const preAutoEntitlements = require('./util-entitlements').preAutoEntitlements - -const osRelease = require('os').release() - -/** - * This function returns a promise validating opts.binaries, the additional binaries to be signed along with the discovered enclosed components. - * @function - * @param {Object} opts - Options. - * @returns {Promise} Promise. - */ -function validateOptsBinariesAsync (opts) { - return new Promise(function (resolve, reject) { - if (opts.binaries) { - if (!Array.isArray(opts.binaries)) { - reject(new Error('Additional binaries should be an Array.')) - return - } - // TODO: Presence check for binary files, reject if any does not exist - } - resolve() - }) -} - -/** - * This function returns a promise validating all options passed in opts. - * @function - * @param {Object} opts - Options. - * @returns {Promise} Promise. - */ -function validateSignOptsAsync (opts) { - if (opts.ignore && !(opts.ignore instanceof Array)) { - opts.ignore = [opts.ignore] - } - - if (opts['provisioning-profile']) { - if (typeof opts['provisioning-profile'] !== 'string' && !(opts['provisioning-profile'] instanceof ProvisioningProfile)) return Promise.reject(new Error('Path to provisioning profile should be a string or a ProvisioningProfile object.')) - } - - if (opts['type']) { - if (opts['type'] !== 'development' && opts['type'] !== 'distribution') return Promise.reject(new Error('Type must be either `development` or `distribution`.')) - } else { - opts['type'] = 'distribution' - } - - return Promise.all([ - validateOptsAppAsync(opts), - validateOptsPlatformAsync(opts), - validateOptsBinariesAsync(opts), - ]) -} - -/** - * This function returns a promise verifying the code sign of application bundle. - * @function - * @param {Object} opts - Options. - * @returns {Promise} Promise resolving output. - */ -async function verifySignApplicationAsync (opts) { - // Verify with codesign - const semver = require('semver') - debuglog('Verifying application bundle with codesign...') - - await execFileAsync('codesign', [ - '--verify', - '--deep' - ] - .concat( - opts['strict-verify'] !== false && - semver.gte(osRelease, '15.0.0') >= 0 // Strict flag since darwin 15.0.0 --> OS X 10.11.0 El Capitan - ? ['--strict' + - (opts['strict-verify'] - ? '=' + opts['strict-verify'] // Array should be converted to a comma separated string - : '')] - : [], - ['--verbose=2', opts.app])) - - // Additionally test Gatekeeper acceptance for darwin platform - if (opts.platform === 'darwin' && opts['gatekeeper-assess'] !== false) { - debuglog('Verifying Gatekeeper acceptance for darwin platform...') - await execFileAsync('spctl', [ - '--assess', - '--type', 'execute', - '--verbose', - '--ignore-cache', - '--no-cache', - opts.app - ]) - } -} - -/** - * This function returns a promise codesigning only. - * @function - * @param {Object} opts - Options. - * @returns {Promise} Promise. - */ -function signApplicationAsync (opts) { - const appContentsPath = getAppContentsPath(opts) - return walkAsync(appContentsPath) - .then(async function (childPaths) { - function ignoreFilePath (opts, filePath) { - if (opts.ignore) { - return opts.ignore.some(function (ignore) { - if (typeof ignore === 'function') { - return ignore(filePath) - } - return filePath.match(ignore) - }) - } - return false - } - - if (opts.binaries) { - // Accept absolute paths for external binaries, else resolve relative paths from the artifact's app Contents path. - const userDefinedBinaries = opts.binaries.map(function (destination) { return fs.existsSync(destination) ? destination : path.resolve(appContentsPath, destination)}) - // Insert at front to prioritize signing. We still sort by depth next - childPaths = userDefinedBinaries.concat(childPaths) - debuglog('Signing addtional user-defined binaries: ' + JSON.stringify(userDefinedBinaries, null, 1)) - } - - /** - * Sort the child paths by how deep they are in the file tree. Some arcane apple - * logic expects the deeper files to be signed first otherwise strange errors get - * thrown our way - */ - childPaths = childPaths.sort((a, b) => { - const aDepth = a.split(path.sep).length - const bDepth = b.split(path.sep).length - return bDepth - aDepth - }) - - - const args = [ - '--sign', opts.identity.hash || opts.identity.name, - '--force' - ] - if (opts.keychain) { - args.push('--keychain', opts.keychain) - } - if (opts.requirements) { - args.push('--requirements', opts.requirements) - } - if (opts.timestamp) { - args.push('--timestamp=' + opts.timestamp) - } else { - args.push('--timestamp') - } - if (opts['signature-size']) { - if (Number.isInteger(opts['signature-size']) && opts['signature-size'] > 0) { - args.push('--signature-size', opts['signature-size']) - } else { - debugwarn(`Invalid value provided for --signature-size (${opts['signature-size']}). Must be a positive integer.`) - } - } - - let optionsArguments = [] - - if (opts['signature-flags']) { - if (Array.isArray(opts['signature-flags'])) { - optionsArguments = [...opts['signature-flags']] - } else { - const flags = opts['signature-flags'].split(',').map(function (flag) { return flag.trim() }) - optionsArguments = [...flags] - } - } - - if (opts.hardenedRuntime || opts['hardened-runtime'] || optionsArguments.includes('runtime')) { - // Hardened runtime since darwin 17.7.0 --> macOS 10.13.6 - if (semver.gte(osRelease, '17.7.0') >= 0) { - optionsArguments.push('runtime') - } else { - // Remove runtime if passed in with --signature-flags - debuglog('Not enabling hardened runtime, current macOS version too low, requires 10.13.6 and higher') - optionsArguments = optionsArguments.filter(function (element, index) { return element !== 'runtime' }) - } - } - - if (opts['restrict']) { - optionsArguments.push('restrict') - debugwarn('This flag is to be deprecated, consider using --signature-flags=restrict instead') - } - - if (optionsArguments.length) { - args.push('--options', [...new Set(optionsArguments)].join(',')) - } - - if (opts.entitlements) { - // Sign with entitlements - for (const filePath of childPaths) { - if (ignoreFilePath(opts, filePath)) { - debuglog('Skipped... ' + filePath) - continue - } - debuglog('Signing... ' + filePath) - let entitlementsFile = opts['entitlements-inherit'] - if (filePath.includes('Library/LoginItems')) { - entitlementsFile = opts['entitlements-loginhelper'] - } - - await execFileAsync('codesign', args.concat('--entitlements', entitlementsFile, filePath)) - } - debuglog('Signing... ' + opts.app) - await execFileAsync('codesign', args.concat('--entitlements', opts.entitlements, opts.app)) - } else { - for (const filePath of childPaths) { - if (ignoreFilePath(opts, filePath)) { - debuglog('Skipped... ' + filePath) - continue - } - - debuglog('Signing... ' + filePath) - await execFileAsync('codesign', args.concat(filePath)) - } - - debuglog('Signing... ' + opts.app) - await execFileAsync('codesign', args.concat(opts.app)) - } - - // Verify code sign - debuglog('Verifying...') - await verifySignApplicationAsync(opts) - debuglog('Verified.') - - // Check entitlements if applicable - if (opts.entitlements) { - debuglog('Displaying entitlements...') - const result = await execFileAsync('codesign', [ - '--display', - '--entitlements', ':-', // Write to standard output and strip off the blob header - opts.app - ]) - debuglog('Entitlements:', '\n', result) - } - }) -} - -/** - * This function returns a promise signing the application. - * @function - * @param {mixed} opts - Options. - * @returns {Promise} Promise. - */ -const signAsync = module.exports.signAsync = function (opts) { - return validateSignOptsAsync(opts) - .then(function () { - // Determine identity for signing - let promise - if (opts.identity) { - debuglog('`identity` passed in arguments.') - if (opts['identity-validation'] === false) { - if (!(opts.identity instanceof Identity)) { - opts.identity = new Identity(opts.identity) - } - return Promise.resolve() - } - promise = findIdentitiesAsync(opts, opts.identity) - } else { - debugwarn('No `identity` passed in arguments...') - if (opts.platform === 'mas') { - if (opts.type === 'distribution') { - debuglog('Finding `3rd Party Mac Developer Application` certificate for signing app distribution in the Mac App Store...') - promise = findIdentitiesAsync(opts, '3rd Party Mac Developer Application:') - } else { - debuglog('Finding `Mac Developer` certificate for signing app in development for the Mac App Store signing...') - promise = findIdentitiesAsync(opts, 'Mac Developer:') - } - } else { - debuglog('Finding `Developer ID Application` certificate for distribution outside the Mac App Store...') - promise = findIdentitiesAsync(opts, 'Developer ID Application:') - } - } - return promise - .then(function (identities) { - if (identities.length > 0) { - // Identity(/ies) found - if (identities.length > 1) { - debugwarn('Multiple identities found, will use the first discovered.') - } else { - debuglog('Found 1 identity.') - } - opts.identity = identities[0] - } else { - // No identity found - return Promise.reject(new Error('No identity found for signing.')) - } - }) - }) - .then(function () { - // Determine entitlements for code signing - let filePath - if (opts.platform === 'mas') { - // To sign apps for Mac App Store, an entitlements file is required, especially for app sandboxing (as well some other services). - // Fallback entitlements for sandboxing by default: Note this may cause troubles while running an signed app due to missing keys special to the project. - // Further reading: https://developer.apple.com/library/mac/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html - if (!opts.entitlements) { - filePath = path.join(__dirname, 'default.entitlements.mas.plist') - debugwarn('No `entitlements` passed in arguments:', '\n', - '* Sandbox entitlements are required for Mac App Store distribution, your codesign entitlements file is default to:', filePath) - opts.entitlements = filePath - } - if (!opts['entitlements-inherit']) { - filePath = path.join(__dirname, 'default.entitlements.mas.inherit.plist') - debugwarn('No `entitlements-inherit` passed in arguments:', '\n', - '* Sandbox entitlements file for enclosing app files is default to:', filePath) - opts['entitlements-inherit'] = filePath - } - } else { - // Not necessary to have entitlements for non Mac App Store distribution - if (!opts.entitlements) { - debugwarn('No `entitlements` passed in arguments:', '\n', - '* Provide `entitlements` to specify entitlements file for codesign.') - } else { - // If entitlements is provided as a flag, fallback to default - if (opts.entitlements === true) { - filePath = path.join(__dirname, 'default.entitlements.darwin.plist') - debugwarn('`entitlements` not specified in arguments:', '\n', - '* Provide `entitlements` to specify entitlements file for codesign.', '\n', - '* Sandbox entitlements file for enclosing app files is default to:', filePath) - opts.entitlements = filePath - } - if (!opts['entitlements-inherit']) { - filePath = path.join(__dirname, 'default.entitlements.darwin.inherit.plist') - debugwarn('No `entitlements-inherit` passed in arguments:', '\n', - '* Sandbox entitlements file for enclosing app files is default to:', filePath) - opts['entitlements-inherit'] = filePath - } - } - } - if (!opts['entitlements-loginhelper']) { - filePath = opts.entitlements - debugwarn('No `entitlements-loginhelper` passed in arguments:', '\n', - '* Sandbox entitlements file for login helper is default to:', filePath) - opts['entitlements-loginhelper'] = filePath - } - }) - .then(async function () { - // Pre-sign operations - const preSignOperations = [] - - if (opts['pre-embed-provisioning-profile'] === false) { - debugwarn('Pre-sign operation disabled for provisioning profile embedding:', '\n', - '* Enable by setting `pre-embed-provisioning-profile` to `true`.') - } else { - debuglog('Pre-sign operation enabled for provisioning profile:', '\n', - '* Disable by setting `pre-embed-provisioning-profile` to `false`.') - preSignOperations.push(preEmbedProvisioningProfile) - } - - if (opts['pre-auto-entitlements'] === false) { - debugwarn('Pre-sign operation disabled for entitlements automation.') - } else { - debuglog('Pre-sign operation enabled for entitlements automation with versions >= `1.1.1`:', '\n', - '* Disable by setting `pre-auto-entitlements` to `false`.') - if (opts.entitlements && (!opts.version || semver.gte(opts.version, '1.1.1') >= 0)) { - // Enable Mac App Store sandboxing without using temporary-exception, introduced in Electron v1.1.1. Relates to electron#5601 - preSignOperations.push(preAutoEntitlements) - } - } - - for (const preSignOperation of preSignOperations) { - await preSignOperation(opts) - } - }) - .then(function () { - debuglog('Signing application...', '\n', - '> Application:', opts.app, '\n', - '> Platform:', opts.platform, '\n', - '> Entitlements:', opts.entitlements, '\n', - '> Child entitlements:', opts['entitlements-inherit'], '\n', - '> Additional binaries:', opts.binaries, '\n', - '> Identity:', opts.identity) - return signApplicationAsync(opts) - }) - .then(function () { - // Post-sign operations - debuglog('Application signed.') - }) -} - -/** - * This function is a normal callback implementation. - * @function - * @param {Object} opts - Options. - * @param {RequestCallback} cb - Callback. - */ -module.exports.sign = function (opts, cb) { - signAsync(opts) - .then(function () { - debuglog('Application signed: ' + opts.app) - if (cb) cb() - }) - .catch(function (err) { - debuglog('Sign failed:') - if (err.message) debuglog(err.message) - else if (err.stack) debuglog(err.stack) - else debuglog(err) - if (cb) cb(err) - }) -} diff --git a/packages/app-builder-lib/electron-osx-sign/util-entitlements.js b/packages/app-builder-lib/electron-osx-sign/util-entitlements.js deleted file mode 100644 index 192ad916f21..00000000000 --- a/packages/app-builder-lib/electron-osx-sign/util-entitlements.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * @module util-entitlements - */ - -'use strict' - -const { executeAppBuilderAsJson, executeAppBuilderAndWriteJson } = require("../out/util/appBuilder") - -const os = require('os') -const path = require('path') - -const util = require('./util') -const debuglog = util.debuglog -const getAppContentsPath = util.getAppContentsPath - -let tmpFileCounter = 0 - -/** - * This function returns a promise completing the entitlements automation: The process includes checking in `Info.plist` for `ElectronTeamID` or setting parsed value from identity, and checking in entitlements file for `com.apple.security.application-groups` or inserting new into array. A temporary entitlements file may be created to replace the input for any changes introduced. - * @function - * @param {Object} opts - Options. - * @returns {Promise} Promise. - */ -async function preAutoEntitlements(opts) { - // If entitlements file not provided, default will be used. Fixes #41 - const appInfoPath = path.join(getAppContentsPath(opts), 'Info.plist') - - debuglog('Automating entitlement app group...', '\n', - '> Info.plist:', appInfoPath, '\n', - '> Entitlements:', opts.entitlements) - - const plistContent = await executeAppBuilderAsJson(["decode-plist", "-f", opts.entitlements, "-f", appInfoPath]) - let entitlements = plistContent[0] - if (!entitlements['com.apple.security.app-sandbox']) { - // Only automate when app sandbox enabled by user - return - } - - const appInfo = plistContent[1] - - // Use ElectronTeamID in Info.plist if already specified - if (appInfo.ElectronTeamID) { - debuglog('`ElectronTeamID` found in `Info.plist`: ' + appInfo.ElectronTeamID) - } else { - // The team identifier in signing identity should not be trusted - if (opts['provisioning-profile']) { - appInfo.ElectronTeamID = opts['provisioning-profile'].message.Entitlements['com.apple.developer.team-identifier'] - debuglog('`ElectronTeamID` not found in `Info.plist`, use parsed from provisioning profile: ' + appInfo.ElectronTeamID) - } else { - appInfo.ElectronTeamID = opts.identity.name.substring(opts.identity.name.indexOf('(') + 1, opts.identity.name.lastIndexOf(')')) - debuglog('`ElectronTeamID` not found in `Info.plist`, use parsed from signing identity: ' + appInfo.ElectronTeamID) - } - await executeAppBuilderAndWriteJson(["encode-plist"], {[appInfoPath]: appInfo}) - debuglog('`Info.plist` updated:', '\n', '> Info.plist:', appInfoPath) - } - - const appIdentifier = appInfo.ElectronTeamID + '.' + appInfo.CFBundleIdentifier - // Insert application identifier if not exists - if (entitlements['com.apple.application-identifier']) { - debuglog('`com.apple.application-identifier` found in entitlements file: ' + entitlements['com.apple.application-identifier']) - } else { - debuglog('`com.apple.application-identifier` not found in entitlements file, new inserted: ' + appIdentifier) - entitlements['com.apple.application-identifier'] = appIdentifier - } - // Insert developer team identifier if not exists - if (entitlements['com.apple.developer.team-identifier']) { - debuglog('`com.apple.developer.team-identifier` found in entitlements file: ' + entitlements['com.apple.developer.team-identifier']) - } else { - debuglog('`com.apple.developer.team-identifier` not found in entitlements file, new inserted: ' + appInfo.ElectronTeamID) - entitlements['com.apple.developer.team-identifier'] = appInfo.ElectronTeamID - } - // Init entitlements app group key to array if not exists - if (!entitlements['com.apple.security.application-groups']) { - entitlements['com.apple.security.application-groups'] = [] - } - // Insert app group if not exists - if (Array.isArray(entitlements['com.apple.security.application-groups']) && entitlements['com.apple.security.application-groups'].indexOf(appIdentifier) === -1) { - debuglog('`com.apple.security.application-groups` not found in entitlements file, new inserted: ' + appIdentifier) - entitlements['com.apple.security.application-groups'].push(appIdentifier) - } else { - debuglog('`com.apple.security.application-groups` found in entitlements file: ' + appIdentifier) - } - - // Create temporary entitlements file - const entitlementsPath = path.join(os.tmpdir(), `tmp-entitlements-${process.pid.toString(16)}-${(tmpFileCounter++).toString(16)}.plist`) - opts.entitlements = entitlementsPath - await executeAppBuilderAndWriteJson(["encode-plist"], {[entitlementsPath]: entitlements}) - debuglog('Entitlements file updated:', '\n', '> Entitlements:', entitlementsPath) -} - -module.exports.preAutoEntitlements = preAutoEntitlements \ No newline at end of file diff --git a/packages/app-builder-lib/electron-osx-sign/util-identities.js b/packages/app-builder-lib/electron-osx-sign/util-identities.js deleted file mode 100644 index 37cfbdba58a..00000000000 --- a/packages/app-builder-lib/electron-osx-sign/util-identities.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @module util-identities - */ - -'use strict' - -const util = require('./util') -const debuglog = util.debuglog -const flatList = util.flatList -const execFileAsync = util.execFileAsync - -/** - * @constructor - * @param {string} name - Name of the signing identity. - * @param {String} hash - SHA-1 hash of the identity. - */ -var Identity = module.exports.Identity = function (name, hash) { - this.name = name - this.hash = hash -} - -/** - * This function returns a promise checking the indentity proposed and updates the identity option to a exact finding from results. - * @function - * @param {Object} opts - Options. - * @param {string} identity - The proposed identity. - * @returns {Promise} Promise. - */ -module.exports.findIdentitiesAsync = function (opts, identity) { - // Only to look for valid identities, excluding those flagged with - // CSSMERR_TP_CERT_EXPIRED or CSSMERR_TP_NOT_TRUSTED. Fixes #9 - - var args = [ - 'find-identity', - '-v' - ] - if (opts.keychain) { - args.push(opts.keychain) - } - - return execFileAsync('security', args) - .then(function (result) { - return result.split('\n').map(function (line) { - if (line.indexOf(identity) >= 0) { - var identityFound = line.substring(line.indexOf('"') + 1, line.lastIndexOf('"')) - var identityHashFound = line.substring(line.indexOf(')') + 2, line.indexOf('"') - 1) - debuglog('Identity:', '\n', - '> Name:', identityFound, '\n', - '> Hash:', identityHashFound) - return new Identity(identityFound, identityHashFound) - } - }) - }) - .then(flatList) -} diff --git a/packages/app-builder-lib/electron-osx-sign/util-provisioning-profiles.js b/packages/app-builder-lib/electron-osx-sign/util-provisioning-profiles.js deleted file mode 100644 index f68f24e189f..00000000000 --- a/packages/app-builder-lib/electron-osx-sign/util-provisioning-profiles.js +++ /dev/null @@ -1,178 +0,0 @@ -/** - * @module util-provisioning-profiles - */ - -'use strict' - -const path = require('path') -const fs = require("fs-extra") -const os = require('os') -const { executeAppBuilderAsJson } = require("../out/util/appBuilder") - -const util = require('./util') -const debuglog = util.debuglog -const debugwarn = util.debugwarn -const getAppContentsPath = util.getAppContentsPath -const copyFileAsync = util.copyFileAsync -const execFileAsync = util.execFileAsync - -/** - * @constructor - * @param {string} filePath - Path to provisioning profile. - * @param {Object} message - Decoded message in provisioning profile. - */ -let ProvisioningProfile = module.exports.ProvisioningProfile = function (filePath, message) { - this.filePath = filePath - this.message = message -} - -Object.defineProperty(ProvisioningProfile.prototype, 'name', { - get: function () { - return this.message['Name'] - } -}) - -Object.defineProperty(ProvisioningProfile.prototype, 'platforms', { - get: function () { - if ('ProvisionsAllDevices' in this.message) return ['darwin'] // Developer ID - else if (this.type === 'distribution') return ['mas'] // Mac App Store - else return ['darwin', 'mas'] // Mac App Development - } -}) - -Object.defineProperty(ProvisioningProfile.prototype, 'type', { - get: function () { - if ('ProvisionedDevices' in this.message) return 'development' // Mac App Development - else return 'distribution' // Developer ID or Mac App Store - } -}) - -/** - * Returns a promise resolving to a ProvisioningProfile instance based on file. - * @function - * @param {string} filePath - Path to provisioning profile. - * @param {string} keychain - Keychain to use when unlocking provisioning profile. - * @returns {Promise} Promise. - */ -function getProvisioningProfileAsync(filePath, keychain) { - const securityArgs = [ - 'cms', - '-D', // Decode a CMS message - '-i', filePath // Use infile as source of data - ] - - if (keychain != null) { - securityArgs.push('-k', keychain) - } - - return util.execFileAsync('security', securityArgs) - .then(async function (result) { - // make filename unique so it doesn't create issues with parallel method calls - const timestamp = process.hrtime.bigint - ? process.hrtime.bigint().toString() - : process.hrtime().join('') - - // todo read directly - const tempFile = path.join(os.tmpdir(), `${require('crypto').createHash('sha1').update(filePath).update(timestamp).digest('hex')}.plist`) - await fs.outputFile(tempFile, result) - const plistContent = await executeAppBuilderAsJson(["decode-plist", "-f", tempFile]) - await fs.unlink(tempFile) - const provisioningProfile = new ProvisioningProfile(filePath, plistContent[0]) - debuglog('Provisioning profile:', '\n', - '> Name:', provisioningProfile.name, '\n', - '> Platforms:', provisioningProfile.platforms, '\n', - '> Type:', provisioningProfile.type, '\n', - '> Path:', provisioningProfile.filePath, '\n', - '> Message:', provisioningProfile.message) - return provisioningProfile - }) -} - -/** - * Returns a promise resolving to a list of suitable provisioning profile within the current working directory. - * @function - * @param {Object} opts - Options. - * @returns {Promise} Promise. - */ -async function findProvisioningProfilesAsync(opts) { - const dirPath = process.cwd() - const dirContent = await Promise.all((await fs.readdir(dirPath)) - .filter(it => it.endsWith(".provisionprofile")) - .map(async function (name) { - const filePath = path.join(dirPath, name) - const stat = await fs.lstat(filePath) - return stat.isFile() ? filePath : undefined - })) - return util.flatList(await Promise.all(util.flatList(dirContent).map(filePath => { - return getProvisioningProfileAsync(filePath) - .then((provisioningProfile) => { - if (provisioningProfile.platforms.indexOf(opts.platform) >= 0 && provisioningProfile.type === opts.type) { - return provisioningProfile - } - debugwarn('Provisioning profile above ignored, not for ' + opts.platform + ' ' + opts.type + '.') - return undefined - }) - }))) -} - -/** - * Returns a promise embedding the provisioning profile in the app Contents folder. - * @function - * @param {Object} opts - Options. - * @returns {Promise} Promise. - */ -module.exports.preEmbedProvisioningProfile = function (opts) { - async function embedProvisioningProfile () { - if (!opts['provisioning-profile']) { - return - } - - debuglog('Looking for existing provisioning profile...') - let embeddedFilePath = path.join(getAppContentsPath(opts), 'embedded.provisionprofile') - try { - await fs.lstat(embeddedFilePath) - debuglog('Found embedded provisioning profile:', '\n', - '* Please manually remove the existing file if not wanted.', '\n', - '* Current file at:', embeddedFilePath) - } catch (err) { - if (err.code === 'ENOENT') { - // File does not exist - debuglog('Embedding provisioning profile...') - return copyFileAsync(opts['provisioning-profile'].filePath, embeddedFilePath) - } else throw err - } - } - - if (opts['provisioning-profile']) { - // User input provisioning profile - debuglog('`provisioning-profile` passed in arguments.') - if (opts['provisioning-profile'] instanceof ProvisioningProfile) { - return embedProvisioningProfile() - } else { - return getProvisioningProfileAsync(opts['provisioning-profile'], opts['keychain']) - .then(function (provisioningProfile) { - opts['provisioning-profile'] = provisioningProfile - }) - .then(embedProvisioningProfile) - } - } else { - // Discover provisioning profile - debuglog('No `provisioning-profile` passed in arguments, will find in current working directory and in user library...') - return findProvisioningProfilesAsync(opts) - .then(function (provisioningProfiles) { - if (provisioningProfiles.length > 0) { - // Provisioning profile(s) found - if (provisioningProfiles.length > 1) { - debuglog('Multiple provisioning profiles found, will use the first discovered.') - } else { - debuglog('Found 1 provisioning profile.') - } - opts['provisioning-profile'] = provisioningProfiles[0] - } else { - // No provisioning profile found - debuglog('No provisioning profile found, will not embed profile in app contents.') - } - }) - .then(embedProvisioningProfile) - } -} diff --git a/packages/app-builder-lib/electron-osx-sign/util.js b/packages/app-builder-lib/electron-osx-sign/util.js deleted file mode 100644 index 6eaff5978d6..00000000000 --- a/packages/app-builder-lib/electron-osx-sign/util.js +++ /dev/null @@ -1,243 +0,0 @@ -/** - * @module util - */ - -'use strict' - -const child = require('child_process') -const fs = require('fs-extra') -const path = require('path') - -const debug = require('debug') - -/** - * This callback is used across signing and flattening. - * @callback RequestCallback - * @param {?Error} err - */ - -/** @function */ -const debuglog = module.exports.debuglog = debug('electron-osx-sign') -debuglog.log = console.log.bind(console) - -/** @function */ -const debugwarn = module.exports.debugwarn = debug('electron-osx-sign:warn') -debugwarn.log = console.warn.bind(console) - -/** @function */ -const removePassword = function (input) { - return input.replace(/(-P |pass:|\/p|-pass )([^ ]+)/, function (match, p1, p2) { - return `${p1}***` - }) -} - -/** @function */ -module.exports.execFileAsync = function (file, args, options) { - if (debuglog.enabled) { - debuglog('Executing...', file, args && Array.isArray(args) ? removePassword(args.join(' ')) : '') - } - - return new Promise(function (resolve, reject) { - child.execFile(file, args, options, function (err, stdout, stderr) { - if (err) { - debuglog('Error executing file:', '\n', - '> Stdout:', stdout, '\n', - '> Stderr:', stderr) - reject(err) - return - } - resolve(stdout) - }) - }) -} - -/** - * This function returns a flattened list of elements from an array of lists. - * @function - * @param {*} list - List. - * @returns Flattened list. - */ -module.exports.flatList = function (list) { - function populateResult(list) { - if (!Array.isArray(list)) { - result.push(list) - } else if (list.length > 0) { - for (let item of list) if (item) populateResult(item) - } - } - - let result = [] - populateResult(list) - return result -} - -/** - * This function returns the path to app contents. - * @function - * @param {Object} opts - Options. - * @returns {string} App contents path. - */ -var getAppContentsPath = module.exports.getAppContentsPath = function (opts) { - return path.join(opts.app, 'Contents') -} - -/** - * This function returns the path to app frameworks within contents. - * @function - * @param {Object} opts - Options. - * @returns {string} App frameworks path. - */ -var getAppFrameworksPath = module.exports.getAppFrameworksPath = function (opts) { - return path.join(getAppContentsPath(opts), 'Frameworks') -} - -/** - * This function returns a promise copying a file from the source to the target. - * @function - * @param {string} source - Source path. - * @param {string} target - Target path. - * @returns {Promise} Promise. - */ -module.exports.copyFileAsync = function (source, target) { - debuglog('Copying file...', '\n', - '> Source:', source, '\n', - '> Target:', target) - return new Promise(function (resolve, reject) { - var readStream = fs.createReadStream(source) - readStream.on('error', reject) - var writeStream = fs.createWriteStream(target) - writeStream.on('error', reject) - writeStream.on('close', resolve) - readStream.pipe(writeStream) - }) -} - -/** - * This function returns a promise with platform resolved. - * @function - * @param {Object} opts - Options. - * @returns {Promise} Promise resolving platform. - */ -var detectElectronPlatformAsync = module.exports.detectElectronPlatformAsync = function (opts) { - return new Promise(function (resolve) { - var appFrameworksPath = getAppFrameworksPath(opts) - // The presence of Squirrel.framework identifies a Mac App Store build as used in https://github.com/atom/electron/blob/master/docs/tutorial/mac-app-store-submission-guide.md - return fs.lstat(path.join(appFrameworksPath, 'Squirrel.framework')) - .then(function () { - resolve('darwin') - }) - .catch(function () { - resolve('mas') - }) - }) -} - -const isBinaryFile = require("isbinaryfile").isBinaryFileSync; - -/** - * This function returns a promise resolving the file path if file binary. We check filepath extension first to reduce overhead of reading the file to a buffer for it to scan start/mid/end encoding of the file - * @function - * @param {string} filePath - Path to file. - * @returns {Promise} Promise resolving file path or undefined. - */ -const getFilePathIfBinarySync = module.exports.getFilePathIfBinarySync = function (filePath) { - const forceAllowedExts = [ - '.dylib', // Dynamic library - '.node' // Native node addon - ] - const name = path.basename(filePath) - const ext = path.extname(filePath) - - // Sometimes .node is the base name, not as a file extension - // https://github.com/electron/electron-osx-sign/pull/169 - if (forceAllowedExts.includes(ext) || forceAllowedExts.includes(name)) { - return filePath - } - // Still consider the file as binary if extension is empty or seems invalid - // https://github.com/electron-userland/electron-builder/issues/5465 - if ((ext === '' || ext.indexOf(' ') >= 0) && name[0] !== '.') { - return isBinaryFile(filePath) ? filePath : undefined - } - return undefined -} - -/** - * This function returns a promise validating opts.app, the application to be signed or flattened. - * @function - * @param {Object} opts - Options. - * @returns {Promise} Promise. - */ -module.exports.validateOptsAppAsync = async function (opts) { - if (!opts.app) { - throw new Error('Path to aplication must be specified.') - } - if (path.extname(opts.app) !== '.app') { - throw new Error('Extension of application must be `.app`.') - } - await fs.lstat(opts.app) -} - -/** - * This function returns a promise validating opts.platform, the platform of Electron build. It allows auto-discovery if no opts.platform is specified. - * @function - * @param {Object} opts - Options. - * @returns {Promise} Promise. - */ -module.exports.validateOptsPlatformAsync = function (opts) { - if (opts.platform) { - if (opts.platform === 'mas' || opts.platform === 'darwin') { - return Promise.resolve() - } else { - debugwarn('`platform` passed in arguments not supported, checking Electron platform...') - } - } else { - debugwarn('No `platform` passed in arguments, checking Electron platform...') - } - - return detectElectronPlatformAsync(opts) - .then(function (platform) { - opts.platform = platform - }) -} - -/** - * This function returns a promise resolving all child paths within the directory specified. - * @function - * @param {string} dirPath - Path to directory. - * @returns {Promise} Promise resolving child paths needing signing in order. - */ -module.exports.walkAsync = async function (dirPath) { - debuglog('Walking... ' + dirPath) - - async function _walkAsync(dirPath) { - const names = await fs.readdir(dirPath) - return await Promise.all(names.map(async (name) => { - let filePath = path.join(dirPath, name) - const stat = await fs.lstat(filePath) - if (stat.isFile()) { - const forceRemoveExts = [ - '.cstemp' // Temporary file generated from past codesign - ] - if (forceRemoveExts.includes(path.extname(filePath))) { - debuglog('Removing... ' + filePath) - await fs.unlink(filePath) - return - } - const binaryPath = getFilePathIfBinarySync(filePath) - if (binaryPath) { - return binaryPath - } - } else if (stat.isDirectory() && !stat.isSymbolicLink()) { - const result = await _walkAsync(filePath) - switch (path.extname(filePath)) { - case '.app': // Application - case '.framework': // Framework - result.push(filePath) - } - return result - } - })) - } - - return module.exports.flatList(await _walkAsync(dirPath)) -} diff --git a/packages/app-builder-lib/package.json b/packages/app-builder-lib/package.json index 66c14c5497f..0851af7c3b2 100644 --- a/packages/app-builder-lib/package.json +++ b/packages/app-builder-lib/package.json @@ -58,6 +58,7 @@ "chromium-pickle-js": "^0.2.0", "debug": "^4.3.2", "ejs": "^3.1.6", + "electron-osx-sign": "^0.5.0", "electron-publish": "workspace:*", "fs-extra": "^10.0.0", "hosted-git-info": "^4.0.2", diff --git a/packages/app-builder-lib/src/asar/unpackDetector.ts b/packages/app-builder-lib/src/asar/unpackDetector.ts index 67ca78ad194..af929f609d8 100644 --- a/packages/app-builder-lib/src/asar/unpackDetector.ts +++ b/packages/app-builder-lib/src/asar/unpackDetector.ts @@ -2,10 +2,10 @@ import BluebirdPromise from "bluebird-lst" import { log } from "builder-util" import { CONCURRENCY } from "builder-util/out/fs" import { mkdir } from "fs-extra" +import { isBinaryFileSync } from "isbinaryfile" import * as path from "path" import { NODE_MODULES_PATTERN } from "../fileTransformer" import { getDestinationPath, ResolvedFileSet } from "../util/appFileCopier" -import { getFilePathIfBinarySync } from "../../electron-osx-sign" function addValue(map: Map>, key: string, value: string) { let list = map.get(key) @@ -83,7 +83,7 @@ export async function detectUnpackedDirs(fileSet: ResolvedFileSet, autoUnpackDir if (moduleName === "ffprobe-static" || moduleName === "ffmpeg-static" || isLibOrExe(file)) { shouldUnpack = true } else if (!file.includes(".", nextSlashIndex)) { - shouldUnpack = !!getFilePathIfBinarySync(file) + shouldUnpack = !!isBinaryFileSync(file) } if (!shouldUnpack) { diff --git a/packages/app-builder-lib/src/codeSign/macCodeSign.ts b/packages/app-builder-lib/src/codeSign/macCodeSign.ts index a4448f9890c..ced91f3b4f5 100644 --- a/packages/app-builder-lib/src/codeSign/macCodeSign.ts +++ b/packages/app-builder-lib/src/codeSign/macCodeSign.ts @@ -312,7 +312,7 @@ export declare class Identity { constructor(name: string, hash: string) } -const _Identity = require("../../electron-osx-sign/util-identities").Identity +const _Identity = require("electron-osx-sign/util-identities").Identity function parseIdentity(line: string): Identity { const firstQuoteIndex = line.indexOf('"') diff --git a/packages/app-builder-lib/src/macPackager.ts b/packages/app-builder-lib/src/macPackager.ts index 5da69d6ee01..6180ec0f88f 100644 --- a/packages/app-builder-lib/src/macPackager.ts +++ b/packages/app-builder-lib/src/macPackager.ts @@ -1,10 +1,10 @@ import BluebirdPromise from "bluebird-lst" import { deepAssign, Arch, AsyncTaskManager, exec, InvalidConfigurationError, log, use, getArchSuffix } from "builder-util" -import { signAsync, SignOptions } from "../electron-osx-sign" +import { signAsync, SignOptions } from "electron-osx-sign" import { mkdir, readdir } from "fs/promises" import { Lazy } from "lazy-val" import * as path from "path" -import { copyFile, unlinkIfExists } from "builder-util/out/fs" +import { copyFile, statOrNull, unlinkIfExists } from "builder-util/out/fs" import { orIfFileNotExist } from "builder-util/out/promise" import { AppInfo } from "./appInfo" import { CertType, CodeSigningInfo, createKeychain, findIdentity, Identity, isSignAllowed, removeKeychain, reportError } from "./codeSign/macCodeSign" @@ -238,6 +238,20 @@ export default class MacPackager extends PlatformPackager { const filterRe = filter == null ? null : filter.map(it => new RegExp(it)) + let binaries = options.binaries || undefined + if (binaries) { + // Accept absolute paths for external binaries, else resolve relative paths from the artifact's app Contents path. + const userDefinedBinaries = await Promise.all(binaries.map(async (destination) => { + if (await statOrNull(destination)) { + return destination + } + return path.resolve(appPath, destination) + })) + // Insert at front to prioritize signing. We still sort by depth next + binaries = userDefinedBinaries.concat(binaries) + log.info('Signing addtional user-defined binaries: ' + JSON.stringify(userDefinedBinaries, null, 1)) + } + const signOptions: any = { "identity-validation": false, // https://github.com/electron-userland/electron-builder/issues/1699 @@ -269,7 +283,7 @@ export default class MacPackager extends PlatformPackager { version: this.config.electronVersion, app: appPath, keychain: keychainFile || undefined, - binaries: options.binaries || undefined, + binaries, requirements: isMas || this.platformSpecificBuildOptions.requirements == null ? undefined : await this.getResource(this.platformSpecificBuildOptions.requirements), // https://github.com/electron-userland/electron-osx-sign/issues/196 // will fail on 10.14.5+ because a signed but unnotarized app is also rejected. diff --git a/packages/electron-updater/src/windowsExecutableCodeSignatureVerifier.ts b/packages/electron-updater/src/windowsExecutableCodeSignatureVerifier.ts index f02b1ec7d13..3d87759af17 100644 --- a/packages/electron-updater/src/windowsExecutableCodeSignatureVerifier.ts +++ b/packages/electron-updater/src/windowsExecutableCodeSignatureVerifier.ts @@ -101,8 +101,7 @@ function handleError(logger: Logger, error: Error | null, stderr: string | null) } try { - // @ts-ignore - execFileSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", "ConvertTo-Json test"], { timeout: 10 * 1000 }) + execFileSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", "ConvertTo-Json test"], { timeout: 10 * 1000 } as any) } catch (testError) { logger.warn( `Cannot execute ConvertTo-Json: ${testError.message}. Ignoring signature validation due to unsupported powershell version. Please upgrade to powershell 3 or higher.` diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e6f4c1d9313..72b7be64650 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,6 +88,7 @@ importers: dmg-builder: workspace:* ejs: ^3.1.6 electron-builder-squirrel-windows: workspace:* + electron-osx-sign: ^0.5.0 electron-publish: workspace:* fs-extra: ^10.0.0 hosted-git-info: ^4.0.2 @@ -112,6 +113,7 @@ importers: chromium-pickle-js: 0.2.0 debug: 4.3.2 ejs: 3.1.6 + electron-osx-sign: 0.5.0 electron-publish: link:../electron-publish fs-extra: 10.0.0 hosted-git-info: 4.0.2 @@ -379,6 +381,7 @@ importers: dmg-builder: workspace:* electron-builder: workspace:* electron-builder-squirrel-windows: workspace:* + electron-osx-sign: ^0.5.0 electron-publish: workspace:* electron-updater: workspace:* esbuild: ^0.12.14 @@ -405,6 +408,7 @@ importers: dmg-builder: link:../packages/dmg-builder electron-builder: link:../packages/electron-builder electron-builder-squirrel-windows: link:../packages/electron-builder-squirrel-windows + electron-osx-sign: 0.5.0 electron-publish: link:../packages/electron-publish electron-updater: link:../packages/electron-updater fs-extra: 10.0.0 @@ -3312,6 +3316,17 @@ packages: dependencies: node-int64: 0.4.0 + /buffer-alloc-unsafe/1.1.0: + resolution: {integrity: sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==} + dev: false + + /buffer-alloc/1.2.0: + resolution: {integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==} + dependencies: + buffer-alloc-unsafe: 1.1.0 + buffer-fill: 1.0.0 + dev: false + /buffer-crc32/0.2.13: resolution: {integrity: sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=} dev: false @@ -3321,6 +3336,10 @@ packages: engines: {node: '>=0.4.0'} dev: false + /buffer-fill/1.0.0: + resolution: {integrity: sha1-+PeLdniYiO858gXNY39o5wISKyw=} + dev: false + /buffer-from/1.1.1: resolution: {integrity: sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==} @@ -3632,6 +3651,11 @@ packages: engines: {node: '>=8'} dev: true + /compare-version/0.1.2: + resolution: {integrity: sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA=} + engines: {node: '>=0.10.0'} + dev: false + /component-emitter/1.3.0: resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} dev: true @@ -3792,7 +3816,6 @@ packages: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} dependencies: ms: 2.0.0 - dev: true /debug/4.3.1: resolution: {integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==} @@ -4044,6 +4067,19 @@ packages: jake: 10.8.2 dev: false + /electron-osx-sign/0.5.0: + resolution: {integrity: sha512-icoRLHzFz/qxzDh/N4Pi2z4yVHurlsCAYQvsCSG7fCedJ4UJXBS6PoQyGH71IfcqKupcKeK7HX/NkyfG+v6vlQ==} + engines: {node: '>=4.0.0'} + hasBin: true + dependencies: + bluebird: 3.7.2 + compare-version: 0.1.2 + debug: 2.6.9 + isbinaryfile: 3.0.3 + minimist: 1.2.5 + plist: 3.0.2 + dev: false + /electron-to-chromium/1.3.766: resolution: {integrity: sha512-u2quJ862q9reRKh/je3GXis3w38+RoXH1J9N3XjtsS6NzmUAosNsyZgUVFZPN/ZlJ3v6T0rTyZR3q/J5c6Sy5w==} @@ -5097,6 +5133,13 @@ packages: /isarray/1.0.0: resolution: {integrity: sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=} + /isbinaryfile/3.0.3: + resolution: {integrity: sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==} + engines: {node: '>=0.6.0'} + dependencies: + buffer-alloc: 1.2.0 + dev: false + /isbinaryfile/4.0.8: resolution: {integrity: sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==} engines: {node: '>= 8.0.0'} @@ -6281,7 +6324,6 @@ packages: /ms/2.0.0: resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} - dev: true /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} diff --git a/test/package.json b/test/package.json index b06520f42f4..a91620a3e8d 100644 --- a/test/package.json +++ b/test/package.json @@ -14,6 +14,7 @@ "dmg-builder": "workspace:*", "electron-builder": "workspace:*", "electron-builder-squirrel-windows": "workspace:*", + "electron-osx-sign": "^0.5.0", "electron-publish": "workspace:*", "electron-updater": "workspace:*", "fs-extra": "^10.0.0", diff --git a/test/src/helpers/CheckingPackager.ts b/test/src/helpers/CheckingPackager.ts index bb7c5b10035..ba1fb7ee169 100644 --- a/test/src/helpers/CheckingPackager.ts +++ b/test/src/helpers/CheckingPackager.ts @@ -5,7 +5,7 @@ import { Identity } from "app-builder-lib/out/codeSign/macCodeSign" import MacPackager from "app-builder-lib/out/macPackager" import { DmgTarget } from "dmg-builder" import { WinPackager } from "app-builder-lib/out/winPackager" -import { SignOptions as MacSignOptions } from "app-builder-lib/electron-osx-sign" +import { SignOptions as MacSignOptions } from "electron-osx-sign" export class CheckingWinPackager extends WinPackager { effectiveDistOptions: any