140 lines
18 KiB
JavaScript
140 lines
18 KiB
JavaScript
![]() |
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
const tslib_1 = require("tslib");
|
||
|
const path_1 = tslib_1.__importDefault(require("path"));
|
||
|
const fs_1 = require("fs");
|
||
|
const debug_1 = tslib_1.__importDefault(require("debug"));
|
||
|
const command_exists_1 = require("command-exists");
|
||
|
const utils_1 = require("../utils");
|
||
|
const shared_1 = require("./shared");
|
||
|
const debug = debug_1.default('devcert:platforms:macos');
|
||
|
const getCertUtilPath = () => path_1.default.join(utils_1.run('brew', ['--prefix', 'nss']).toString().trim(), 'bin', 'certutil');
|
||
|
class MacOSPlatform {
|
||
|
constructor() {
|
||
|
this.FIREFOX_BUNDLE_PATH = '/Applications/Firefox.app';
|
||
|
this.FIREFOX_BIN_PATH = path_1.default.join(this.FIREFOX_BUNDLE_PATH, 'Contents/MacOS/firefox');
|
||
|
this.FIREFOX_NSS_DIR = path_1.default.join(process.env.HOME, 'Library/Application Support/Firefox/Profiles/*');
|
||
|
this.HOST_FILE_PATH = '/etc/hosts';
|
||
|
}
|
||
|
/**
|
||
|
* macOS is pretty simple - just add the certificate to the system keychain,
|
||
|
* and most applications will delegate to that for determining trusted
|
||
|
* certificates. Firefox, of course, does it's own thing. We can try to
|
||
|
* automatically install the cert with Firefox if we can use certutil via the
|
||
|
* `nss` Homebrew package, otherwise we go manual with user-facing prompts.
|
||
|
*/
|
||
|
addToTrustStores(certificatePath, options = {}) {
|
||
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
||
|
// Chrome, Safari, system utils
|
||
|
debug('Adding devcert root CA to macOS system keychain');
|
||
|
utils_1.run('sudo', [
|
||
|
'security',
|
||
|
'add-trusted-cert',
|
||
|
'-d',
|
||
|
'-r',
|
||
|
'trustRoot',
|
||
|
'-k',
|
||
|
'/Library/Keychains/System.keychain',
|
||
|
'-p',
|
||
|
'ssl',
|
||
|
'-p',
|
||
|
'basic',
|
||
|
certificatePath
|
||
|
]);
|
||
|
if (this.isFirefoxInstalled()) {
|
||
|
// Try to use certutil to install the cert automatically
|
||
|
debug('Firefox install detected. Adding devcert root CA to Firefox trust store');
|
||
|
if (!this.isNSSInstalled()) {
|
||
|
if (!options.skipCertutilInstall) {
|
||
|
if (command_exists_1.sync('brew')) {
|
||
|
debug(`certutil is not already installed, but Homebrew is detected. Trying to install certutil via Homebrew...`);
|
||
|
try {
|
||
|
utils_1.run('brew', ['install', 'nss'], { stdio: 'ignore' });
|
||
|
}
|
||
|
catch (e) {
|
||
|
debug(`brew install nss failed`);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
debug(`Homebrew didn't work, so we can't try to install certutil. Falling back to manual certificate install`);
|
||
|
return yield shared_1.openCertificateInFirefox(this.FIREFOX_BIN_PATH, certificatePath);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
debug(`certutil is not already installed, and skipCertutilInstall is true, so we have to fall back to a manual install`);
|
||
|
return yield shared_1.openCertificateInFirefox(this.FIREFOX_BIN_PATH, certificatePath);
|
||
|
}
|
||
|
}
|
||
|
yield shared_1.closeFirefox();
|
||
|
yield shared_1.addCertificateToNSSCertDB(this.FIREFOX_NSS_DIR, certificatePath, getCertUtilPath());
|
||
|
}
|
||
|
else {
|
||
|
debug('Firefox does not appear to be installed, skipping Firefox-specific steps...');
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
removeFromTrustStores(certificatePath) {
|
||
|
debug('Removing devcert root CA from macOS system keychain');
|
||
|
try {
|
||
|
utils_1.run('sudo', [
|
||
|
'security',
|
||
|
'remove-trusted-cert',
|
||
|
'-d',
|
||
|
certificatePath
|
||
|
], {
|
||
|
stdio: 'ignore'
|
||
|
});
|
||
|
}
|
||
|
catch (e) {
|
||
|
debug(`failed to remove ${certificatePath} from macOS cert store, continuing. ${e.toString()}`);
|
||
|
}
|
||
|
if (this.isFirefoxInstalled() && this.isNSSInstalled()) {
|
||
|
debug('Firefox install and certutil install detected. Trying to remove root CA from Firefox NSS databases');
|
||
|
shared_1.removeCertificateFromNSSCertDB(this.FIREFOX_NSS_DIR, certificatePath, getCertUtilPath());
|
||
|
}
|
||
|
}
|
||
|
addDomainToHostFileIfMissing(domain) {
|
||
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
||
|
const trimDomain = domain.trim().replace(/[\s;]/g, '');
|
||
|
let hostsFileContents = fs_1.readFileSync(this.HOST_FILE_PATH, 'utf8');
|
||
|
if (!hostsFileContents.includes(trimDomain)) {
|
||
|
utils_1.sudoAppend(this.HOST_FILE_PATH, `127.0.0.1 ${trimDomain}\n`);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
deleteProtectedFiles(filepath) {
|
||
|
shared_1.assertNotTouchingFiles(filepath, 'delete');
|
||
|
utils_1.run('sudo', ['rm', '-rf', filepath]);
|
||
|
}
|
||
|
readProtectedFile(filepath) {
|
||
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
||
|
shared_1.assertNotTouchingFiles(filepath, 'read');
|
||
|
return (yield utils_1.run('sudo', ['cat', filepath])).toString().trim();
|
||
|
});
|
||
|
}
|
||
|
writeProtectedFile(filepath, contents) {
|
||
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
||
|
shared_1.assertNotTouchingFiles(filepath, 'write');
|
||
|
if (fs_1.existsSync(filepath)) {
|
||
|
yield utils_1.run('sudo', ['rm', filepath]);
|
||
|
}
|
||
|
fs_1.writeFileSync(filepath, contents);
|
||
|
yield utils_1.run('sudo', ['chown', '0', filepath]);
|
||
|
yield utils_1.run('sudo', ['chmod', '600', filepath]);
|
||
|
});
|
||
|
}
|
||
|
isFirefoxInstalled() {
|
||
|
return fs_1.existsSync(this.FIREFOX_BUNDLE_PATH);
|
||
|
}
|
||
|
isNSSInstalled() {
|
||
|
try {
|
||
|
return utils_1.run('brew', ['list', '-1']).toString().includes('\nnss\n');
|
||
|
}
|
||
|
catch (e) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
exports.default = MacOSPlatform;
|
||
|
;
|
||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGFyd2luLmpzIiwic291cmNlUm9vdCI6Ii4vIiwic291cmNlcyI6WyJwbGF0Zm9ybXMvZGFyd2luLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHdEQUF3QjtBQUN4QiwyQkFBNEY7QUFDNUYsMERBQWdDO0FBQ2hDLG1EQUF1RDtBQUN2RCxvQ0FBMkM7QUFFM0MscUNBQXFKO0FBR3JKLE1BQU0sS0FBSyxHQUFHLGVBQVcsQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO0FBRXJELE1BQU0sZUFBZSxHQUFHLEdBQUcsRUFBRSxDQUFDLGNBQUksQ0FBQyxJQUFJLENBQUMsV0FBRyxDQUFDLE1BQU0sRUFBRSxDQUFDLFVBQVUsRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDLElBQUksRUFBRSxFQUFFLEtBQUssRUFBRSxVQUFVLENBQUMsQ0FBQztBQUUvRyxNQUFxQixhQUFhO0lBQWxDO1FBRVUsd0JBQW1CLEdBQUcsMkJBQTJCLENBQUM7UUFDbEQscUJBQWdCLEdBQUcsY0FBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsbUJBQW1CLEVBQUUsd0JBQXdCLENBQUMsQ0FBQztRQUNqRixvQkFBZSxHQUFHLGNBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsZ0RBQWdELENBQUMsQ0FBQztRQUVoRyxtQkFBYyxHQUFHLFlBQVksQ0FBQztJQW9IeEMsQ0FBQztJQWxIQzs7Ozs7O09BTUc7SUFDRyxnQkFBZ0IsQ0FBQyxlQUF1QixFQUFFLFVBQW1CLEVBQUU7O1lBRW5FLCtCQUErQjtZQUMvQixLQUFLLENBQUMsaURBQWlELENBQUMsQ0FBQztZQUN6RCxXQUFHLENBQUMsTUFBTSxFQUFFO2dCQUNWLFVBQVU7Z0JBQ1Ysa0JBQWtCO2dCQUNsQixJQUFJO2dCQUNKLElBQUk7Z0JBQ0osV0FBVztnQkFDWCxJQUFJO2dCQUNKLG9DQUFvQztnQkFDcEMsSUFBSTtnQkFDSixLQUFLO2dCQUNMLElBQUk7Z0JB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
|