From a6c7affedbe529cb73c3408da9e665ed2adcf7a0 Mon Sep 17 00:00:00 2001 From: Gal Zahavi <38544478+galz10@users.noreply.github.com> Date: Tue, 24 Mar 2026 18:46:15 -0700 Subject: [PATCH] fix(core): conditionally expose additional_permissions in shell tool (#23729) Co-authored-by: Sandy Tao --- packages/core/src/policy/policy-engine.ts | 9 --- .../core/src/tools/definitions/coreTools.ts | 8 ++- .../coreToolsModelSnapshots.test.ts | 2 +- .../dynamic-declaration-helpers.ts | 59 ++++++++++--------- .../model-family-sets/default-legacy.ts | 12 +++- .../definitions/model-family-sets/gemini-3.ts | 12 +++- packages/core/src/tools/definitions/types.ts | 1 + packages/core/src/tools/shell.test.ts | 1 + packages/core/src/tools/shell.ts | 2 + 9 files changed, 64 insertions(+), 42 deletions(-) diff --git a/packages/core/src/policy/policy-engine.ts b/packages/core/src/policy/policy-engine.ts index c1709248fe..4a1dc879af 100644 --- a/packages/core/src/policy/policy-engine.ts +++ b/packages/core/src/policy/policy-engine.ts @@ -702,15 +702,6 @@ export class PolicyEngine { } } - // Sandbox Expansion requests MUST always be confirmed by the user, - // even if the base command is otherwise ALLOWED by the policy engine. - if ( - decision === PolicyDecision.ALLOW && - toolCall.args?.['additional_permissions'] - ) { - decision = PolicyDecision.ASK_USER; - } - return { decision: this.applyNonInteractiveMode(decision), rule: matchedRule, diff --git a/packages/core/src/tools/definitions/coreTools.ts b/packages/core/src/tools/definitions/coreTools.ts index 9204f9240e..85fc9906e6 100644 --- a/packages/core/src/tools/definitions/coreTools.ts +++ b/packages/core/src/tools/definitions/coreTools.ts @@ -233,13 +233,19 @@ export { export function getShellDefinition( enableInteractiveShell: boolean, enableEfficiency: boolean, + enableToolSandboxing: boolean = false, ): ToolDefinition { return { - base: getShellDeclaration(enableInteractiveShell, enableEfficiency), + base: getShellDeclaration( + enableInteractiveShell, + enableEfficiency, + enableToolSandboxing, + ), overrides: (modelId) => getToolSet(modelId).run_shell_command( enableInteractiveShell, enableEfficiency, + enableToolSandboxing, ), }; } diff --git a/packages/core/src/tools/definitions/coreToolsModelSnapshots.test.ts b/packages/core/src/tools/definitions/coreToolsModelSnapshots.test.ts index 6ccea4274c..d1f98fd020 100644 --- a/packages/core/src/tools/definitions/coreToolsModelSnapshots.test.ts +++ b/packages/core/src/tools/definitions/coreToolsModelSnapshots.test.ts @@ -69,7 +69,7 @@ describe('coreTools snapshots for specific models', () => { { name: 'list_directory', definition: LS_DEFINITION }, { name: 'run_shell_command', - definition: getShellDefinition(true, true), + definition: getShellDefinition(true, true, true), }, { name: 'replace', definition: EDIT_DEFINITION }, { name: 'google_web_search', definition: WEB_SEARCH_DEFINITION }, diff --git a/packages/core/src/tools/definitions/dynamic-declaration-helpers.ts b/packages/core/src/tools/definitions/dynamic-declaration-helpers.ts index e33d42311a..530f908977 100644 --- a/packages/core/src/tools/definitions/dynamic-declaration-helpers.ts +++ b/packages/core/src/tools/definitions/dynamic-declaration-helpers.ts @@ -81,6 +81,7 @@ export function getCommandDescription(): string { export function getShellDeclaration( enableInteractiveShell: boolean, enableEfficiency: boolean, + enableToolSandboxing: boolean = false, ): FunctionDeclaration { return { name: SHELL_TOOL_NAME, @@ -110,35 +111,39 @@ export function getShellDeclaration( description: 'Set to true if this command should be run in the background (e.g. for long-running servers or watchers). The command will be started, allowed to run for a brief moment to check for immediate errors, and then moved to the background.', }, - [PARAM_ADDITIONAL_PERMISSIONS]: { - type: 'object', - description: - 'Sandbox permissions for the command. Use this to request additional sandboxed filesystem or network permissions if a previous command failed with "Operation not permitted".', - properties: { - network: { - type: 'boolean', - description: - 'Set to true to enable network access for this command.', - }, - fileSystem: { - type: 'object', - properties: { - read: { - type: 'array', - items: { type: 'string' }, - description: - 'List of additional absolute paths to allow reading.', - }, - write: { - type: 'array', - items: { type: 'string' }, - description: - 'List of additional absolute paths to allow writing.', + ...(enableToolSandboxing + ? { + [PARAM_ADDITIONAL_PERMISSIONS]: { + type: 'object', + description: + 'Sandbox permissions for the command. Use this to request additional sandboxed filesystem or network permissions if a previous command failed with "Operation not permitted".', + properties: { + network: { + type: 'boolean', + description: + 'Set to true to enable network access for this command.', + }, + fileSystem: { + type: 'object', + properties: { + read: { + type: 'array', + items: { type: 'string' }, + description: + 'List of additional absolute paths to allow reading.', + }, + write: { + type: 'array', + items: { type: 'string' }, + description: + 'List of additional absolute paths to allow writing.', + }, + }, + }, }, }, - }, - }, - }, + } + : {}), }, required: [SHELL_PARAM_COMMAND], }, diff --git a/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts b/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts index 061dfdbc8b..cd79694f78 100644 --- a/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts +++ b/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts @@ -332,8 +332,16 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = { }, }, - run_shell_command: (enableInteractiveShell, enableEfficiency) => - getShellDeclaration(enableInteractiveShell, enableEfficiency), + run_shell_command: ( + enableInteractiveShell, + enableEfficiency, + enableToolSandboxing, + ) => + getShellDeclaration( + enableInteractiveShell, + enableEfficiency, + enableToolSandboxing, + ), replace: { name: EDIT_TOOL_NAME, diff --git a/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts b/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts index f7d9fa499c..7543adc2ae 100644 --- a/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts +++ b/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts @@ -338,8 +338,16 @@ export const GEMINI_3_SET: CoreToolSet = { }, }, - run_shell_command: (enableInteractiveShell, enableEfficiency) => - getShellDeclaration(enableInteractiveShell, enableEfficiency), + run_shell_command: ( + enableInteractiveShell, + enableEfficiency, + enableToolSandboxing, + ) => + getShellDeclaration( + enableInteractiveShell, + enableEfficiency, + enableToolSandboxing, + ), replace: { name: EDIT_TOOL_NAME, diff --git a/packages/core/src/tools/definitions/types.ts b/packages/core/src/tools/definitions/types.ts index 9d335310e9..30cffe5474 100644 --- a/packages/core/src/tools/definitions/types.ts +++ b/packages/core/src/tools/definitions/types.ts @@ -37,6 +37,7 @@ export interface CoreToolSet { run_shell_command: ( enableInteractiveShell: boolean, enableEfficiency: boolean, + enableToolSandboxing: boolean, ) => FunctionDeclaration; replace: FunctionDeclaration; google_web_search: FunctionDeclaration; diff --git a/packages/core/src/tools/shell.test.ts b/packages/core/src/tools/shell.test.ts index 9320b4f3f8..d1dfc415b7 100644 --- a/packages/core/src/tools/shell.test.ts +++ b/packages/core/src/tools/shell.test.ts @@ -137,6 +137,7 @@ describe('ShellTool', () => { getShellToolInactivityTimeout: vi.fn().mockReturnValue(1000), getEnableInteractiveShell: vi.fn().mockReturnValue(false), getEnableShellOutputEfficiency: vi.fn().mockReturnValue(true), + getSandboxEnabled: vi.fn().mockReturnValue(false), sanitizationConfig: {}, sandboxManager: new NoopSandboxManager(), } as unknown as Config; diff --git a/packages/core/src/tools/shell.ts b/packages/core/src/tools/shell.ts index 116718c946..f72b6f28fe 100644 --- a/packages/core/src/tools/shell.ts +++ b/packages/core/src/tools/shell.ts @@ -696,6 +696,7 @@ export class ShellTool extends BaseDeclarativeTool< const definition = getShellDefinition( context.config.getEnableInteractiveShell(), context.config.getEnableShellOutputEfficiency(), + context.config.getSandboxEnabled(), ); super( ShellTool.Name, @@ -745,6 +746,7 @@ export class ShellTool extends BaseDeclarativeTool< const definition = getShellDefinition( this.context.config.getEnableInteractiveShell(), this.context.config.getEnableShellOutputEfficiency(), + this.context.config.getSandboxEnabled(), ); return resolveToolDeclaration(definition, modelId); }