mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 13:22:35 -07:00
feat(admin): Introduce remote admin settings & implement secureModeEnabled/mcpEnabled (#15935)
This commit is contained in:
@@ -1069,7 +1069,7 @@ describe('Approval mode tool exclusion logic', () => {
|
||||
};
|
||||
|
||||
await expect(loadCliConfig(settings, 'test-session', argv)).rejects.toThrow(
|
||||
'Cannot start in YOLO mode when it is disabled by settings',
|
||||
'Cannot start in YOLO mode since it is disabled by your admin',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2412,3 +2412,134 @@ describe('Policy Engine Integration in loadCliConfig', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadCliConfig secureModeEnabled', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
||||
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
||||
vi.spyOn(ExtensionManager.prototype, 'getExtensions').mockReturnValue([]);
|
||||
vi.mocked(isWorkspaceTrusted).mockReturnValue({
|
||||
isTrusted: true,
|
||||
source: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should throw an error if YOLO mode is attempted when secureModeEnabled is true', async () => {
|
||||
process.argv = ['node', 'script.js', '--yolo'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = {
|
||||
admin: {
|
||||
secureModeEnabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
await expect(loadCliConfig(settings, 'test-session', argv)).rejects.toThrow(
|
||||
'Cannot start in YOLO mode since it is disabled by your admin',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if approval-mode=yolo is attempted when secureModeEnabled is true', async () => {
|
||||
process.argv = ['node', 'script.js', '--approval-mode=yolo'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = {
|
||||
admin: {
|
||||
secureModeEnabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
await expect(loadCliConfig(settings, 'test-session', argv)).rejects.toThrow(
|
||||
'Cannot start in YOLO mode since it is disabled by your admin',
|
||||
);
|
||||
});
|
||||
|
||||
it('should set disableYoloMode to true when secureModeEnabled is true', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = {
|
||||
admin: {
|
||||
secureModeEnabled: true,
|
||||
},
|
||||
};
|
||||
const config = await loadCliConfig(settings, 'test-session', argv);
|
||||
expect(config.isYoloModeDisabled()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadCliConfig mcpEnabled', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
||||
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
|
||||
vi.spyOn(ExtensionManager.prototype, 'getExtensions').mockReturnValue([]);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
const mcpSettings = {
|
||||
mcp: {
|
||||
serverCommand: 'mcp-server',
|
||||
allowed: ['serverA'],
|
||||
excluded: ['serverB'],
|
||||
},
|
||||
mcpServers: { serverA: { url: 'http://a' } },
|
||||
};
|
||||
|
||||
it('should enable MCP by default', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = { ...mcpSettings };
|
||||
const config = await loadCliConfig(settings, 'test-session', argv);
|
||||
expect(config.getMcpEnabled()).toBe(true);
|
||||
expect(config.getMcpServerCommand()).toBe('mcp-server');
|
||||
expect(config.getMcpServers()).toEqual({ serverA: { url: 'http://a' } });
|
||||
expect(config.getAllowedMcpServers()).toEqual(['serverA']);
|
||||
expect(config.getBlockedMcpServers()).toEqual(['serverB']);
|
||||
});
|
||||
|
||||
it('should disable MCP when mcpEnabled is false', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = {
|
||||
...mcpSettings,
|
||||
admin: {
|
||||
mcp: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
const config = await loadCliConfig(settings, 'test-session', argv);
|
||||
expect(config.getMcpEnabled()).toBe(false);
|
||||
expect(config.getMcpServerCommand()).toBeUndefined();
|
||||
expect(config.getMcpServers()).toEqual({});
|
||||
expect(config.getAllowedMcpServers()).toEqual([]);
|
||||
expect(config.getBlockedMcpServers()).toEqual([]);
|
||||
});
|
||||
|
||||
it('should enable MCP when mcpEnabled is true', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = {
|
||||
...mcpSettings,
|
||||
admin: {
|
||||
mcp: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
const config = await loadCliConfig(settings, 'test-session', argv);
|
||||
expect(config.getMcpEnabled()).toBe(true);
|
||||
expect(config.getMcpServerCommand()).toBe('mcp-server');
|
||||
expect(config.getMcpServers()).toEqual({ serverA: { url: 'http://a' } });
|
||||
expect(config.getAllowedMcpServers()).toEqual(['serverA']);
|
||||
expect(config.getBlockedMcpServers()).toEqual(['serverB']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -505,11 +505,19 @@ export async function loadCliConfig(
|
||||
}
|
||||
|
||||
// Override approval mode if disableYoloMode is set.
|
||||
if (settings.security?.disableYoloMode) {
|
||||
if (settings.security?.disableYoloMode || settings.admin?.secureModeEnabled) {
|
||||
if (approvalMode === ApprovalMode.YOLO) {
|
||||
debugLogger.error('YOLO mode is disabled by the "disableYolo" setting.');
|
||||
if (settings.admin?.secureModeEnabled) {
|
||||
debugLogger.error(
|
||||
'YOLO mode is disabled by "secureModeEnabled" setting.',
|
||||
);
|
||||
} else {
|
||||
debugLogger.error(
|
||||
'YOLO mode is disabled by the "disableYolo" setting.',
|
||||
);
|
||||
}
|
||||
throw new FatalConfigError(
|
||||
'Cannot start in YOLO mode when it is disabled by settings',
|
||||
'Cannot start in YOLO mode since it is disabled by your admin',
|
||||
);
|
||||
}
|
||||
approvalMode = ApprovalMode.DEFAULT;
|
||||
@@ -628,6 +636,8 @@ export async function loadCliConfig(
|
||||
|
||||
const ptyInfo = await getPty();
|
||||
|
||||
const mcpEnabled = settings.admin?.mcp?.enabled ?? true;
|
||||
|
||||
return new Config({
|
||||
sessionId,
|
||||
embeddingModel: DEFAULT_GEMINI_EMBEDDING_MODEL,
|
||||
@@ -646,12 +656,17 @@ export async function loadCliConfig(
|
||||
excludeTools,
|
||||
toolDiscoveryCommand: settings.tools?.discoveryCommand,
|
||||
toolCallCommand: settings.tools?.callCommand,
|
||||
mcpServerCommand: settings.mcp?.serverCommand,
|
||||
mcpServers: settings.mcpServers,
|
||||
allowedMcpServers: argv.allowedMcpServerNames ?? settings.mcp?.allowed,
|
||||
blockedMcpServers: argv.allowedMcpServerNames
|
||||
? undefined
|
||||
: settings.mcp?.excluded,
|
||||
mcpServerCommand: mcpEnabled ? settings.mcp?.serverCommand : undefined,
|
||||
mcpServers: mcpEnabled ? settings.mcpServers : {},
|
||||
mcpEnabled,
|
||||
allowedMcpServers: mcpEnabled
|
||||
? (argv.allowedMcpServerNames ?? settings.mcp?.allowed)
|
||||
: undefined,
|
||||
blockedMcpServers: mcpEnabled
|
||||
? argv.allowedMcpServerNames
|
||||
? undefined
|
||||
: settings.mcp?.excluded
|
||||
: undefined,
|
||||
blockedEnvironmentVariables:
|
||||
settings.security?.environmentVariableRedaction?.blocked,
|
||||
enableEnvironmentVariableRedaction:
|
||||
@@ -660,7 +675,8 @@ export async function loadCliConfig(
|
||||
geminiMdFileCount: fileCount,
|
||||
geminiMdFilePaths: filePaths,
|
||||
approvalMode,
|
||||
disableYoloMode: settings.security?.disableYoloMode,
|
||||
disableYoloMode:
|
||||
settings.security?.disableYoloMode || settings.admin?.secureModeEnabled,
|
||||
showMemoryUsage: settings.ui?.showMemoryUsage || false,
|
||||
accessibility: {
|
||||
...settings.ui?.accessibility,
|
||||
|
||||
@@ -1718,6 +1718,74 @@ const SETTINGS_SCHEMA = {
|
||||
mergeStrategy: MergeStrategy.CONCAT,
|
||||
},
|
||||
},
|
||||
|
||||
admin: {
|
||||
type: 'object',
|
||||
label: 'Admin',
|
||||
category: 'Admin',
|
||||
requiresRestart: false,
|
||||
default: {},
|
||||
description: 'Settings configured remotely by enterprise admins.',
|
||||
showInDialog: false,
|
||||
mergeStrategy: MergeStrategy.REPLACE,
|
||||
properties: {
|
||||
secureModeEnabled: {
|
||||
type: 'boolean',
|
||||
label: 'Secure Mode Enabled',
|
||||
category: 'Admin',
|
||||
requiresRestart: false,
|
||||
default: false,
|
||||
description: 'If true, disallows yolo mode from being used.',
|
||||
showInDialog: false,
|
||||
mergeStrategy: MergeStrategy.REPLACE,
|
||||
},
|
||||
extensions: {
|
||||
type: 'object',
|
||||
label: 'Extensions Settings',
|
||||
category: 'Admin',
|
||||
requiresRestart: false,
|
||||
default: {},
|
||||
description: 'Extensions-specific admin settings.',
|
||||
showInDialog: false,
|
||||
mergeStrategy: MergeStrategy.REPLACE,
|
||||
properties: {
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
label: 'Extensions Enabled',
|
||||
category: 'Admin',
|
||||
requiresRestart: false,
|
||||
default: true,
|
||||
description:
|
||||
'If false, disallows extensions from being installed or used.',
|
||||
showInDialog: false,
|
||||
mergeStrategy: MergeStrategy.REPLACE,
|
||||
},
|
||||
},
|
||||
},
|
||||
mcp: {
|
||||
type: 'object',
|
||||
label: 'MCP Settings',
|
||||
category: 'Admin',
|
||||
requiresRestart: false,
|
||||
default: {},
|
||||
description: 'MCP-specific admin settings.',
|
||||
showInDialog: false,
|
||||
mergeStrategy: MergeStrategy.REPLACE,
|
||||
properties: {
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
label: 'MCP Enabled',
|
||||
category: 'Admin',
|
||||
requiresRestart: false,
|
||||
default: true,
|
||||
description: 'If false, disallows MCP servers from being used.',
|
||||
showInDialog: false,
|
||||
mergeStrategy: MergeStrategy.REPLACE,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const satisfies SettingsSchema;
|
||||
|
||||
export type SettingsSchemaType = typeof SETTINGS_SCHEMA;
|
||||
|
||||
Reference in New Issue
Block a user