Skip to content

Commit

Permalink
fix: Align customSSLCert capability logic (appium#1334)
Browse files Browse the repository at this point in the history
  • Loading branch information
mykola-mokhnach committed Sep 14, 2021
1 parent 35bc8b3 commit 8eca81c
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 17 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,8 @@ Capability | Description
|`appium:iosSimulatorLogsPredicate`|Set the `--predicate` flag in the ios simulator logs|e.g.: `'process != "locationd" AND process != "DTServiceHub"' AND process != "mobileassetd"`|
|`appium:simulatorPasteboardAutomaticSync`| Handle the `-PasteboardAutomaticSync` flag when simulator process launches. It could improve launching simulator performance not to sync pasteboard with the system when this value is `off`. `on` forces the flag enabled. `system` does not provide the flag to the launching command. `on`, `off`, or `system` is available. They are case insensitive. Defaults to `off` | e.g. `system` |
|`appium:simulatorDevicesSetPath`| This capability allows to set an alternative path to the simulator devices set in case you have multiple sets deployed on your local system. Such feature could be useful if you, for example, would like to save disk space on the main system volume. | e.g. `/MyVolume/Devices` |
|`appium:customSSLCert`| Adds a root SSL certificate to IOS Simulator. | e.g. ```-----BEGIN CERTIFICATE-----MIIFWjCCBEKg...-----END CERTIFICATE-----```|
|`appium:webkitResponseTimeout`| (Real device only) Set the time, in ms, to wait for a response from WebKit in a Safari session. Defaults to `5000`|e.g., `10000`|
|`appium:customSSLCert`| Adds a root SSL certificate to IOS Simulator. The certificate content must be provided in [PEM](https://knowledge.digicert.com/quovadis/ssl-certificates/ssl-general-topics/what-is-pem-format.html) format | e.g. ```-----BEGIN CERTIFICATE-----MIIFWjCCBEKg...-----END CERTIFICATE-----```|
|`appium:webkitResponseTimeout`| (Real device only) Set the time, in ms, to wait for a response from WebKit in a Safari session. Defaults to `5000` | e.g., `10000`|

### Web Context

Expand Down Expand Up @@ -567,7 +567,7 @@ On real devices or simulators before Xcode 11.4 SDK Apple provides no official w

Name | Type | Required | Description | Example
--- | --- | --- | --- | ---
content | string | yes | Base64-encoded content of the public certificate | a23234...
content | string | yes | Base64-encoded content of the public certificate in [PEM](https://knowledge.digicert.com/quovadis/ssl-certificates/ssl-general-topics/what-is-pem-format.html) format | a23234...
commonName | string | no | Common name of the certificate. If this is not set then the script will try to parse it from the given certificate content. | com.myorg
isRoot | boolean | no | This option defines where the certificate should be installed to: either Trusted Root Store (`true`, the default option) or the Keychain (`false`). On environments other than Xcode 11.4+ Simulator this option is ignored. | false

Expand Down
61 changes: 61 additions & 0 deletions lib/cert-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import _ from 'lodash';
import { installSSLCert, hasSSLCert } from 'appium-ios-simulator';

/**
* Check whether the given Simulator device supports simctl keychain API.
*
* @param {object} device Simulator instance created by appium-ios-simulator module
* @returns {boolean} `true` if the current Simulator SDK supports keychain commands.
*/
const doesSupportKeychainApi = _.memoize(async function doesSupportKeychainApi (device) {
try {
await device.simctl.exec('help', {
args: ['keychain']
});
return true;
} catch (e) {
return false;
}
});

/**
* Adds a certificate to the trusted root store.
* Simulator must be in BOOTED state for this API to work.
*
* @param {object} device Simulator instance created by appium-ios-simulator module
* @param {string} payload Certificate payload
*/
async function installCertificate (device, payload) {
await device.simctl.addRootCertificate(payload, {raw: true});
}

/**
* Check whether the given certificate is already installed.
* The function is using hacky calls to make certificate stuff working for older SDKs.
* Simulator must be in SHUTDOWN state for this API to work.
*
* @param {object} device Simulator instance created by appium-ios-simulator module
* @param {string} payload Certificate payload
* @returns {boolean} `true` if the certificate is already present in the root store.
*/
async function hasCertificateLegacy (device, payload) {
return await hasSSLCert(payload, device.udid);
}

/**
* Adds a certificate to the trusted root store.
* The function is using hacky calls to make certificate stuff working for older SDKs.
* Simulator must be in SHUTDOWN state for this API to work.
*
* @param {object} device Simulator instance created by appium-ios-simulator module
* @param {string} payload Certificate payload
*/
async function installCertificateLegacy (device, payload) {
await installSSLCert(payload, device.udid);
}


export {
doesSupportKeychainApi, installCertificate, installCertificateLegacy,
hasCertificateLegacy
};
5 changes: 4 additions & 1 deletion lib/commands/certificate.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,10 @@ commands.mobileInstallCertificate = async function mobileInstallCertificate (opt
if (this.isSimulator()) {
try {
const methodName = isRoot ? 'addRootCertificate' : 'addCertificate';
return void (await this.opts.device.simctl[methodName](content, {raw: true}));
await this.opts.device.simctl[methodName](
Buffer.from(content, 'base64').toString(), {raw: true}
);
return;
} catch (e) {
log.debug(e);
log.info(`The certificate cannot be installed via CLI. ` +
Expand Down
32 changes: 19 additions & 13 deletions lib/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import {
createSim, getExistingSim, runSimulatorReset, installToSimulator,
shutdownOtherSimulators, shutdownSimulator, setLocaleAndPreferences
} from './simulator-management';
import { getSimulator, installSSLCert, hasSSLCert } from 'appium-ios-simulator';
import { getSimulator } from 'appium-ios-simulator';
import {
doesSupportKeychainApi, installCertificate, installCertificateLegacy,
hasCertificateLegacy,
} from './cert-utils';
import { retryInterval, retry } from 'asyncbox';
import { verifyApplicationPlatform, extractBundleId } from './app-utils';
import { desiredCapConstraints, PLATFORM_NAME_IOS, PLATFORM_NAME_TVOS } from './desired-caps';
Expand Down Expand Up @@ -434,26 +438,28 @@ class XCUITestDriver extends BaseDriver {
await setLocaleAndPreferences(sim, this.opts, this.isSafari());
});

if (this.opts.customSSLCert) {
if (this.opts.customSSLCert && !(await doesSupportKeychainApi(this.opts.device))) {
const certHead = _.truncate(this.opts.customSSLCert, {length: 20});
log.info(`Installing the custom SSL certificate '${certHead}'`);
try {
await this.opts.device.simctl.addRootCertificate(this.opts.customSSLCert, {raw: true});
} catch (ign) {
if (await hasSSLCert(this.opts.customSSLCert, this.opts.udid)) {
log.info(`SSL certificate '${certHead}' already installed`);
} else {
log.info(`Making sure Simulator is shut down, ' +
'so that SSL certificate installation takes effect`);
await shutdownSimulator(this.opts.device);
await installSSLCert(this.opts.customSSLCert, this.opts.udid);
}
if (await hasCertificateLegacy(this.opts.device, this.opts.customSSLCert)) {
log.info(`SSL certificate '${certHead}' already installed`);
} else {
log.info(`Making sure Simulator is shut down, ' +
'so that SSL certificate installation takes effect`);
await shutdownSimulator(this.opts.device);
await installCertificateLegacy(this.opts.device, this.opts.customSSLCert);
}
this.logEvent('customCertInstalled');
}

await this.startSim();

if (this.opts.customSSLCert && await doesSupportKeychainApi(this.opts.device)) {
// Simulator must be booted in order to call this helper
await installCertificate(this.opts.device, this.opts.customSSLCert);
this.logEvent('customCertInstalled');
}

if (this.opts.launchWithIDB && this.isSimulator()) {
try {
const idb = new IDB({udid});
Expand Down

0 comments on commit 8eca81c

Please sign in to comment.