mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-20 02:08:47 +00:00
Ensure session rules only apply to a single session
This commit is contained in:
@@ -266,8 +266,9 @@ export class ChatTerminalToolConfirmationSubPart extends BaseChatToolInvocationS
|
||||
const userRules = newRules.filter(r => r.scope === 'user');
|
||||
|
||||
// Handle session-scoped rules (temporary, in-memory only)
|
||||
const chatSessionId = this.context.element.sessionId;
|
||||
for (const rule of sessionRules) {
|
||||
this.terminalChatService.addSessionAutoApproveRule(rule.key, rule.value);
|
||||
this.terminalChatService.addSessionAutoApproveRule(chatSessionId, rule.key, rule.value);
|
||||
}
|
||||
|
||||
// Handle workspace-scoped rules
|
||||
|
||||
@@ -220,16 +220,18 @@ export interface ITerminalChatService {
|
||||
|
||||
/**
|
||||
* Add a session-scoped auto-approve rule.
|
||||
* @param chatSessionId The chat session ID to associate the rule with
|
||||
* @param key The rule key (command or regex pattern)
|
||||
* @param value The rule value (approval boolean or object with approve and matchCommandLine)
|
||||
*/
|
||||
addSessionAutoApproveRule(key: string, value: boolean | { approve: boolean; matchCommandLine?: boolean }): void;
|
||||
addSessionAutoApproveRule(chatSessionId: string, key: string, value: boolean | { approve: boolean; matchCommandLine?: boolean }): void;
|
||||
|
||||
/**
|
||||
* Get all session-scoped auto-approve rules.
|
||||
* @returns A record of all session-scoped auto-approve rules
|
||||
* Get all session-scoped auto-approve rules for a specific chat session.
|
||||
* @param chatSessionId The chat session ID to get rules for
|
||||
* @returns A record of all session-scoped auto-approve rules for the session
|
||||
*/
|
||||
getSessionAutoApproveRules(): Readonly<Record<string, boolean | { approve: boolean; matchCommandLine?: boolean }>>;
|
||||
getSessionAutoApproveRules(chatSessionId: string): Readonly<Record<string, boolean | { approve: boolean; matchCommandLine?: boolean }>>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -54,10 +54,11 @@ export class TerminalChatService extends Disposable implements ITerminalChatServ
|
||||
private readonly _sessionAutoApprovalEnabled = new Set<string>();
|
||||
|
||||
/**
|
||||
* Tracks session-scoped auto-approve rules. These are temporary rules that last only for the
|
||||
* duration of the VS Code session (not persisted to disk).
|
||||
* Tracks session-scoped auto-approve rules per chat session. These are temporary rules that
|
||||
* last only for the duration of the chat session (not persisted to disk).
|
||||
* Map<chatSessionId, Record<ruleKey, ruleValue>>
|
||||
*/
|
||||
private readonly _sessionAutoApproveRules: Record<string, boolean | { approve: boolean; matchCommandLine?: boolean }> = {};
|
||||
private readonly _sessionAutoApproveRules = new Map<string, Record<string, boolean | { approve: boolean; matchCommandLine?: boolean }>>();
|
||||
|
||||
constructor(
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@@ -72,6 +73,16 @@ export class TerminalChatService extends Disposable implements ITerminalChatServ
|
||||
this._hasHiddenToolTerminalContext = TerminalChatContextKeys.hasHiddenChatTerminals.bindTo(this._contextKeyService);
|
||||
|
||||
this._restoreFromStorage();
|
||||
|
||||
// Clear session auto-approve rules when chat sessions end
|
||||
this._register(this._chatService.onDidDisposeSession(e => {
|
||||
for (const resource of e.sessionResource) {
|
||||
const sessionId = LocalChatSessionUri.parseLocalSessionId(resource);
|
||||
if (sessionId) {
|
||||
this._sessionAutoApproveRules.delete(sessionId);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
registerTerminalInstanceWithToolSession(terminalToolSessionId: string | undefined, instance: ITerminalInstance): void {
|
||||
@@ -320,11 +331,16 @@ export class TerminalChatService extends Disposable implements ITerminalChatServ
|
||||
return this._sessionAutoApprovalEnabled.has(chatSessionId);
|
||||
}
|
||||
|
||||
addSessionAutoApproveRule(key: string, value: boolean | { approve: boolean; matchCommandLine?: boolean }): void {
|
||||
this._sessionAutoApproveRules[key] = value;
|
||||
addSessionAutoApproveRule(chatSessionId: string, key: string, value: boolean | { approve: boolean; matchCommandLine?: boolean }): void {
|
||||
let sessionRules = this._sessionAutoApproveRules.get(chatSessionId);
|
||||
if (!sessionRules) {
|
||||
sessionRules = {};
|
||||
this._sessionAutoApproveRules.set(chatSessionId, sessionRules);
|
||||
}
|
||||
sessionRules[key] = value;
|
||||
}
|
||||
|
||||
getSessionAutoApproveRules(): Readonly<Record<string, boolean | { approve: boolean; matchCommandLine?: boolean }>> {
|
||||
return this._sessionAutoApproveRules;
|
||||
getSessionAutoApproveRules(chatSessionId: string): Readonly<Record<string, boolean | { approve: boolean; matchCommandLine?: boolean }>> {
|
||||
return this._sessionAutoApproveRules.get(chatSessionId) ?? {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ export class CommandLineAutoApprover extends Disposable {
|
||||
this._denyListCommandLineRules = denyListCommandLineRules;
|
||||
}
|
||||
|
||||
isCommandAutoApproved(command: string, shell: string, os: OperatingSystem): ICommandApprovalResultWithReason {
|
||||
isCommandAutoApproved(command: string, shell: string, os: OperatingSystem, chatSessionId?: string): ICommandApprovalResultWithReason {
|
||||
// Check if the command has a transient environment variable assignment prefix which we
|
||||
// always deny for now as it can easily lead to execute other commands
|
||||
if (transientEnvVarRegex.test(command)) {
|
||||
@@ -107,7 +107,7 @@ export class CommandLineAutoApprover extends Disposable {
|
||||
}
|
||||
|
||||
// Check session allow rules (session deny rules can't exist)
|
||||
for (const rule of this._getSessionRules().allowListRules) {
|
||||
for (const rule of this._getSessionRules(chatSessionId).allowListRules) {
|
||||
if (this._commandMatchesRule(rule, command, shell, os)) {
|
||||
return {
|
||||
result: 'approved',
|
||||
@@ -137,7 +137,7 @@ export class CommandLineAutoApprover extends Disposable {
|
||||
};
|
||||
}
|
||||
|
||||
isCommandLineAutoApproved(commandLine: string): ICommandApprovalResultWithReason {
|
||||
isCommandLineAutoApproved(commandLine: string, chatSessionId?: string): ICommandApprovalResultWithReason {
|
||||
// Check the config deny list first to see if this command line requires explicit approval
|
||||
for (const rule of this._denyListCommandLineRules) {
|
||||
if (rule.regex.test(commandLine)) {
|
||||
@@ -150,7 +150,7 @@ export class CommandLineAutoApprover extends Disposable {
|
||||
}
|
||||
|
||||
// Check session allow list (session deny rules can't exist)
|
||||
for (const rule of this._getSessionRules().allowListCommandLineRules) {
|
||||
for (const rule of this._getSessionRules(chatSessionId).allowListCommandLineRules) {
|
||||
if (rule.regex.test(commandLine)) {
|
||||
return {
|
||||
result: 'approved',
|
||||
@@ -176,7 +176,7 @@ export class CommandLineAutoApprover extends Disposable {
|
||||
};
|
||||
}
|
||||
|
||||
private _getSessionRules(): {
|
||||
private _getSessionRules(chatSessionId?: string): {
|
||||
denyListRules: IAutoApproveRule[];
|
||||
allowListRules: IAutoApproveRule[];
|
||||
allowListCommandLineRules: IAutoApproveRule[];
|
||||
@@ -187,7 +187,11 @@ export class CommandLineAutoApprover extends Disposable {
|
||||
const allowListCommandLineRules: IAutoApproveRule[] = [];
|
||||
const denyListCommandLineRules: IAutoApproveRule[] = [];
|
||||
|
||||
const sessionRulesConfig = this._terminalChatService.getSessionAutoApproveRules();
|
||||
if (!chatSessionId) {
|
||||
return { denyListRules, allowListRules, allowListCommandLineRules, denyListCommandLineRules };
|
||||
}
|
||||
|
||||
const sessionRulesConfig = this._terminalChatService.getSessionAutoApproveRules(chatSessionId);
|
||||
for (const [key, value] of Object.entries(sessionRulesConfig)) {
|
||||
if (typeof value === 'boolean') {
|
||||
const { regex, regexCaseInsensitive } = this._convertAutoApproveEntryToRegex(key);
|
||||
|
||||
@@ -87,8 +87,8 @@ export class CommandLineAutoApproveAnalyzer extends Disposable implements IComma
|
||||
};
|
||||
}
|
||||
|
||||
const subCommandResults = subCommands.map(e => this._commandLineAutoApprover.isCommandAutoApproved(e, options.shell, options.os));
|
||||
const commandLineResult = this._commandLineAutoApprover.isCommandLineAutoApproved(options.commandLine);
|
||||
const subCommandResults = subCommands.map(e => this._commandLineAutoApprover.isCommandAutoApproved(e, options.shell, options.os, options.chatSessionId));
|
||||
const commandLineResult = this._commandLineAutoApprover.isCommandLineAutoApproved(options.commandLine, options.chatSessionId);
|
||||
const autoApproveReasons: string[] = [
|
||||
...subCommandResults.map(e => e.reason),
|
||||
commandLineResult.reason,
|
||||
|
||||
Reference in New Issue
Block a user