mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-21 18:44:30 -07:00
fix(core): improve shell command with redirection detection (#15683)
This commit is contained in:
@@ -51,7 +51,6 @@ import * as path from 'node:path';
|
||||
import * as crypto from 'node:crypto';
|
||||
import * as summarizer from '../utils/summarizer.js';
|
||||
import { ToolErrorType } from './tool-error.js';
|
||||
import { ToolConfirmationOutcome } from './tools.js';
|
||||
import { OUTPUT_UPDATE_INTERVAL_MS } from './shell.js';
|
||||
import { SHELL_TOOL_NAME } from './tool-names.js';
|
||||
import { WorkspaceContext } from '../utils/workspaceContext.js';
|
||||
@@ -472,7 +471,7 @@ describe('ShellTool', () => {
|
||||
});
|
||||
|
||||
describe('shouldConfirmExecute', () => {
|
||||
it('should request confirmation for a new command and allowlist it on "Always"', async () => {
|
||||
it('should return confirmation details when PolicyEngine delegates', async () => {
|
||||
const params = { command: 'npm install' };
|
||||
const invocation = shellTool.build(params);
|
||||
const confirmation = await invocation.shouldConfirmExecute(
|
||||
@@ -481,18 +480,6 @@ describe('ShellTool', () => {
|
||||
|
||||
expect(confirmation).not.toBe(false);
|
||||
expect(confirmation && confirmation.type).toBe('exec');
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
await (confirmation as any).onConfirm(
|
||||
ToolConfirmationOutcome.ProceedAlways,
|
||||
);
|
||||
|
||||
// Should now be allowlisted
|
||||
const secondInvocation = shellTool.build({ command: 'npm test' });
|
||||
const secondConfirmation = await secondInvocation.shouldConfirmExecute(
|
||||
new AbortController().signal,
|
||||
);
|
||||
expect(secondConfirmation).toBe(false);
|
||||
});
|
||||
|
||||
it('should throw an error if validation fails', () => {
|
||||
|
||||
@@ -62,7 +62,6 @@ export class ShellToolInvocation extends BaseToolInvocation<
|
||||
constructor(
|
||||
private readonly config: Config,
|
||||
params: ShellToolParams,
|
||||
private readonly allowlist: Set<string>,
|
||||
messageBus?: MessageBus,
|
||||
_toolName?: string,
|
||||
_toolDisplayName?: string,
|
||||
@@ -127,23 +126,15 @@ export class ShellToolInvocation extends BaseToolInvocation<
|
||||
);
|
||||
}
|
||||
|
||||
const commandsToConfirm = rootCommands.filter(
|
||||
(command) => !this.allowlist.has(command),
|
||||
);
|
||||
|
||||
if (commandsToConfirm.length === 0) {
|
||||
return false; // already approved and allowlisted
|
||||
}
|
||||
|
||||
// Rely entirely on PolicyEngine for interactive confirmation.
|
||||
// If we are here, it means PolicyEngine returned ASK_USER (or no message bus),
|
||||
// so we must provide confirmation details.
|
||||
const confirmationDetails: ToolExecuteConfirmationDetails = {
|
||||
type: 'exec',
|
||||
title: 'Confirm Shell Command',
|
||||
command: this.params.command,
|
||||
rootCommand: commandsToConfirm.join(', '),
|
||||
rootCommand: rootCommands.join(', '),
|
||||
onConfirm: async (outcome: ToolConfirmationOutcome) => {
|
||||
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
|
||||
commandsToConfirm.forEach((command) => this.allowlist.add(command));
|
||||
}
|
||||
await this.publishPolicyUpdate(outcome);
|
||||
},
|
||||
};
|
||||
@@ -451,8 +442,6 @@ export class ShellTool extends BaseDeclarativeTool<
|
||||
> {
|
||||
static readonly Name = SHELL_TOOL_NAME;
|
||||
|
||||
private allowlist: Set<string> = new Set();
|
||||
|
||||
constructor(
|
||||
private readonly config: Config,
|
||||
messageBus?: MessageBus,
|
||||
@@ -533,7 +522,6 @@ export class ShellTool extends BaseDeclarativeTool<
|
||||
return new ShellToolInvocation(
|
||||
this.config,
|
||||
params,
|
||||
this.allowlist,
|
||||
messageBus,
|
||||
_toolName,
|
||||
_toolDisplayName,
|
||||
|
||||
Reference in New Issue
Block a user