Add setting to disable YOLO mode (#11609)

Co-authored-by: Shreya Keshive <shreyakeshive@google.com>
This commit is contained in:
Adib234
2025-10-22 11:57:10 -07:00
committed by GitHub
parent 5bb9cd1a13
commit 6d75005afc
10 changed files with 203 additions and 1 deletions

View File

@@ -1112,6 +1112,23 @@ describe('Approval mode tool exclusion logic', () => {
expect(excludedTools).not.toContain(WRITE_FILE_TOOL_NAME); // Should be allowed in auto_edit
});
it('should throw an error if YOLO mode is attempted when disableYoloMode is true', async () => {
process.argv = ['node', 'script.js', '--yolo'];
const argv = await parseArguments({} as Settings);
const settings: Settings = {
security: {
disableYoloMode: true,
},
};
const extensions: GeminiCLIExtension[] = [];
await expect(
loadCliConfig(settings, extensions, 'test-session', argv),
).rejects.toThrow(
'Cannot start in YOLO mode when it is disabled by settings',
);
});
it('should throw an error for invalid approval mode values in loadCliConfig', async () => {
// Create a mock argv with an invalid approval mode that bypasses argument parsing validation
const invalidArgv: Partial<CliArgs> & { approvalMode: string } = {

View File

@@ -442,6 +442,21 @@ export async function loadCliConfig(
argv.yolo || false ? ApprovalMode.YOLO : ApprovalMode.DEFAULT;
}
// Override approval mode if disableYoloMode is set.
if (settings.security?.disableYoloMode) {
if (approvalMode === ApprovalMode.YOLO) {
debugLogger.error('YOLO mode is disabled by the "disableYolo" setting.');
throw new FatalConfigError(
'Cannot start in YOLO mode when it is disabled by settings',
);
}
approvalMode = ApprovalMode.DEFAULT;
} else if (approvalMode === ApprovalMode.YOLO) {
debugLogger.warn(
'YOLO mode is enabled. All tool calls will be automatically approved.',
);
}
// Force approval mode to default if the folder is not trusted.
if (!trustedFolder && approvalMode !== ApprovalMode.DEFAULT) {
debugLogger.warn(
@@ -583,6 +598,7 @@ export async function loadCliConfig(
geminiMdFileCount: fileCount,
geminiMdFilePaths: filePaths,
approvalMode,
disableYoloMode: settings.security?.disableYoloMode,
showMemoryUsage: settings.ui?.showMemoryUsage || false,
accessibility: {
...settings.ui?.accessibility,

View File

@@ -609,6 +609,40 @@ describe('Settings Loading and Merging', () => {
expect(settings.merged.security?.folderTrust?.enabled).toBe(true); // System setting should be used
});
it('should not allow user or workspace to override system disableYoloMode', () => {
(mockFsExistsSync as Mock).mockReturnValue(true);
const userSettingsContent = {
security: {
disableYoloMode: false,
},
};
const workspaceSettingsContent = {
security: {
disableYoloMode: false, // This should be ignored
},
};
const systemSettingsContent = {
security: {
disableYoloMode: true,
},
};
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === getSystemSettingsPath())
return JSON.stringify(systemSettingsContent);
if (p === USER_SETTINGS_PATH)
return JSON.stringify(userSettingsContent);
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
return JSON.stringify(workspaceSettingsContent);
return '{}';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.merged.security?.disableYoloMode).toBe(true); // System setting should be used
});
it('should handle contextFileName correctly when only in user settings', () => {
(mockFsExistsSync as Mock).mockImplementation(
(p: fs.PathLike) => p === USER_SETTINGS_PATH,

View File

@@ -937,6 +937,15 @@ const SETTINGS_SCHEMA = {
description: 'Security-related settings.',
showInDialog: false,
properties: {
disableYoloMode: {
type: 'boolean',
label: 'Disable YOLO Mode',
category: 'Security',
requiresRestart: true,
default: false,
description: 'Disable YOLO mode, even if enabled by a flag.',
showInDialog: true,
},
folderTrust: {
type: 'object',
label: 'Folder Trust',