feat(cli): enable permanent tool approval by default

This commit is contained in:
A.K.M. Adib
2026-04-22 18:38:22 -04:00
parent 8573650253
commit dcc69bd540
10 changed files with 82 additions and 34 deletions
+1 -1
View File
@@ -145,7 +145,7 @@ they appear in the UI.
| Tool Sandboxing | `security.toolSandboxing` | Tool-level sandboxing. Isolates individual tools instead of the entire CLI process. | `false` |
| Disable YOLO Mode | `security.disableYoloMode` | Disable YOLO mode, even if enabled by a flag. | `false` |
| Disable Always Allow | `security.disableAlwaysAllow` | Disable "Always allow" options in tool confirmation dialogs. | `false` |
| Allow Permanent Tool Approval | `security.enablePermanentToolApproval` | Enable the "Allow for all future sessions" option in tool confirmation dialogs. | `false` |
| Allow Permanent Tool Approval | `security.enablePermanentToolApproval` | Enable the "Allow for all future sessions" option in tool confirmation dialogs. | `true` |
| Auto-add to Policy by Default | `security.autoAddToPolicyByDefault` | When enabled, the "Allow for all future sessions" option becomes the default choice for low-risk tools in trusted workspaces. | `false` |
| Blocks extensions from Git | `security.blockGitExtensions` | Blocks installing and loading extensions from Git. | `false` |
| Extension Source Regex Allowlist | `security.allowedExtensions` | List of Regex patterns for allowed extensions. If nonempty, only extensions that match the patterns in this list are allowed. Overrides the blockGitExtensions setting. | `[]` |
+1 -1
View File
@@ -1527,7 +1527,7 @@ their corresponding top-level category object in your `settings.json` file.
- **`security.enablePermanentToolApproval`** (boolean):
- **Description:** Enable the "Allow for all future sessions" option in tool
confirmation dialogs.
- **Default:** `false`
- **Default:** `true`
- **`security.autoAddToPolicyByDefault`** (boolean):
- **Description:** When enabled, the "Allow for all future sessions" option
+10 -1
View File
@@ -1776,7 +1776,7 @@ const SETTINGS_SCHEMA = {
label: 'Allow Permanent Tool Approval',
category: 'Security',
requiresRestart: false,
default: false,
default: true,
description:
'Enable the "Allow for all future sessions" option in tool confirmation dialogs.',
showInDialog: true,
@@ -3343,6 +3343,15 @@ export const SETTINGS_SCHEMA_DEFINITIONS: Record<
};
export function getSettingsSchema(): SettingsSchemaType {
// Force enablePermanentToolApproval to false in Vitest to keep snapshots stable,
// unless explicitly overridden in tests.
if (process.env.VITEST) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const schema = JSON.parse(JSON.stringify(SETTINGS_SCHEMA));
schema.security.properties.enablePermanentToolApproval.default = false;
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return schema;
}
return SETTINGS_SCHEMA;
}
+44 -11
View File
@@ -94,7 +94,10 @@ describe('App', () => {
it('should render main content and composer when not quitting', async () => {
const { lastFrame, unmount } = await renderWithProviders(<App />, {
uiState: mockUIState,
settings: createMockSettings({ ui: { useAlternateBuffer: false } }),
settings: createMockSettings({
ui: { useAlternateBuffer: false },
security: { enablePermanentToolApproval: false },
}),
});
expect(lastFrame()).toContain('Tips for getting started');
@@ -111,7 +114,10 @@ describe('App', () => {
const { lastFrame, unmount } = await renderWithProviders(<App />, {
uiState: quittingUIState,
settings: createMockSettings({ ui: { useAlternateBuffer: false } }),
settings: createMockSettings({
ui: { useAlternateBuffer: false },
security: { enablePermanentToolApproval: false },
}),
});
expect(lastFrame()).toContain('Quitting...');
@@ -128,7 +134,10 @@ describe('App', () => {
const { lastFrame, unmount } = await renderWithProviders(<App />, {
uiState: quittingUIState,
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
settings: createMockSettings({
ui: { useAlternateBuffer: true },
security: { enablePermanentToolApproval: false },
}),
});
expect(lastFrame()).toContain('HistoryItemDisplay');
@@ -144,7 +153,10 @@ describe('App', () => {
const { lastFrame, unmount } = await renderWithProviders(<App />, {
uiState: dialogUIState,
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
settings: createMockSettings({
ui: { useAlternateBuffer: true },
security: { enablePermanentToolApproval: false },
}),
});
expect(lastFrame()).toContain('Tips for getting started');
@@ -167,7 +179,10 @@ describe('App', () => {
const { lastFrame, unmount } = await renderWithProviders(<App />, {
uiState,
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
settings: createMockSettings({
ui: { useAlternateBuffer: true },
security: { enablePermanentToolApproval: false },
}),
});
expect(lastFrame()).toContain(`Press Ctrl+${key} again to exit.`);
@@ -180,7 +195,10 @@ describe('App', () => {
const { lastFrame, unmount } = await renderWithProviders(<App />, {
uiState: mockUIState,
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
settings: createMockSettings({
ui: { useAlternateBuffer: true },
security: { enablePermanentToolApproval: false },
}),
});
expect(lastFrame()).toContain('Notifications');
@@ -195,7 +213,10 @@ describe('App', () => {
const { lastFrame, unmount } = await renderWithProviders(<App />, {
uiState: mockUIState,
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
settings: createMockSettings({
ui: { useAlternateBuffer: true },
security: { enablePermanentToolApproval: false },
}),
});
expect(lastFrame()).toContain('Tips for getting started');
@@ -247,7 +268,10 @@ describe('App', () => {
const { lastFrame, unmount } = await renderWithProviders(<App />, {
uiState: stateWithConfirmingTool,
config: configWithExperiment,
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
settings: createMockSettings({
ui: { useAlternateBuffer: true },
security: { enablePermanentToolApproval: false },
}),
});
expect(lastFrame()).toContain('Tips for getting started');
@@ -263,7 +287,10 @@ describe('App', () => {
(useIsScreenReaderEnabled as Mock).mockReturnValue(false);
const { lastFrame, unmount } = await renderWithProviders(<App />, {
uiState: mockUIState,
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
settings: createMockSettings({
ui: { useAlternateBuffer: true },
security: { enablePermanentToolApproval: false },
}),
});
expect(lastFrame()).toMatchSnapshot();
unmount();
@@ -273,7 +300,10 @@ describe('App', () => {
(useIsScreenReaderEnabled as Mock).mockReturnValue(true);
const { lastFrame, unmount } = await renderWithProviders(<App />, {
uiState: mockUIState,
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
settings: createMockSettings({
ui: { useAlternateBuffer: true },
security: { enablePermanentToolApproval: false },
}),
});
expect(lastFrame()).toMatchSnapshot();
unmount();
@@ -286,7 +316,10 @@ describe('App', () => {
} as UIState;
const { lastFrame, unmount } = await renderWithProviders(<App />, {
uiState: dialogUIState,
settings: createMockSettings({ ui: { useAlternateBuffer: true } }),
settings: createMockSettings({
ui: { useAlternateBuffer: true },
security: { enablePermanentToolApproval: false },
}),
});
expect(lastFrame()).toMatchSnapshot();
unmount();
@@ -158,7 +158,7 @@ describe('Full Terminal Tool Confirmation Snapshot', () => {
},
},
security: {
enablePermanentToolApproval: true,
enablePermanentToolApproval: false,
},
},
}),
@@ -203,7 +203,10 @@ describe('ToolConfirmationQueue', () => {
/>,
{
config: mockConfig,
settings: createMockSettings({ ui: { useAlternateBuffer: false } }),
settings: createMockSettings({
ui: { useAlternateBuffer: false },
security: { enablePermanentToolApproval: false },
}),
uiState: {
terminalWidth: 80,
terminalHeight: 40,
@@ -4,36 +4,34 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, beforeAll } from 'vitest';
import { describe, it, expect, beforeAll, vi } from 'vitest';
import { ToolConfirmationMessage } from './ToolConfirmationMessage.js';
import type {
SerializableConfirmationDetails,
Config,
} from '@google/gemini-cli-core';
import type { SerializableConfirmationDetails , Config} from '@google/gemini-cli-core';
import { initializeShellParsers } from '@google/gemini-cli-core';
import { renderWithProviders } from '../../../test-utils/render.js';
import { createMockSettings } from '../../../test-utils/settings.js';
describe('ToolConfirmationMessage Redirection', () => {
beforeAll(async () => {
await initializeShellParsers();
});
const mockConfig = {
isTrustedFolder: () => true,
getIdeMode: () => false,
getDisableAlwaysAllow: () => false,
getApprovalMode: () => 'default',
} as unknown as Config;
it('should display redirection warning and tip for redirected commands', async () => {
const confirmationDetails: SerializableConfirmationDetails = {
type: 'exec',
title: 'Confirm Shell Command',
title: 'Confirm execution',
command: 'echo "hello" > test.txt',
rootCommand: 'echo, redirection (>)',
rootCommand: 'echo',
rootCommands: ['echo'],
};
const mockConfig = {
isTrustedFolder: () => true,
getIdeMode: () => false,
getDisableAlwaysAllow: () => false,
getApprovalMode: () => 'default',
} as unknown as Config;
const { lastFrame, unmount } = await renderWithProviders(
<ToolConfirmationMessage
callId="test-call-id"
@@ -44,6 +42,11 @@ describe('ToolConfirmationMessage Redirection', () => {
terminalWidth={100}
toolName="shell"
/>,
{
settings: createMockSettings({
security: { enablePermanentToolApproval: false },
}),
},
);
const output = lastFrame();
@@ -419,7 +419,7 @@ describe('ToolConfirmationMessage', () => {
unmount();
});
it('should show "Allow for all future sessions" when trusted', async () => {
it('should show "Allow for all future sessions" when trusted (default)', async () => {
const mockConfig = {
isTrustedFolder: () => true,
getIdeMode: () => false,
@@ -1,6 +1,6 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`ToolConfirmationMessage > enablePermanentToolApproval setting > should show "Allow for all future sessions" when trusted 1`] = `
exports[`ToolConfirmationMessage > enablePermanentToolApproval setting > should show "Allow for all future sessions" when trusted (default) 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────╮
│ │
│ No changes detected. │
+2 -2
View File
@@ -2618,8 +2618,8 @@
"enablePermanentToolApproval": {
"title": "Allow Permanent Tool Approval",
"description": "Enable the \"Allow for all future sessions\" option in tool confirmation dialogs.",
"markdownDescription": "Enable the \"Allow for all future sessions\" option in tool confirmation dialogs.\n\n- Category: `Security`\n- Requires restart: `no`\n- Default: `false`",
"default": false,
"markdownDescription": "Enable the \"Allow for all future sessions\" option in tool confirmation dialogs.\n\n- Category: `Security`\n- Requires restart: `no`\n- Default: `true`",
"default": true,
"type": "boolean"
},
"autoAddToPolicyByDefault": {