@salesforce/plugin-auth
Version:
plugin for sf auth commands
166 lines • 7.46 kB
JavaScript
/*
* Copyright (c) 2022, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import childProcess from 'node:child_process';
import util from 'node:util';
import { join } from 'node:path';
import fs from 'node:fs';
import { Global, Lifecycle, Logger, Messages } from '@salesforce/core';
import { asString, isString } from '@salesforce/ts-types';
import { parseJsonMap } from '@salesforce/kit';
let logger;
const getLogger = () => {
if (!logger) {
logger = Logger.childFromRoot('plugin-auth-diagnostics');
}
return logger;
};
const pluginName = '@salesforce/plugin-auth';
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages(pluginName, 'diagnostics');
let exec;
export const hook = async (options) => {
getLogger().debug(`Running SfDoctor diagnostics for ${pluginName}`);
exec = util.promisify(childProcess.exec);
try {
await exec('npm -v');
return await Promise.all([cryptoVersionTest(options.doctor)]);
}
catch (e) {
const errMsg = e instanceof Error ? e.message : isString(e) ? e : 'unknown';
getLogger().warn(`Unable to run SfDoctor diagnostics for ${pluginName} due to: ${errMsg}`);
return Promise.resolve([undefined]);
}
};
// ============================
// *** DIAGNOSTIC TESTS ***
// ============================
// Detects if the auth key used is crypto v1 or v2
// Detects if the SF_CRYPTO_V2 env var is set and if it matches the key crypto version
const cryptoVersionTest = async (doctor) => {
getLogger().debug('Running Crypto Version tests');
const sfCryptoV2Support = await supportsCliV2Crypto(doctor);
let cryptoVersion = 'unknown';
const sfCryptoV2 = process.env.SF_CRYPTO_V2;
const isUsingGenericKeychain = process.platform === 'win32' ||
process.env.SF_USE_GENERIC_UNIX_KEYCHAIN?.toLowerCase() === 'true' ||
process.env.USE_GENERIC_UNIX_KEYCHAIN?.toLowerCase() === 'true';
// If the CLI is using key.json, we can read the file and get the key length
// to discover the crypto version being used. If not, then we can't detect it.
if (isUsingGenericKeychain) {
try {
const keyFile = join(Global.DIR, 'key.json');
const key = asString(parseJsonMap(fs.readFileSync(keyFile, 'utf8'))?.key);
cryptoVersion = key?.length === 64 ? 'v2' : key?.length === 32 ? 'v1' : 'unknown';
}
catch (e) {
const errMsg = e instanceof Error ? e.message : isString(e) ? e : 'unknown';
getLogger().debug(`Could not detect key size due to:\n${errMsg}`);
}
}
doctor.addPluginData(pluginName, {
sfCryptoV2,
isUsingGenericKeychain,
sfCryptoV2Support,
cryptoVersion,
});
const testName1 = `[${pluginName}] CLI supports v2 crypto`;
let status1 = 'pass';
if (!sfCryptoV2Support) {
status1 = 'fail';
doctor.addSuggestion(messages.getMessage('sfCryptoV2Support'));
}
void Lifecycle.getInstance().emit('Doctor:diagnostic', { testName: testName1, status: status1 });
// Only do this test if we know they are using v2 crypto
if (cryptoVersion === 'v2') {
const testName2 = `[${pluginName}] CLI using stable v2 crypto`;
let status2 = 'pass';
if (!sfCryptoV2Support) {
status2 = 'fail';
doctor.addSuggestion(messages.getMessage('sfCryptoV2Unstable'));
}
void Lifecycle.getInstance().emit('Doctor:diagnostic', { testName: testName2, status: status2 });
}
// Only do this test if we know they are using v1 crypto
if (cryptoVersion === 'v1') {
const testName3 = `[${pluginName}] CLI using stable v1 crypto`;
let status3 = 'pass';
if (sfCryptoV2?.toLowerCase() === 'true') {
// They have SF_CRYPTO_V2=true but are using v1 crypto. They might not know this
// or know how to generate a v2 key.
if (sfCryptoV2Support) {
status3 = 'warn';
doctor.addSuggestion(messages.getMessage('sfCryptoV2Desired'));
}
}
void Lifecycle.getInstance().emit('Doctor:diagnostic', { testName: testName3, status: status3 });
}
};
// Inspect CLI install and plugins to ensure all versions of `@salesforce/core` can support v2 crypto.
// This uses `npm explain @salesforce/core` to ensure all versions are greater than 6.6.0.
const supportsCliV2Crypto = async (doctor) => {
const diagnosis = doctor.getDiagnosis();
let coreSupportsV2 = false;
let pluginsSupportV2 = false;
let linksSupportsV2 = false;
const { root, dataDir } = diagnosis.cliConfig;
// check core CLI
if (root?.length) {
try {
const { stdout } = await exec('npm explain @salesforce/core --json', { cwd: root });
const coreExplanation = JSON.parse(stdout);
coreSupportsV2 = coreExplanation.every((exp) => exp?.version > '6.6.0');
}
catch (e) {
const errMsg = e instanceof Error ? e.message : isString(e) ? e : 'unknown';
getLogger().debug(`Cannot determine CLI v2 crypto core support due to: ${errMsg}`);
}
}
// check installed plugins
if (dataDir?.length) {
try {
const { stdout } = await exec('npm explain @salesforce/core --json', { cwd: dataDir });
const pluginsExplanation = JSON.parse(stdout);
pluginsSupportV2 = pluginsExplanation?.length ? pluginsExplanation.every((exp) => exp?.version > '6.6.0') : true;
}
catch (e) {
const errMsg = e instanceof Error ? e.message : isString(e) ? e : 'unknown';
if (errMsg.includes('No dependencies found matching @salesforce/core')) {
pluginsSupportV2 = true;
}
else {
getLogger().debug(`Cannot determine CLI v2 crypto installed plugins support due to: ${errMsg}`);
}
}
}
// check linked plugins
const pluginVersions = diagnosis?.versionDetail?.pluginVersions;
const linkedPlugins = pluginVersions.filter((pv) => pv.includes('(link)'));
if (linkedPlugins?.length) {
try {
const coreVersionChecks = await Promise.all(linkedPlugins.map(async (pluginEntry) => {
// last entry is the path. E.g., "auth 3.3.17 (link) /Users/me/dev/plugin-auth",
const pluginPath = pluginEntry.split(' ')?.pop();
if (pluginPath?.length) {
const { stdout } = await exec('npm explain @salesforce/core --json', { cwd: pluginPath });
const linksExplanation = JSON.parse(stdout);
return linksExplanation?.every((exp) => exp?.version > '6.6.0');
}
return true;
}));
linksSupportsV2 = !coreVersionChecks.includes(false);
}
catch (e) {
const errMsg = e instanceof Error ? e.message : isString(e) ? e : 'unknown';
getLogger().debug(`Cannot determine CLI v2 crypto linked plugins support due to: ${errMsg}`);
}
}
else {
linksSupportsV2 = true;
}
return coreSupportsV2 && pluginsSupportV2 && linksSupportsV2;
};
//# sourceMappingURL=diagnostics.js.map