Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support identity object #272

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 20 additions & 11 deletions src/flat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { debugLog, debugWarn, execFileAsync, validateOptsApp, validateOptsPlatfo
import { Identity, findIdentities } from './util-identities';

import { FlatOptions, ValidatedFlatOptions } from './types';
import { isIdentity } from './isIdentity';

const pkgVersion = require('../../package.json').version as string;

Expand All @@ -13,7 +14,7 @@ const pkgVersion = require('../../package.json').version as string;
* @param {Object} opts - Options.
* @returns {Promise} Promise.
*/
async function validateFlatOpts (opts: FlatOptions): Promise<ValidatedFlatOptions> {
async function validateFlatOpts(opts: FlatOptions): Promise<ValidatedFlatOptions> {
await validateOptsApp(opts);

let pkg = opts.pkg;
Expand All @@ -24,7 +25,7 @@ async function validateFlatOpts (opts: FlatOptions): Promise<ValidatedFlatOption
}
} else {
debugWarn(
'No `pkg` passed in arguments, will fallback to default inferred from the given application.'
'No `pkg` passed in arguments, will fallback to default inferred from the given application.',
);
pkg = path.join(path.dirname(opts.app), path.basename(opts.app, '.app') + '.pkg');
}
Expand All @@ -43,7 +44,7 @@ async function validateFlatOpts (opts: FlatOptions): Promise<ValidatedFlatOption
...opts,
pkg,
install,
platform: await validateOptsPlatform(opts)
platform: await validateOptsPlatform(opts),
};
}

