diff --git a/src/hooks/verifyInstallSignature.ts b/src/hooks/verifyInstallSignature.ts index 8dcefbe5..45bfc223 100644 --- a/src/hooks/verifyInstallSignature.ts +++ b/src/hooks/verifyInstallSignature.ts @@ -6,7 +6,7 @@ */ import { Hook } from '@oclif/core'; -import { Logger } from '@salesforce/core'; +import { Logger, Messages } from '@salesforce/core'; import { ux } from '@oclif/core'; import { ConfigContext, @@ -14,6 +14,7 @@ import { doPrompt, InstallationVerification, VerificationConfig, + isAllowListed, } from '../shared/installationVerification.js'; import { NpmName } from '../shared/NpmName.js'; @@ -43,7 +44,7 @@ export const hook: Hook.PluginsPreinstall = async function (options) { npmName.tag = npmName.tag.slice(1); } - const configContext: ConfigContext = { + const configContext = { cacheDir: options.config.cacheDir, configDir: options.config.configDir, dataDir: options.config.dataDir, @@ -64,8 +65,20 @@ export const hook: Hook.PluginsPreinstall = async function (options) { logger.debug(error.message); this.error(error); } + } else if (options.plugin.url) { + const isAllowed = await isAllowListed({ + logger: await Logger.child('verifyInstallSignature'), + name: options.plugin.url, + configPath: options.config.configDir, + }); + if (isAllowed) { + const messages = Messages.loadMessages('@salesforce/plugin-trust', 'verify'); + ux.log(messages.getMessage('SkipSignatureCheck', [options.plugin.url])); + } else { + await doPrompt(options.plugin.url); + } } else { - await doPrompt(options.plugin.url); + await doPrompt(); } }; diff --git a/src/shared/installationVerification.ts b/src/shared/installationVerification.ts index 07769f22..44a95214 100644 --- a/src/shared/installationVerification.ts +++ b/src/shared/installationVerification.ts @@ -156,6 +156,32 @@ const errorHandlerForVerify = (err: Error): Error => { export const getNpmRegistry = (): URL => new URL(process.env.SF_NPM_REGISTRY ?? process.env.SFDX_NPM_REGISTRY ?? DEFAULT_REGISTRY); +export async function isAllowListed({ + logger, + configPath, + name, +}: { + logger: Logger; + configPath: string; + name?: string; +}): Promise { + const allowListedFilePath = path.join(configPath, ALLOW_LIST_FILENAME); + logger.debug(`isAllowListed | allowlistFilePath: ${allowListedFilePath}`); + let fileContent: string; + try { + fileContent = await fs.promises.readFile(allowListedFilePath, 'utf8'); + const allowlistArray = JSON.parse(fileContent) as string[]; + logger.debug('isAllowListed | Successfully parsed allowlist.'); + return name ? allowlistArray.includes(name) : false; + } catch (err) { + if (err instanceof Error && 'code' in err && err.code === 'ENOENT') { + return false; + } else { + throw err; + } + } +} + /** * class for verifying a digital signature pack of an npm */ @@ -236,23 +262,11 @@ export class InstallationVerification implements Verifier { } public async isAllowListed(): Promise { - const logger = await this.getLogger(); - const allowListedFilePath = path.join(this.getConfigPath() ?? '', ALLOW_LIST_FILENAME); - logger.debug(`isAllowListed | allowlistFilePath: ${allowListedFilePath}`); - let fileContent: string; - try { - fileContent = await fs.promises.readFile(allowListedFilePath, 'utf8'); - const allowlistArray = JSON.parse(fileContent) as string[]; - logger.debug('isAllowListed | Successfully parsed allowlist.'); - const nameToFind = this.pluginNpmName?.toString(); - return nameToFind ? allowlistArray.includes(nameToFind) : false; - } catch (err) { - if (err instanceof Error && 'code' in err && err.code === 'ENOENT') { - return false; - } else { - throw err; - } - } + return isAllowListed({ + logger: await this.getLogger(), + configPath: this.getConfigPath() ?? '', + name: this.pluginNpmName?.toString(), + }); } /**