feat: new per-file API with defaults that work out of the box (#253)

BREAKING CHANGE: The entire API changed
This commit is contained in:
Samuel Attard 2022-03-23 23:35:46 -07:00 коммит произвёл GitHub
Родитель 6bdaabfba2
Коммит af53ad09d6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
18 изменённых файлов: 298 добавлений и 450 удалений

Просмотреть файл

@ -28,21 +28,21 @@ version: 2
jobs:
test-mac-16:
macos:
xcode: "11.0.0"
xcode: "11.6.0"
environment:
NODE_VERSION: "16"
<<: *steps-test
test-mac-14:
macos:
xcode: "11.0.0"
xcode: "11.6.0"
environment:
NODE_VERSION: "14"
<<: *steps-test
test-mac-12:
macos:
xcode: "11.0.0"
xcode: "11.6.0"
environment:
NODE_VERSION: "12"
<<: *steps-test

Просмотреть файл

@ -77,10 +77,7 @@ The examples below assume that `--pre-auto-entitlements` is enabled.
electron-osx-sign path/to/my.app --provisioning-profile=path/to/my.provisionprofile
```
- To specify the entitlements file:
```sh
electron-osx-sign path/to/my.app --entitlements=path/to/my.entitlements
```
- To specify custom entitlements files you have to use the JS API.
- It is recommended to make use of `--version` while signing legacy versions of Electron:
```sh
@ -151,34 +148,21 @@ Needs file extension `.app`.
Path to additional binaries that will be signed along with built-ins of Electron.
Default to `undefined`.
`entitlements` - *String*
`optionsForFile` - *Function*
Path to entitlements file for signing the app.
Default to built-in entitlements file, Sandbox enabled for Mac App Store platform.
See [default.entitlements.mas.plist](https://github.com/electron-userland/electron-osx-sign/blob/master/default.entitlements.mas.plist) or [default.entitlements.darwin.plist](https://github.com/electron-userland/electron-osx-sign/blob/master/default.entitlements.darwin.plist) with respect to your platform.
Function that receives the path to a file and can return the entitlements to use for that file to override the default behavior. The
object this function returns can include any of the following optional keys.
`entitlements-inherit` - *String*
Path to child entitlements which inherit the security settings for signing frameworks and bundles of a distribution. *This option only applies when signing with entitlements.*
See [default.entitlements.mas.inherit.plist](https://github.com/electron-userland/electron-osx-sign/blob/master/default.entitlements.mas.inherit.plist) or [default.entitlements.darwin.inherit.plist](https://github.com/electron-userland/electron-osx-sign/blob/master/default.entitlements.darwin.inherit.plist) with respect to your platform.
`entitlements-loginhelper` - *String*
Path to login helper entitlement file. When using App Sandbox, the inherited entitlement should not be used since this is a standalone executable. *This option only applies when signing with entitlements.*
Default to the same entitlements file used for signing the app bundle.
`entitlementsForFile` - *Function*
Function that receives the path to a file and the current codesign arguments as parameters. If you wish to override the entitlements used for this file path this function should return the absolute path to a different entitlements file.
| Option | Description | Usage Example |
|-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------|
| `entitlements` | String specifying the path to an `entitlements.plist` file. Will default to built-in entitlements files. Can also be an array of entitlement keys that osx-sign will write to an entitlements file for you. | `'path/to/entitlements'` |
| `hardenedRuntime` | Boolean flag to enable the Hardened Runtime when signing the app. Enabled by default. | `false` |
| `requirements` | String specifying the [requirements](https://developer.apple.com/library/mac/documentation/Security/Conceptual/CodeSigningGuide/RequirementLang/RequirementLang.html) that you recommend to be used to evaluate the code signature. | `'anchor apple or anchor = "/var/db/yourcorporateanchor.cert"'` |
| `signatureFlags` | List of [code signature flags](https://developer.apple.com/documentation/security/seccodesignatureflags?language=objc). Accepts an array of strings or a comma-separated string. | `['kSecCodeSignatureRestrict']` |
| `timestamp` | String specifying the URL of the timestamp authority server. Defaults to the server provided by Apple. Please note that this default server may not support signatures not furnished by Apple. Disable the timestamp service with `none`. | `'https://different.timeserver'` |
**Note:** Only available via the JS API
`gatekeeper-assess` - *Boolean*
Flag to enable/disable Gatekeeper assessment after signing the app. Disabling it is useful for signing with self-signed certificates.
Gatekeeper assessment is enabled by default on `darwin` platform.
Default to `true`.
`hardenedRuntime` or `hardened-runtime` - *Boolean*
Flag to enable the Mojave hardened runtime when signing the app. Disabled by default, requires Xcode >= 10 and
@ -191,7 +175,7 @@ Default to be selected with respect to `provisioning-profile` and `platform` fro
Signing platform `mas` will look for `3rd Party Mac Developer Application: * (*)`, and platform `darwin` will look for `Developer ID Application: * (*)` by default.
`identity-validation` - *Boolean*
`identityValidation` - *Boolean*
Flag to enable/disable validation for the signing identity. If enabled, the `identity` provided will be validated in the `keychain` specified.
Default to `true`.
@ -213,49 +197,27 @@ Build platform of Electron.
Allowed values: `darwin`, `mas`.
Default to auto detect by presence of `Squirrel.framework` within the application bundle.
`pre-auto-entitlements` - *Boolean*
`preAutoEntitlements` - *Boolean*
Flag to enable/disable automation of `com.apple.security.application-groups` in entitlements file and update `Info.plist` with `ElectronTeamID`.
Default to `true`.
`pre-embed-provisioning-profile` - *Boolean*
`preEmbedProvisioningProfile` - *Boolean*
Flag to enable/disable embedding of provisioning profile in the current working directory.
Default to `true`.
`provisioning-profile` - *String*
`provisioningProfile` - *String*
Path to provisioning profile.
`requirements` - *String*
Specify the criteria that you recommend to be used to evaluate the code signature.
See more info from https://developer.apple.com/library/mac/documentation/Security/Conceptual/CodeSigningGuide/RequirementLang/RequirementLang.html
Default to `undefined`.
`restrict` - *Boolean*
**To be deprecated, see `signature-flags`.**
Restrict dyld loading. See doc about this [code signature flag](https://developer.apple.com/documentation/security/seccodesignatureflags/kseccodesignaturerestrict?language=objc) for more details. Disabled by default.
`signature-flags` - *String*
Comma separated string or array for [code signature flag](https://developer.apple.com/documentation/security/seccodesignatureflags?language=objc). Default to `undefined`.
`signature-size` - *Number*
Provide a value to be passed to `codesign` along with the `--signature-size` flag, to work around the *signature too large to embed* issue. A value of `12000` should do it - see the [FAQ](https://github.com/electron/electron-osx-sign/wiki/FAQ) for details. Default to `undefined`.
`strict-verify` - *Boolean|String|Array.<String>*
`strictVerify` - *Boolean|String|Array.<String>*
Flag to enable/disable `--strict` flag when verifying the signed application bundle.
If provided as a string, each component should be separated with comma (`,`).
If provided as an array, each item should be a string corresponding to a component.
Default to `true`.
`timestamp` - *String*
Specify the URL of the timestamp authority server, default to server provided by Apple. Please note that this default server may not support signatures not furnished by Apple.
Disable the timestamp service with `none`.
`type` - *String*
Specify whether to sign app for development or for distribution.
@ -354,7 +316,7 @@ Default to be selected with respect to `platform` from `keychain` or keychain by
Flattening platform `mas` will look for `3rd Party Mac Developer Installer: * (*)`, and platform `darwin` will look for `Developer ID Installer: * (*)` by default.
`identity-validation` - *Boolean*
`identityValidation` - *Boolean*
Flag to enable/disable validation for signing identity. If enabled, the `identity` provided will be validated in the `keychain` specified.
Default to `true`.

Просмотреть файл

@ -17,7 +17,7 @@ DESCRIPTION
Name of certificate to use when signing.
Default to selected with respect to --platform from --keychain specified or keychain by system default.
--identity-validation, --no-identity-validation
--identityValidation, --no-identityValidation
Flag to enable/disable validation for the signing identity.
--install=install-path

Просмотреть файл

@ -13,26 +13,6 @@ DESCRIPTION
embedded-binary ...
Path to additional binaries that will be signed along with built-ins of Electron, spaced.
--entitlements=file
Path to entitlements file for signing the app.
Default to built-in entitlements file, Sandbox enabled for Mac App Store platform.
--entitlements-inherit=file
Path to child entitlements which inherit the security settings for signing frameworks and bundles of a distribution.
This option only applies when signing with entitlements.
--entitlements-loginhelper=file
Path to login helper entitlement file. When using App Sandbox, the inherited entitlement should not be used since this is a standalone executable.
This option only applies when signing with entitlements.
--gatekeeper-assess, --no-gatekeeper-assess
Flag to enable/disable Gatekeeper assessment after signing the app. Disabling it is useful for signing with self-signed certificates.
Gatekeeper assessment is enabled by default on ``darwin'' platform.
--hardened-runtime
Flag to enable the Mojave hardened runtime when signing the app. Disabled by default, requires Xcode >= 10 and macOS
>= 10.13.6.
--help
Flag to display all commands.
@ -40,7 +20,7 @@ DESCRIPTION
Name of certificate to use when signing.
Default to selected with respect to --provisioning-profile and --platform from --keychain specified or keychain by system default.
--identity-validation, --no-identity-validation
--identityValidation, --no-identityValidation
Flag to enable/disable validation for the signing identity.
--ignore=path
@ -64,28 +44,11 @@ DESCRIPTION
--provisioning-profile=file
Path to provisioning profile.
--requirements=requirements
Specify the criteria that you recommend to be used to evaluate the code signature.
--restrict
(This will be deprecated soon, see --sign-flags.)
Flag to enable restrict mode. Disabled by default.
--signature-flags=flags
Code signature flags. Default to none.
--signature-size=size
Signature size. Default to none.
--strict-verify, --strict-verify=options, --no-strict-verify
--strictVerify, --strictVerify=options, --no-strictVerify
Flag to enable/disable ``--strict'' flag when verifying the signed application bundle.
Each component should be separated in ``options'' with comma (``,'').
Enabled by default.
--timestamp=timestamp
Specify the URL of the timestamp authority server, default to server provided by Apple.
Disable the timestamp service with ``none''.
--type=type
Specify whether to sign app for development or for distribution.
Allowed values: ``development'', ``distribution''.

Просмотреть файл

@ -6,21 +6,16 @@ const args = require('minimist')(process.argv.slice(2), {
string: [
'signature-flags'
],
number: [
'signature-size'
],
boolean: [
'help',
'pre-auto-entitlements',
'pre-embed-provisioning-profile',
'gatekeeper-assess',
'hardened-runtime',
'restrict'
],
default: {
'pre-auto-entitlements': true,
'pre-embed-provisioning-profile': true,
'gatekeeper-assess': true
'pre-embed-provisioning-profile': true
}
});
const usage = fs.readFileSync(path.join(__dirname, 'electron-osx-sign-usage.txt')).toString();

Просмотреть файл

@ -2,7 +2,7 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<key>com.apple.security.cs.allow-jit</key>
<true/>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.bluetooth</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.device.print</key>
<true/>
<key>com.apple.security.device.usb</key>
<true/>
<key>com.apple.security.personal-information.location</key>
<true/>
</dict>
</plist>

Просмотреть файл

@ -2,5 +2,9 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

Просмотреть файл

@ -2,5 +2,7 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
</dict>
</plist>

22
entitlements/default.mas.plist Executable file
Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.print</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.device.microphone</key>
<true/>
<key>com.apple.security.device.usb</key>
<true/>
</dict>
</plist>

43
index.d.ts поставляемый
Просмотреть файл

@ -1,43 +0,0 @@
declare module "electron-osx-sign" {
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 | ((file: string) => boolean);
'pre-auto-entitlements'?: boolean;
'pre-embed-provisioning-profile'?: boolean;
'provisioning-profile'?: string;
'requirements'?: string;
'signature-flags'?: string | ((file: string) => string[]);
'signature-size'?: number;
'type'?: string;
version?: string;
entitlementsForFile?: (file: string, codeSignArgs: string[]) => string | null;
}
export function sign(opts: SignOptions, callback: (error: Error) => void): void;
export function signAsync(opts: SignOptions): Promise<any>;
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<any>;
}

Просмотреть файл

@ -69,6 +69,6 @@
]
},
"engines": {
"node": ">=4.0.0"
"node": ">=12.0.0"
}
}

Просмотреть файл

@ -77,7 +77,7 @@ export async function buildPkg (_opts: FlatOptions) {
if (validatedOptions.identity) {
debugLog('`identity` passed in arguments.');
if (validatedOptions['identity-validation'] === false) {
if (validatedOptions.identityValidation === false) {
// Do nothing
} else {
identities = await findIdentities(validatedOptions.keychain || null, validatedOptions.identity);

Просмотреть файл

@ -1,5 +1,7 @@
import * as fs from 'fs-extra';
import * as os from 'os';
import * as path from 'path';
import * as plist from 'plist';
import compareVersion from 'compare-version';
import {
@ -12,12 +14,9 @@ import {
walkAsync
} from './util';
import { Identity, findIdentities } from './util-identities';
import {
preEmbedProvisioningProfile,
getProvisioningProfile
} from './util-provisioning-profiles';
import { preEmbedProvisioningProfile, getProvisioningProfile } from './util-provisioning-profiles';
import { preAutoEntitlements } from './util-entitlements';
import { ElectronMacPlatform, SignOptions, ValidatedSignOptions } from './types';
import { ElectronMacPlatform, PerFileSignOptions, SignOptions, ValidatedSignOptions } from './types';
const pkgVersion: string = require('../../package.json').version;
@ -41,85 +40,6 @@ function validateOptsIgnore (ignore: SignOptions['ignore']): ValidatedSignOption
}
}
function validateOptsEntitlements (opts: SignOptions, platform: ElectronMacPlatform) {
const entitlementOptions = {
entitlements: opts.entitlements,
'entitlements-inherit': opts['entitlements-inherit']
};
const entitlementsFolder = path.resolve(__dirname, '..', '..', 'entitlements');
if (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 (!entitlementOptions.entitlements) {
const entitlementsPath = path.resolve(entitlementsFolder, '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:',
entitlementsPath
);
entitlementOptions.entitlements = entitlementsPath;
}
if (!entitlementOptions['entitlements-inherit']) {
const entitlementsPath = path.join(
entitlementsFolder,
'default.entitlements.mas.inherit.plist'
);
debugWarn(
'No `entitlements-inherit` passed in arguments:',
'\n',
'* Sandbox entitlements file for enclosed app files is default to:',
entitlementsPath
);
entitlementOptions['entitlements-inherit'] = entitlementsPath;
}
} 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 boolean flag, fallback to default
if ((entitlementOptions.entitlements as any) === true) {
const entitlementsPath = path.join(entitlementsFolder, 'default.entitlements.darwin.plist');
debugWarn(
'`entitlements` not specified in arguments:',
'\n',
'* Provide `entitlements` to specify entitlements file for codesign.',
'\n',
'* Entitlements file is default to:',
entitlementsPath
);
entitlementOptions.entitlements = entitlementsPath;
}
if (!opts['entitlements-inherit']) {
const entitlementsPath = path.join(
entitlementsFolder,
'default.entitlements.darwin.inherit.plist'
);
debugWarn(
'No `entitlements-inherit` passed in arguments:',
'\n',
'* Entitlements file for enclosed app files is default to:',
entitlementsPath
);
entitlementOptions['entitlements-inherit'] = entitlementsPath;
}
}
}
return entitlementOptions as {
entitlements: string;
'entitlements-inherit': string;
};
}
/**
* This function returns a promise validating all options passed in opts.
*/
@ -127,7 +47,7 @@ async function validateSignOpts (opts: SignOptions): Promise<Readonly<ValidatedS
await validateOptsBinaries(opts);
await validateOptsApp(opts);
if (opts['provisioning-profile'] && typeof opts['provisioning-profile'] !== 'string') {
if (opts.provisioningProfile && typeof opts.provisioningProfile !== 'string') {
throw new Error('Path to provisioning profile should be a string.');
}
@ -138,7 +58,6 @@ async function validateSignOpts (opts: SignOptions): Promise<Readonly<ValidatedS
const platform = await validateOptsPlatform(opts);
const cloned: ValidatedSignOptions = {
...opts,
...validateOptsEntitlements(opts, platform),
ignore: validateOptsIgnore(opts.ignore),
type: opts.type || 'distribution',
platform
@ -156,31 +75,90 @@ async function verifySignApplication (opts: ValidatedSignOptions) {
await execFileAsync(
'codesign',
['--verify', '--deep'].concat(
opts['strict-verify'] !== false && compareVersion(osRelease, '15.0.0') >= 0 // Strict flag since darwin 15.0.0 --> OS X 10.11.0 El Capitan
opts.strictVerify !== false && compareVersion(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
(opts.strictVerify
? '=' + opts.strictVerify // 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
]);
function defaultOptionsForFile (filePath: string, platform: ElectronMacPlatform) {
const entitlementsFolder = path.resolve(__dirname, '..', '..', 'entitlements');
let entitlementsFile: string;
if (platform === 'darwin') {
// Default Entitlements
// c.f. https://source.chromium.org/chromium/chromium/src/+/main:chrome/app/app-entitlements.plist
// Also include JIT for main process V8
entitlementsFile = path.resolve(entitlementsFolder, 'default.darwin.plist');
// Plugin helper
// 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
} 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
} else if (filePath.includes('(Renderer).app')) {
entitlementsFile = path.resolve(entitlementsFolder, 'default.darwin.renderer.plist');
}
} else {
// Default entitlements
// TODO: Can these be more scoped like the non-mas variant?
entitlementsFile = path.resolve(entitlementsFolder, 'default.mas.plist');
// If it is not the top level app bundle, we sign with inherit
if (filePath.includes('.app/')) {
entitlementsFile = path.resolve(entitlementsFolder, 'default.mas.child.plist');
}
}
return {
entitlements: entitlementsFile,
hardenedRuntime: true,
requirements: undefined as string | undefined,
signatureFlags: undefined as string | string[] | undefined,
timestamp: undefined as string | undefined
};
}
async function mergeOptionsForFile (
opts: PerFileSignOptions | null,
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 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');
opts.entitlements = entitlementsPath;
}
mergedPerFileOptions.entitlements = opts.entitlements;
}
if (opts.hardenedRuntime !== undefined) {
mergedPerFileOptions.hardenedRuntime = opts.hardenedRuntime;
}
if (opts.requirements !== undefined) mergedPerFileOptions.requirements = opts.requirements;
if (opts.signatureFlags !== undefined) {
mergedPerFileOptions.signatureFlags = opts.signatureFlags;
}
if (opts.timestamp !== undefined) mergedPerFileOptions.timestamp = opts.timestamp;
}
return mergedPerFileOptions;
}
/**
@ -207,23 +185,6 @@ async function signApplication (opts: ValidatedSignOptions, identity: Identity)
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.`
);
}
}
/**
* Sort the child paths by how deep they are in the file tree. Some arcane apple
@ -235,105 +196,90 @@ async function signApplication (opts: ValidatedSignOptions, identity: Identity)
const bDepth = b.split(path.sep).length;
return bDepth - aDepth;
});
if (opts.entitlements) {
// Sign with entitlements
// promise = Promise.mapSeries(childPaths, function (filePath) {
for (const filePath of children) {
if (shouldIgnoreFilePath(filePath)) {
debugLog('Skipped... ' + filePath);
continue;
}
debugLog('Signing... ' + filePath);
let optionsArguments: string[] = [];
if (opts['signature-flags']) {
if (Array.isArray(opts['signature-flags'])) {
optionsArguments.push(...opts['signature-flags']);
} else if (typeof opts['signature-flags'] === 'function') {
const flags = opts['signature-flags'](filePath);
optionsArguments.push(...flags);
} else {
const flags = opts['signature-flags'].split(',').map(function (flag) {
return flag.trim();
});
optionsArguments.push(...flags);
}
}
if (
opts.hardenedRuntime ||
opts['hardened-runtime'] ||
optionsArguments.includes('runtime')
) {
// Hardened runtime since darwin 17.7.0 --> macOS 10.13.6
if (compareVersion(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((arg) => {
return arg !== '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(','));
}
let entitlementsFile = opts['entitlements-inherit'];
if (filePath.includes('Library/LoginItems')) {
entitlementsFile = opts['entitlements-loginhelper']!;
}
const clonedArgs = args.concat([]);
if (opts.entitlementsForFile) {
entitlementsFile = opts.entitlementsForFile(filePath, clonedArgs) || entitlementsFile;
}
await execFileAsync(
'codesign',
clonedArgs.concat('--entitlements', entitlementsFile, filePath)
);
for (const filePath of [...children, opts.app]) {
if (shouldIgnoreFilePath(filePath)) {
debugLog('Skipped... ' + filePath);
continue;
}
// Sign the actual app now
debugLog('Signing... ' + opts.app);
const perFileOptions = await mergeOptionsForFile(
opts.optionsForFile ? opts.optionsForFile(filePath) : null,
defaultOptionsForFile(filePath, opts.platform)
);
const clonedArgs = args.concat([]);
let entitlementsFile = opts.entitlements;
if (opts.entitlementsForFile) {
entitlementsFile = opts.entitlementsForFile(opts.app, clonedArgs) || entitlementsFile;
if (opts.preAutoEntitlements === 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.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
});
// preAutoEntitlements may provide us new entitlements, if so we update our options
// and ensure that entitlements-loginhelper has a correct default value
if (newEntitlements) {
perFileOptions.entitlements = newEntitlements;
}
}
}
debugLog('Signing... ' + filePath);
if (perFileOptions.requirements) {
args.push('--requirements', perFileOptions.requirements);
}
if (perFileOptions.timestamp) {
args.push('--timestamp=' + perFileOptions.timestamp);
} else {
args.push('--timestamp');
}
let optionsArguments: string[] = [];
if (perFileOptions.signatureFlags) {
if (Array.isArray(perFileOptions.signatureFlags)) {
optionsArguments.push(...perFileOptions.signatureFlags);
} else {
const flags = perFileOptions.signatureFlags.split(',').map(function (flag) {
return flag.trim();
});
optionsArguments.push(...flags);
}
}
if (perFileOptions.hardenedRuntime || optionsArguments.includes('runtime')) {
// Hardened runtime since darwin 17.7.0 --> macOS 10.13.6
if (compareVersion(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((arg) => {
return arg !== 'runtime';
});
}
}
if (optionsArguments.length) {
args.push('--options', [...new Set(optionsArguments)].join(','));
}
await execFileAsync(
'codesign',
clonedArgs.concat('--entitlements', entitlementsFile, opts.app)
args.concat('--entitlements', perFileOptions.entitlements, filePath)
);
} else {
// Otherwise normally
for (const filePath of children) {
if (shouldIgnoreFilePath(filePath)) {
debugLog('Skipped... ' + filePath);
continue;
}
await execFileAsync('codesign', args.concat(filePath));
}
debugLog('Signing... ' + opts.app);
await execFileAsync('codesign', args.concat(opts.app));
}
// Verify code sign
@ -342,17 +288,15 @@ async function signApplication (opts: ValidatedSignOptions, identity: Identity)
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('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);
}
debugLog('Entitlements:', '\n', result);
}
/**
@ -360,14 +304,14 @@ async function signApplication (opts: ValidatedSignOptions, identity: Identity)
*/
export async function signApp (_opts: SignOptions) {
debugLog('electron-osx-sign@%s', pkgVersion);
let validatedOpts = await validateSignOpts(_opts);
const validatedOpts = await validateSignOpts(_opts);
let identities: Identity[] = [];
let identityInUse: Identity | null = null;
// Determine identity for signing
if (validatedOpts.identity) {
debugLog('`identity` passed in arguments.');
if (validatedOpts['identity-validation'] === false) {
if (validatedOpts.identityValidation === false) {
identityInUse = new Identity(validatedOpts.identity);
} else {
identities = await findIdentities(validatedOpts.keychain || null, validatedOpts.identity);
@ -416,7 +360,7 @@ export async function signApp (_opts: SignOptions) {
}
// Pre-sign operations
if (validatedOpts['pre-embed-provisioning-profile'] === false) {
if (validatedOpts.preEmbedProvisioningProfile === false) {
debugWarn(
'Pre-sign operation disabled for provisioning profile embedding:',
'\n',
@ -430,50 +374,12 @@ export async function signApp (_opts: SignOptions) {
);
await preEmbedProvisioningProfile(
validatedOpts,
validatedOpts['provisioning-profile']
? await getProvisioningProfile(
validatedOpts['provisioning-profile'],
validatedOpts.keychain
)
validatedOpts.provisioningProfile
? await getProvisioningProfile(validatedOpts.provisioningProfile, validatedOpts.keychain)
: null
);
}
if (validatedOpts['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 (
validatedOpts.entitlements &&
(!validatedOpts.version || compareVersion(validatedOpts.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(validatedOpts, {
identity: identityInUse,
provisioningProfile: validatedOpts['provisioning-profile']
? await getProvisioningProfile(
validatedOpts['provisioning-profile'],
validatedOpts.keychain
)
: undefined
});
// preAutoEntitlements may provide us new entitlements, if so we update our options
// and ensure that entitlements-loginhelper has a correct default value
if (newEntitlements) {
validatedOpts = {
...validatedOpts,
entitlements: newEntitlements,
'entitlements-loginhelper': validatedOpts['entitlements-loginhelper'] || newEntitlements
};
}
}
}
debugLog(
'Signing application...',
'\n',
@ -483,15 +389,6 @@ export async function signApp (_opts: SignOptions) {
'> Platform:',
validatedOpts.platform,
'\n',
'> Entitlements:',
validatedOpts.entitlements,
'\n',
'> Child entitlements:',
validatedOpts['entitlements-inherit'],
'\n',
'> Login helper entitlements:',
validatedOpts['entitlements-loginhelper'],
'\n',
'> Additional binaries:',
validatedOpts.binaries,
'\n',

Просмотреть файл

@ -12,42 +12,56 @@ type OnlyValidatedBaseSignOptions = {
platform: ElectronMacPlatform;
};
type OnlySignOptions = {
binaries?: string[];
entitlements?: string;
'entitlements-inherit'?: string;
'entitlements-loginhelper'?: string;
'gatekeeper-assess'?: boolean;
/**
* Any missing options will use the default values, providing a partial
* structure will shallow merge with the default values.
*/
export type PerFileSignOptions = {
/**
* The entitlements file to use when signing this file
*/
entitlements?: string | string[];
/**
* Whether to enable hardened runtime for this file. Enabled by default.
*/
hardenedRuntime?: boolean;
/**
* @deprecated use hardenedRuntime instead
* The designated requirements to embed when signing this file
*/
['hardened-runtime']?: boolean;
'identity-validation'?: boolean;
ignore?: string | string[] | ((file: string) => boolean);
'pre-auto-entitlements'?: boolean;
'pre-embed-provisioning-profile'?: boolean;
'provisioning-profile'?: string;
requirements?: string;
restrict?: boolean;
'signature-flags'?: string | ((file: string) => string[]);
'signature-size'?: number;
'strict-verify'?: boolean;
/**
* See --options of the "codesign" command.
*
* https://www.manpagez.com/man/1/codesign
*/
signatureFlags?: string | string[];
/**
* The timestamp server to use when signing, by default uses the Apple provided
* timestamp server.
*/
timestamp?: string;
}
type OnlySignOptions = {
binaries?: string[];
optionsForFile?: (filePath: string) => PerFileSignOptions;
identityValidation?: boolean;
ignore?: string | string[] | ((file: string) => boolean);
preAutoEntitlements?: boolean;
preEmbedProvisioningProfile?: boolean;
provisioningProfile?: string;
strictVerify?: boolean;
type?: SigningDistributionType;
version?: string;
entitlementsForFile?: (file: string, codeSignArgs: string[]) => string | null;
};
type OnlyValidatedSignOptions = {
entitlements: string;
'entitlements-inherit': string;
ignore?: (string | ((file: string) => boolean))[];
type: SigningDistributionType;
};
type OnlyFlatOptions = {
'identity-validation'?: boolean;
identityValidation?: boolean;
install?: string;
pkg?: string;
scripts?: string;

Просмотреть файл

@ -2,7 +2,7 @@ import * as fs from 'fs-extra';
import * as os from 'os';
import * as path from 'path';
import * as plist from 'plist';
import { ValidatedSignOptions } from './types';
import { PerFileSignOptions, ValidatedSignOptions } from './types';
import { debugLog, getAppContentsPath } from './util';
import { Identity } from './util-identities';
import { ProvisioningProfile } from './util-provisioning-profiles';
@ -12,6 +12,8 @@ type ComputedOptions = {
provisioningProfile?: ProvisioningProfile;
};
const preAuthMemo = new Map<string, string>();
/**
* This function returns a promise completing the entitlements automation: The
* process includes checking in `Info.plist` for `ElectronTeamID` or setting
@ -22,9 +24,13 @@ type ComputedOptions = {
*/
export async function preAutoEntitlements (
opts: ValidatedSignOptions,
perFileOpts: PerFileSignOptions,
computed: ComputedOptions
): Promise<void | string> {
if (!opts.entitlements) return;
if (!perFileOpts.entitlements) return;
const memoKey = [opts.app, perFileOpts.entitlements].join('---');
if (preAuthMemo.has(memoKey)) return preAuthMemo.get(memoKey);
// If entitlements file not provided, default will be used. Fixes #41
const appInfoPath = path.join(getAppContentsPath(opts), 'Info.plist');
@ -34,12 +40,18 @@ export async function preAutoEntitlements (
'\n',
'> Info.plist:',
appInfoPath,
'\n',
'> Entitlements:',
opts.entitlements
'\n'
);
const entitlementsContents = await fs.readFile(opts.entitlements, 'utf8');
const entitlements = plist.parse(entitlementsContents) as Record<string, any>;
let entitlements: Record<string, any>;
if (typeof perFileOpts.entitlements === 'string') {
const entitlementsContents = await fs.readFile(perFileOpts.entitlements, 'utf8');
entitlements = plist.parse(entitlementsContents) as Record<string, any>;
} else {
entitlements = perFileOpts.entitlements.reduce<Record<string, any>>((dict, entitlementKey) => ({
...dict,
[entitlementKey]: true
}), {});
}
if (!entitlements['com.apple.security.app-sandbox']) {
// Only automate when app sandbox enabled by user
return;
@ -126,5 +138,6 @@ export async function preAutoEntitlements (
await fs.writeFile(entitlementsPath, plist.build(entitlements), 'utf8');
debugLog('Entitlements file updated:', '\n', '> Entitlements:', entitlementsPath);
preAuthMemo.set(memoKey, entitlementsPath);
return entitlementsPath;
}

Просмотреть файл

@ -11,8 +11,7 @@ function createDefaultsTest (release) {
const opts = {
app: util.generateAppPath(release),
identity: 'codesign.electronjs.org',
'gatekeeper-assess': false
identity: 'codesign.electronjs.org'
}; // test with no other options for self discovery
waterfall(