Expand All @@ -53,7 +54,7 @@ async function validateFlatOpts (opts: FlatOptions): Promise<ValidatedFlatOption
* @param {Object} opts - Options.
* @returns {Promise} Promise.
*/
async function buildApplicationPkg (opts: ValidatedFlatOptions, identity: Identity) {
async function buildApplicationPkg(opts: ValidatedFlatOptions, identity: Identity) {
const args = ['--component', opts.app, opts.install, '--sign', identity.name, opts.pkg];
if (opts.keychain) {
args.unshift('--keychain', opts.keychain);
Expand All @@ -69,7 +70,7 @@ async function buildApplicationPkg (opts: ValidatedFlatOptions, identity: Identi
/**
* This function is exported and returns a promise flattening the application.
*/
export async function buildPkg (_opts: FlatOptions) {
export async function buildPkg(_opts: FlatOptions) {
debugLog('@electron/osx-sign@%s', pkgVersion);
const validatedOptions = await validateFlatOpts(_opts);
let identities: Identity[] = [];
Expand All @@ -80,23 +81,31 @@ export async function buildPkg (_opts: FlatOptions) {
if (validatedOptions.identityValidation === false) {
// Do nothing
} else {
identities = await findIdentities(validatedOptions.keychain || null, validatedOptions.identity);
identities = await findIdentities(
validatedOptions.keychain || null,
isIdentity(validatedOptions.identity)
? validatedOptions.identity.name
: validatedOptions.identity,
);
}
} else {
debugWarn('No `identity` passed in arguments...');
if (validatedOptions.platform === 'mas') {
debugLog(
'Finding `3rd Party Mac Developer Installer` certificate for flattening app distribution in the Mac App Store...'
'Finding `3rd Party Mac Developer Installer` certificate for flattening app distribution in the Mac App Store...',
);
identities = await findIdentities(
validatedOptions.keychain || null,
'3rd Party Mac Developer Installer:'
'3rd Party Mac Developer Installer:',
);
} else {
debugLog(
'Finding `Developer ID Application` certificate for distribution outside the Mac App Store...'
'Finding `Developer ID Application` certificate for distribution outside the Mac App Store...',
);
identities = await findIdentities(
validatedOptions.keychain || null,
'Developer ID Installer:',
);
identities = await findIdentities(validatedOptions.keychain || null, 'Developer ID Installer:');
}
}

Expand Down Expand Up @@ -129,7 +138,7 @@ export async function buildPkg (_opts: FlatOptions) {
validatedOptions.identity,
'\n',
'> Scripts:',
validatedOptions.scripts
validatedOptions.scripts,
);
await buildApplicationPkg(validatedOptions, identityInUse);

Expand Down
8 changes: 8 additions & 0 deletions src/isIdentity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Identity } from './types';

export const isIdentity = (element: string | Identity): element is Identity => {
return (
typeof (element as Identity).name !== 'undefined' &&
typeof (element as Identity).hash !== 'undefined'
);
};
98 changes: 56 additions & 42 deletions src/sign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@ import {
execFileAsync,
validateOptsApp,
validateOptsPlatform,
walkAsync
walkAsync,
} from './util';
import { Identity, findIdentities } from './util-identities';
import { preEmbedProvisioningProfile, getProvisioningProfile } from './util-provisioning-profiles';
import { preAutoEntitlements } from './util-entitlements';
import { ElectronMacPlatform, PerFileSignOptions, SignOptions, ValidatedSignOptions } from './types';
import {
ElectronMacPlatform,
PerFileSignOptions,
SignOptions,
ValidatedSignOptions,
} from './types';
import { isIdentity } from './isIdentity';

const pkgVersion: string = require('../../package.json').version;

Expand All @@ -25,7 +31,7 @@ const osRelease = os.release();
/**
* This function returns a promise validating opts.binaries, the additional binaries to be signed along with the discovered enclosed components.
*/
async function validateOptsBinaries (opts: SignOptions) {
async function validateOptsBinaries(opts: SignOptions) {
if (opts.binaries) {
if (!Array.isArray(opts.binaries)) {
throw new Error('Additional binaries should be an Array.');
Expand All @@ -34,7 +40,7 @@ async function validateOptsBinaries (opts: SignOptions) {
}
}

function validateOptsIgnore (ignore: SignOptions['ignore']): ValidatedSignOptions['ignore'] {
function validateOptsIgnore(ignore: SignOptions['ignore']): ValidatedSignOptions['ignore'] {
if (ignore && !(ignore instanceof Array)) {
return [ignore];
}
Expand All @@ -43,7 +49,7 @@ function validateOptsIgnore (ignore: SignOptions['ignore']): ValidatedSignOption
/**
* This function returns a promise validating all options passed in opts.
*/
async function validateSignOpts (opts: SignOptions): Promise<Readonly<ValidatedSignOptions>> {
async function validateSignOpts(opts: SignOptions): Promise<Readonly<ValidatedSignOptions>> {
await validateOptsBinaries(opts);
await validateOptsApp(opts);

Expand All @@ -60,15 +66,15 @@ async function validateSignOpts (opts: SignOptions): Promise<Readonly<ValidatedS
...opts,
ignore: validateOptsIgnore(opts.ignore),
type: opts.type || 'distribution',
platform
platform,
};
return cloned;
}

/**
* This function returns a promise verifying the code sign of application bundle.
*/
async function verifySignApplication (opts: ValidatedSignOptions) {
async function verifySignApplication(opts: ValidatedSignOptions) {
// Verify with codesign
debugLog('Verifying application bundle with codesign...');

Expand All @@ -80,15 +86,15 @@ async function verifySignApplication (opts: ValidatedSignOptions) {
'--strict' +
(opts.strictVerify
? '=' + opts.strictVerify // Array should be converted to a comma separated string
: '')
: ''),
]
: [],
['--verbose=2', opts.app]
)
['--verbose=2', opts.app],
),
);
}

function defaultOptionsForFile (filePath: string, platform: ElectronMacPlatform) {
function defaultOptionsForFile(filePath: string, platform: ElectronMacPlatform) {
const entitlementsFolder = path.resolve(__dirname, '..', '..', 'entitlements');

let entitlementsFile: string;
Expand All @@ -101,12 +107,12 @@ function defaultOptionsForFile (filePath: string, platform: ElectronMacPlatform)
// c.f. https://source.chromium.org/chromium/chromium/src/+/main:chrome/app/helper-plugin-entitlements.plist
if (filePath.includes('(Plugin).app')) {
entitlementsFile = path.resolve(entitlementsFolder, 'default.darwin.plugin.plist');
// GPU Helper
// c.f. https://source.chromium.org/chromium/chromium/src/+/main:chrome/app/helper-gpu-entitlements.plist
// GPU Helper
// c.f. https://source.chromium.org/chromium/chromium/src/+/main:chrome/app/helper-gpu-entitlements.plist
} else if (filePath.includes('(GPU).app')) {
entitlementsFile = path.resolve(entitlementsFolder, 'default.darwin.gpu.plist');
// Renderer Helper
// c.f. https://source.chromium.org/chromium/chromium/src/+/main:chrome/app/helper-renderer-entitlements.plist
// Renderer Helper
// c.f. https://source.chromium.org/chromium/chromium/src/+/main:chrome/app/helper-renderer-entitlements.plist
} else if (filePath.includes('(Renderer).app')) {
entitlementsFile = path.resolve(entitlementsFolder, 'default.darwin.renderer.plist');
}
Expand All @@ -126,22 +132,25 @@ function defaultOptionsForFile (filePath: string, platform: ElectronMacPlatform)
hardenedRuntime: true,
requirements: undefined as string | undefined,
signatureFlags: undefined as string | string[] | undefined,
timestamp: undefined as string | undefined
timestamp: undefined as string | undefined,
};
}

async function mergeOptionsForFile (
async function mergeOptionsForFile(
opts: PerFileSignOptions | null,
defaults: ReturnType<typeof defaultOptionsForFile>
defaults: ReturnType<typeof defaultOptionsForFile>,
) {
const mergedPerFileOptions = { ...defaults };
if (opts) {
if (opts.entitlements !== undefined) {
if (Array.isArray(opts.entitlements)) {
const entitlements = opts.entitlements.reduce<Record<string, any>>((dict, entitlementKey) => ({
...dict,
[entitlementKey]: true
}), {});
const entitlements = opts.entitlements.reduce<Record<string, any>>(
(dict, entitlementKey) => ({
...dict,
[entitlementKey]: true,
}),
{},
);
const dir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'tmp-entitlements-'));
const entitlementsPath = path.join(dir, 'entitlements.plist');
await fs.writeFile(entitlementsPath, plist.build(entitlements), 'utf8');
Expand All @@ -164,8 +173,8 @@ async function mergeOptionsForFile (
/**
* This function returns a promise codesigning only.
*/
async function signApplication (opts: ValidatedSignOptions, identity: Identity) {
function shouldIgnoreFilePath (filePath: string) {
async function signApplication(opts: ValidatedSignOptions, identity: Identity) {
function shouldIgnoreFilePath(filePath: string) {
if (opts.ignore) {
return opts.ignore.some(function (ignore) {
if (typeof ignore === 'function') {
Expand Down Expand Up @@ -205,7 +214,7 @@ async function signApplication (opts: ValidatedSignOptions, identity: Identity)

const perFileOptions = await mergeOptionsForFile(
opts.optionsForFile ? opts.optionsForFile(filePath) : null,
defaultOptionsForFile(filePath, opts.platform)
defaultOptionsForFile(filePath, opts.platform),
);

if (opts.preAutoEntitlements === false) {
Expand All @@ -214,15 +223,15 @@ async function signApplication (opts: ValidatedSignOptions, identity: Identity)
debugLog(
'Pre-sign operation enabled for entitlements automation with versions >= `1.1.1`:',
'\n',
'* Disable by setting `pre-auto-entitlements` to `false`.'
'* Disable by setting `pre-auto-entitlements` to `false`.',
);
if (!opts.version || compareVersion(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
const newEntitlements = await preAutoEntitlements(opts, perFileOptions, {
identity,
provisioningProfile: opts.provisioningProfile
? await getProvisioningProfile(opts.provisioningProfile, opts.keychain)
: undefined
: undefined,
});

// preAutoEntitlements may provide us new entitlements, if so we update our options
Expand Down Expand Up @@ -266,7 +275,7 @@ async function signApplication (opts: ValidatedSignOptions, identity: Identity)
} 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'
'Not enabling hardened runtime, current macOS version too low, requires 10.13.6 and higher',
);
optionsArguments = optionsArguments.filter((arg) => {
return arg !== 'runtime';
Expand All @@ -280,7 +289,7 @@ async function signApplication (opts: ValidatedSignOptions, identity: Identity)

await execFileAsync(
'codesign',
perFileArgs.concat('--entitlements', perFileOptions.entitlements, filePath)
perFileArgs.concat('--entitlements', perFileOptions.entitlements, filePath),
);
}

Expand All @@ -295,7 +304,7 @@ async function signApplication (opts: ValidatedSignOptions, identity: Identity)
'--display',
'--entitlements',
':-', // Write to standard output and strip off the blob header
opts.app
opts.app,
]);

debugLog('Entitlements:', '\n', result);
Expand All @@ -304,7 +313,7 @@ async function signApplication (opts: ValidatedSignOptions, identity: Identity)
/**
* This function returns a promise signing the application.
*/
export async function signApp (_opts: SignOptions) {
export async function signApp(_opts: SignOptions) {
debugLog('electron-osx-sign@%s', pkgVersion);
const validatedOpts = await validateSignOpts(_opts);
let identities: Identity[] = [];
Expand All @@ -314,34 +323,39 @@ export async function signApp (_opts: SignOptions) {
if (validatedOpts.identity) {
debugLog('`identity` passed in arguments.');
if (validatedOpts.identityValidation === false) {
identityInUse = new Identity(validatedOpts.identity);
identityInUse = new Identity(
isIdentity(validatedOpts.identity) ? validatedOpts.identity.name : validatedOpts.identity,
);
} else {
identities = await findIdentities(validatedOpts.keychain || null, validatedOpts.identity);
identities = await findIdentities(
validatedOpts.keychain || null,
isIdentity(validatedOpts.identity) ? validatedOpts.identity.name : validatedOpts.identity,
);
}
} else {
debugWarn('No `identity` passed in arguments...');
if (validatedOpts.platform === 'mas') {
if (validatedOpts.type === 'distribution') {
debugLog(
'Finding `3rd Party Mac Developer Application` certificate for signing app distribution in the Mac App Store...'
'Finding `3rd Party Mac Developer Application` certificate for signing app distribution in the Mac App Store...',
);
identities = await findIdentities(
validatedOpts.keychain || null,
'3rd Party Mac Developer Application:'
'3rd Party Mac Developer Application:',
);
} else {
debugLog(
'Finding `Mac Developer` certificate for signing app in development for the Mac App Store signing...'
'Finding `Mac Developer` certificate for signing app in development for the Mac App Store signing...',
);
identities = await findIdentities(validatedOpts.keychain || null, 'Mac Developer:');
}
} else {
debugLog(
'Finding `Developer ID Application` certificate for distribution outside the Mac App Store...'
'Finding `Developer ID Application` certificate for distribution outside the Mac App Store...',
);
identities = await findIdentities(
validatedOpts.keychain || null,
'Developer ID Application:'
'Developer ID Application:',
);
}
}
Expand All @@ -366,19 +380,19 @@ export async function signApp (_opts: SignOptions) {
debugWarn(
'Pre-sign operation disabled for provisioning profile embedding:',
'\n',
'* Enable by setting `pre-embed-provisioning-profile` to `true`.'
'* 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`.'
'* Disable by setting `pre-embed-provisioning-profile` to `false`.',
);
await preEmbedProvisioningProfile(
validatedOpts,
validatedOpts.provisioningProfile
? await getProvisioningProfile(validatedOpts.provisioningProfile, validatedOpts.keychain)
: null
: null,
);
}

Expand All @@ -395,7 +409,7 @@ export async function signApp (_opts: SignOptions) {
validatedOpts.binaries,
'\n',
'> Identity:',
validatedOpts.identity
validatedOpts.identity,
);
await signApplication(validatedOpts, identityInUse);

Expand Down
Loading