mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-30 15:04:16 -07:00
feat(core): improve shell redirection transparency and security (#16486)
This commit is contained in:
@@ -45,8 +45,10 @@ export interface ToolInvocation<
|
||||
toolLocations(): ToolLocation[];
|
||||
|
||||
/**
|
||||
* Determines if the tool should prompt for confirmation before execution.
|
||||
* @returns Confirmation details or false if no confirmation is needed.
|
||||
* Checks if the tool call should be confirmed by the user before execution.
|
||||
*
|
||||
* @param abortSignal An AbortSignal that can be used to cancel the confirmation request.
|
||||
* @returns A ToolCallConfirmationDetails object if confirmation is required, or false if not.
|
||||
*/
|
||||
shouldConfirmExecute(
|
||||
abortSignal: AbortSignal,
|
||||
@@ -143,7 +145,7 @@ export abstract class BaseToolInvocation<
|
||||
) {
|
||||
if (this._toolName) {
|
||||
const options = this.getPolicyUpdateOptions(outcome);
|
||||
await this.messageBus.publish({
|
||||
void this.messageBus.publish({
|
||||
type: MessageBusType.UPDATE_POLICY,
|
||||
toolName: this._toolName,
|
||||
persist: outcome === ToolConfirmationOutcome.ProceedAlwaysAndSave,
|
||||
@@ -179,16 +181,21 @@ export abstract class BaseToolInvocation<
|
||||
protected getMessageBusDecision(
|
||||
abortSignal: AbortSignal,
|
||||
): Promise<'ALLOW' | 'DENY' | 'ASK_USER'> {
|
||||
if (!this.messageBus) {
|
||||
if (!this.messageBus || !this._toolName) {
|
||||
// If there's no message bus, we can't make a decision, so we allow.
|
||||
// The legacy confirmation flow will still apply if the tool needs it.
|
||||
return Promise.resolve('ALLOW');
|
||||
}
|
||||
|
||||
const correlationId = randomUUID();
|
||||
const toolCall = {
|
||||
name: this._toolName || this.constructor.name,
|
||||
args: this.params as Record<string, unknown>,
|
||||
const request: ToolConfirmationRequest = {
|
||||
type: MessageBusType.TOOL_CONFIRMATION_REQUEST,
|
||||
correlationId,
|
||||
toolCall: {
|
||||
name: this._toolName,
|
||||
args: this.params as Record<string, unknown>,
|
||||
},
|
||||
serverName: this._serverName,
|
||||
};
|
||||
|
||||
return new Promise<'ALLOW' | 'DENY' | 'ASK_USER'>((resolve) => {
|
||||
@@ -197,18 +204,19 @@ export abstract class BaseToolInvocation<
|
||||
return;
|
||||
}
|
||||
|
||||
let timeoutId: NodeJS.Timeout | undefined;
|
||||
let timeoutId: NodeJS.Timeout | null = null;
|
||||
let unsubscribe: (() => void) | null = null;
|
||||
|
||||
const cleanup = () => {
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = undefined;
|
||||
timeoutId = null;
|
||||
}
|
||||
if (unsubscribe) {
|
||||
unsubscribe();
|
||||
unsubscribe = null;
|
||||
}
|
||||
abortSignal.removeEventListener('abort', abortHandler);
|
||||
this.messageBus.unsubscribe(
|
||||
MessageBusType.TOOL_CONFIRMATION_RESPONSE,
|
||||
responseHandler,
|
||||
);
|
||||
};
|
||||
|
||||
const abortHandler = () => {
|
||||
@@ -245,17 +253,15 @@ export abstract class BaseToolInvocation<
|
||||
MessageBusType.TOOL_CONFIRMATION_RESPONSE,
|
||||
responseHandler,
|
||||
);
|
||||
|
||||
const request: ToolConfirmationRequest = {
|
||||
type: MessageBusType.TOOL_CONFIRMATION_REQUEST,
|
||||
toolCall,
|
||||
correlationId,
|
||||
serverName: this._serverName,
|
||||
unsubscribe = () => {
|
||||
this.messageBus?.unsubscribe(
|
||||
MessageBusType.TOOL_CONFIRMATION_RESPONSE,
|
||||
responseHandler,
|
||||
);
|
||||
};
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.messageBus.publish(request);
|
||||
void this.messageBus.publish(request);
|
||||
} catch (_error) {
|
||||
cleanup();
|
||||
resolve('ALLOW');
|
||||
|
||||
Reference in New Issue
Block a user