diff --git a/packages/cli/src/commands/extensions/install.ts b/packages/cli/src/commands/extensions/install.ts index e2fbcb84f7..e9f0c59be2 100644 --- a/packages/cli/src/commands/extensions/install.ts +++ b/packages/cli/src/commands/extensions/install.ts @@ -6,6 +6,7 @@ import type { CommandModule } from 'yargs'; import { + INSTALL_WARNING_MESSAGE, installOrUpdateExtension, requestConsentNonInteractive, } from '../../config/extension.js'; @@ -18,6 +19,7 @@ interface InstallArgs { ref?: string; autoUpdate?: boolean; allowPreRelease?: boolean; + consent?: boolean; } export async function handleInstall(args: InstallArgs) { @@ -54,9 +56,16 @@ export async function handleInstall(args: InstallArgs) { } } + const requestConsent = args.consent + ? () => Promise.resolve(true) + : requestConsentNonInteractive; + if (args.consent) { + console.log('You have consented to the following:'); + console.log(INSTALL_WARNING_MESSAGE); + } const name = await installOrUpdateExtension( installMetadata, - requestConsentNonInteractive, + requestConsent, ); console.log(`Extension "${name}" installed successfully and enabled.`); } catch (error) { @@ -87,6 +96,12 @@ export const installCommand: CommandModule = { describe: 'Enable pre-release versions for this extension.', type: 'boolean', }) + .option('consent', { + describe: + 'Acknowledge the security risks of installing an extension and skip the confirmation prompt.', + type: 'boolean', + default: false, + }) .check((argv) => { if (!argv.source) { throw new Error('The source argument must be provided.'); @@ -99,6 +114,7 @@ export const installCommand: CommandModule = { ref: argv['ref'] as string | undefined, autoUpdate: argv['auto-update'] as boolean | undefined, allowPreRelease: argv['pre-release'] as boolean | undefined, + consent: argv['consent'] as boolean | undefined, }); }, }; diff --git a/packages/cli/src/config/extension.test.ts b/packages/cli/src/config/extension.test.ts index 4aa1ad7081..eb3136924b 100644 --- a/packages/cli/src/config/extension.test.ts +++ b/packages/cli/src/config/extension.test.ts @@ -12,6 +12,7 @@ import { EXTENSIONS_CONFIG_FILENAME, ExtensionStorage, INSTALL_METADATA_FILENAME, + INSTALL_WARNING_MESSAGE, annotateActiveExtensions, disableExtension, enableExtension, @@ -972,7 +973,7 @@ describe('extension tests', () => { expect(mockRequestConsent).toHaveBeenCalledWith( `Installing extension "my-local-extension". -**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.** +${INSTALL_WARNING_MESSAGE} This extension will run the following MCP servers: * test-server (local): node dobadthing \\u001b[12D\\u001b[K server.js * test-server-2 (remote): https://google.com`, diff --git a/packages/cli/src/config/extension.ts b/packages/cli/src/config/extension.ts index 2d097120c4..f3ecba81b9 100644 --- a/packages/cli/src/config/extension.ts +++ b/packages/cli/src/config/extension.ts @@ -50,7 +50,8 @@ export const EXTENSIONS_DIRECTORY_NAME = path.join(GEMINI_DIR, 'extensions'); export const EXTENSIONS_CONFIG_FILENAME = 'gemini-extension.json'; export const INSTALL_METADATA_FILENAME = '.gemini-extension-install.json'; - +export const INSTALL_WARNING_MESSAGE = + '**The extension you are about to install may have been created by a third-party developer and sourced from a public repository. Google does not vet, endorse, or guarantee the functionality or security of extensions. Please carefully inspect any extension and its source code before installing to understand the permissions it requires and the actions it may perform.**'; /** * Extension definition as written to disk in gemini-extension.json files. * This should *not* be referenced outside of the logic for reading files. @@ -623,9 +624,7 @@ function extensionConsentString(extensionConfig: ExtensionConfig): string { const output: string[] = []; const mcpServerEntries = Object.entries(sanitizedConfig.mcpServers || {}); output.push(`Installing extension "${sanitizedConfig.name}".`); - output.push( - '**Extensions may introduce unexpected behavior. Ensure you have investigated the extension source and trust the author.**', - ); + output.push(INSTALL_WARNING_MESSAGE); if (mcpServerEntries.length) { output.push('This extension will run the following MCP servers:');