Skip to content

Commit 5e71977

Browse files
committed
Merge allow and deny lists into autoApprove
Fixes microsoft#253472
1 parent aa3ac44 commit 5e71977

File tree

4 files changed

+132
-141
lines changed

4 files changed

+132
-141
lines changed

src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/commandLineAutoApprover.ts

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,39 +10,48 @@ import { TerminalChatAgentToolsSettingId } from '../common/terminalChatAgentTool
1010
import { isPowerShell } from './runInTerminalHelpers.js';
1111

1212
export class CommandLineAutoApprover extends Disposable {
13-
private _denyListRegexes: RegExp[] = [];
14-
private _allowListRegexes: RegExp[] = [];
13+
private _autoApproveRegexes: { regex: RegExp; approved: boolean }[] = [];
1514

1615
constructor(
1716
@IConfigurationService private readonly _configurationService: IConfigurationService,
1817
) {
1918
super();
2019
this.updateConfiguration();
2120
this._register(this._configurationService.onDidChangeConfiguration(e => {
22-
if (e.affectsConfiguration(TerminalChatAgentToolsSettingId.AllowList) || e.affectsConfiguration(TerminalChatAgentToolsSettingId.DenyList)) {
21+
if (e.affectsConfiguration(TerminalChatAgentToolsSettingId.AutoApprove)) {
2322
this.updateConfiguration();
2423
}
2524
}));
2625
}
2726

2827
updateConfiguration() {
29-
this._denyListRegexes = this._mapAutoApproveConfigToRegexList(this._configurationService.getValue(TerminalChatAgentToolsSettingId.DenyList));
30-
this._allowListRegexes = this._mapAutoApproveConfigToRegexList(this._configurationService.getValue(TerminalChatAgentToolsSettingId.AllowList));
28+
this._autoApproveRegexes = this._mapAutoApproveConfigToRegexList(this._configurationService.getValue(TerminalChatAgentToolsSettingId.AutoApprove));
3129
}
3230

3331
isAutoApproved(command: string, shell: string, os: OperatingSystem): boolean {
34-
// Check the deny list to see if this command requires explicit approval
35-
for (const regex of this._denyListRegexes) {
32+
// Check all patterns in the auto approve list
33+
// Deny patterns (false) take precedence over allow patterns (true)
34+
let hasAllowMatch = false;
35+
let hasDenyMatch = false;
36+
37+
for (const { regex, approved } of this._autoApproveRegexes) {
3638
if (this._commandMatchesRegex(regex, command, shell, os)) {
37-
return false;
39+
if (approved) {
40+
hasAllowMatch = true;
41+
} else {
42+
hasDenyMatch = true;
43+
}
3844
}
3945
}
4046

41-
// Check the allow list to see if the command is allowed to run without explicit approval
42-
for (const regex of this._allowListRegexes) {
43-
if (this._commandMatchesRegex(regex, command, shell, os)) {
44-
return true;
45-
}
47+
// If there's a deny match, always require approval
48+
if (hasDenyMatch) {
49+
return false;
50+
}
51+
52+
// If there's an allow match and no deny match, auto-approve
53+
if (hasAllowMatch) {
54+
return true;
4655
}
4756

4857
// TODO: LLM-based auto-approval https://github.com/microsoft/vscode/issues/253267
@@ -64,12 +73,18 @@ export class CommandLineAutoApprover extends Disposable {
6473
return false;
6574
}
6675

67-
private _mapAutoApproveConfigToRegexList(config: unknown): RegExp[] {
76+
private _mapAutoApproveConfigToRegexList(config: unknown): { regex: RegExp; approved: boolean }[] {
6877
if (!config || typeof config !== 'object') {
6978
return [];
7079
}
7180
return Object.entries(config)
72-
.map(([key, value]) => value ? this._convertAutoApproveEntryToRegex(key) : undefined)
81+
.map(([key, value]) => {
82+
if (typeof value === 'boolean') {
83+
const regex = this._convertAutoApproveEntryToRegex(key);
84+
return { regex, approved: value };
85+
}
86+
return undefined;
87+
})
7388
.filter(e => !!e);
7489
}
7590

src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts

Lines changed: 21 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@ import type { IConfigurationPropertySchema } from '../../../../../platform/confi
99

1010
export const enum TerminalChatAgentToolsSettingId {
1111
CoreToolsEnabled = 'chat.agent.terminal.coreToolsEnabled',
12-
AllowList = 'chat.agent.terminal.allowList',
13-
DenyList = 'chat.agent.terminal.denyList',
12+
AutoApprove = 'chat.agent.terminal.autoApprove',
1413
}
1514

1615
export interface ITerminalChatAgentToolsConfiguration {
1716
coreToolsEnabled: boolean;
18-
allowList: { [key: string]: string };
19-
denyList: { [key: string]: string };
17+
autoApprove: { [key: string]: boolean };
2018
}
2119

2220
export const terminalChatAgentToolsConfiguration: IStringDictionary<IConfigurationPropertySchema> = {
@@ -28,8 +26,8 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary<IConfigurati
2826
],
2927
default: true,
3028
},
31-
[TerminalChatAgentToolsSettingId.AllowList]: {
32-
markdownDescription: localize('allowList', "A list of commands or regular expressions that allow the run in terminal tool commands to run without explicit approval. These will be matched against the start of a command. A regular expression can be provided by wrapping the string in `/` characters.\n\nExamples:\n- `\"mkdir\"` Will allow all command lines starting with `mkdir`\n- `\"npm run build\"` Will allow all command lines starting with `npm run build`\n- `\"/^git (status|show\\b.*)$/\"` will allow `git status` and all command lines starting with `git show`\n- `\"/.*/\"` will allow all command lines\n\nThis will be overridden by anything that matches an entry in `#chat.agent.terminal.denyList#`."),
29+
[TerminalChatAgentToolsSettingId.AutoApprove]: {
30+
markdownDescription: localize('autoApprove', "A list of commands or regular expressions that control whether the run in terminal tool commands require explicit approval. These will be matched against the start of a command. A regular expression can be provided by wrapping the string in `/` characters.\n\nSet to `true` to automatically approve commands, or `false` to require explicit approval.\n\nExamples:\n- `\"mkdir\": true` Will allow all command lines starting with `mkdir`\n- `\"npm run build\": true` Will allow all command lines starting with `npm run build`\n- `\"rm\": false` Will require explicit approval for all command lines starting with `rm`\n- `\"/^git (status|show\\b.*)$/\": true` will allow `git status` and all command lines starting with `git show`\n- `\"/.*/\": true` will allow all command lines\n\nCommands set to `false` will override those set to `true`."),
3331
type: 'object',
3432
additionalProperties: {
3533
type: 'boolean',
@@ -38,45 +36,29 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary<IConfigurati
3836
false,
3937
],
4038
enumDescriptions: [
41-
localize('allowList.true', "Allow the pattern."),
42-
localize('allowList.false', "Do not allow the pattern."),
39+
localize('autoApprove.true', "Automatically approve the pattern."),
40+
localize('autoApprove.false', "Require explicit approval for the pattern."),
4341
],
44-
description: localize('allowList.key', "The start of a command to match against. A regular expression can be provided by wrapping the string in `/` characters."),
45-
},
46-
tags: [
47-
'experimental'
48-
],
49-
default: {},
50-
},
51-
[TerminalChatAgentToolsSettingId.DenyList]: {
52-
markdownDescription: localize('denyList', "A list of commands or regular expressions that override matches in `#chat.agent.terminal.allowList#` and force a command line to require explicit approval. This will be matched against the start of a command. A regular expression can be provided by wrapping the string in `/` characters.\n\nExamples:\n- `\"rm\"` will require explicit approval for any command starting with `rm`\n- `\"/^git (push|pull)/\"` will require explicit approval for any command starting with `git push` or `git pull` \n\nThis provides basic protection by preventing certain commands from running automatically, especially those a user would likely want to approve first. It is not intended as a comprehensive security measure or a defense against prompt injection."),
53-
type: 'object',
54-
additionalProperties: {
55-
type: 'boolean',
56-
enum: [
57-
true,
58-
false
59-
],
60-
enumDescriptions: [
61-
localize('denyList.value.true', "Deny the pattern."),
62-
localize('denyList.value.false', "Do not deny the pattern."),
63-
],
64-
description: localize('denyList.key', "The start of a command to match against. A regular expression can be provided by wrapping the string in `/` characters.")
42+
description: localize('autoApprove.key', "The start of a command to match against. A regular expression can be provided by wrapping the string in `/` characters."),
6543
},
6644
tags: [
6745
'experimental'
6846
],
6947
default: {
70-
rm: true,
71-
rmdir: true,
72-
del: true,
73-
kill: true,
74-
curl: true,
75-
wget: true,
76-
eval: true,
77-
chmod: true,
78-
chown: true,
79-
'Remove-Item': true,
48+
rm: false,
49+
rmdir: false,
50+
del: false,
51+
kill: false,
52+
curl: false,
53+
wget: false,
54+
eval: false,
55+
chmod: false,
56+
chown: false,
57+
'Remove-Item': false,
8058
},
59+
policy: {
60+
name: 'TerminalChatAgentToolsAutoApprove',
61+
minimumVersion: '1.103',
62+
}
8163
}
8264
};

0 commit comments

Comments
 (0)