Skip to content

Commit e371d09

Browse files
authored
Merge pull request microsoft#256754 from microsoft/tyriar/autoapprove_merge__regex_flags
Support regex flags and case insensitive remove-item in defaults
2 parents f8d624f + 315a8d1 commit e371d09

File tree

3 files changed

+72
-4
lines changed

3 files changed

+72
-4
lines changed

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,16 @@ export class CommandLineAutoApprover extends Disposable {
9090

9191
private _convertAutoApproveEntryToRegex(value: string): RegExp {
9292
// If it's wrapped in `/`, it's in regex format and should be converted directly
93-
if (value.match(/^\/.+\/$/)) {
94-
return new RegExp(value.slice(1, -1));
93+
// Support all standard JavaScript regex flags: d, g, i, m, s, u, v, y
94+
const regexMatch = value.match(/^\/(?<pattern>.+)\/(?<flags>[dgimsuvy]*)$/);
95+
const regexPattern = regexMatch?.groups?.pattern;
96+
if (regexPattern) {
97+
let flags = regexMatch.groups?.flags;
98+
// Remove global flag as it can cause confusion
99+
if (flags) {
100+
flags = flags.replaceAll('g', '');
101+
}
102+
return new RegExp(regexPattern, flags || undefined);
95103
}
96104

97105
// Escape regex special characters

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary<IConfigurati
2727
default: true,
2828
},
2929
[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, `false` to always require explicit approval or `null` to unset the value.\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- `\"rm\": null` will unset the default `false` value for `rm`\n\nNote that these commands and regular expressions are evaluated for every _sub-command_ within a single command line, so `foo && bar` for example will need both `foo` and `bar` to match a `true` entry and must not match a `false` entry in order to auto approve. Inline commands are also detected so `echo $(rm file)` will need both `echo $(rm file)` and `rm file` to pass."),
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 followed by optional flags such as `i`.\n\nSet to `true` to automatically approve commands, `false` to always require explicit approval or `null` to unset the value.\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- `\"/^Get-ChildItem\\b/i\": true` will allow Get-ChildItem command regardless of casing\n- `\"/.*/\": true` will allow all command lines\n- `\"rm\": null` will unset the default `false` value for `rm`\n\nNote that these commands and regular expressions are evaluated for every _sub-command_ within a single command line, so `foo && bar` for example will need both `foo` and `bar` to match a `true` entry and must not match a `false` entry in order to auto approve. Inline commands are also detected so `echo $(rm file)` will need both `echo $(rm file)` and `rm file` to pass."),
3131
type: 'object',
3232
additionalProperties: {
3333
anyOf: [
@@ -62,7 +62,7 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary<IConfigurati
6262
eval: false,
6363
chmod: false,
6464
chown: false,
65-
'Remove-Item': false,
65+
'/^Remove-Item\\b/i': false,
6666
},
6767
}
6868
};

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,66 @@ suite('CommandLineAutoApprover', () => {
184184
ok(!isAutoApproved('del file'));
185185
ok(!isAutoApproved('kill process'));
186186
});
187+
188+
suite('flags', () => {
189+
test('should handle case-insensitive regex patterns with i flag', () => {
190+
setAutoApprove({
191+
"/^echo/i": true,
192+
"/^ls/i": true,
193+
"/rm|del/i": false
194+
});
195+
196+
ok(isAutoApproved('echo hello'));
197+
ok(isAutoApproved('ECHO hello'));
198+
ok(isAutoApproved('Echo hello'));
199+
ok(isAutoApproved('ls -la'));
200+
ok(isAutoApproved('LS -la'));
201+
ok(isAutoApproved('Ls -la'));
202+
ok(!isAutoApproved('rm file'));
203+
ok(!isAutoApproved('RM file'));
204+
ok(!isAutoApproved('del file'));
205+
ok(!isAutoApproved('DEL file'));
206+
});
207+
208+
test('should handle multiple regex flags', () => {
209+
setAutoApprove({
210+
"/^git\\s+/gim": true,
211+
"/dangerous/gim": false
212+
});
213+
214+
ok(isAutoApproved('git status'));
215+
ok(isAutoApproved('GIT status'));
216+
ok(isAutoApproved('Git status'));
217+
ok(!isAutoApproved('dangerous command'));
218+
ok(!isAutoApproved('DANGEROUS command'));
219+
});
220+
221+
test('should handle various regex flags', () => {
222+
setAutoApprove({
223+
"/^echo.*/s": true, // dotall flag
224+
"/^git\\s+/i": true, // case-insensitive flag
225+
"/rm|del/g": false // global flag
226+
});
227+
228+
ok(isAutoApproved('echo hello\nworld'));
229+
ok(isAutoApproved('git status'));
230+
ok(isAutoApproved('GIT status'));
231+
ok(!isAutoApproved('rm file'));
232+
ok(!isAutoApproved('del file'));
233+
});
234+
235+
test('should handle regex patterns without flags', () => {
236+
setAutoApprove({
237+
"/^echo/": true,
238+
"/rm|del/": false
239+
});
240+
241+
ok(isAutoApproved('echo hello'));
242+
ok(!isAutoApproved('ECHO hello'), 'Should be case-sensitive without i flag');
243+
ok(!isAutoApproved('rm file'));
244+
ok(!isAutoApproved('RM file'), 'Should be case-sensitive without i flag');
245+
});
246+
});
187247
});
188248

189249
suite('edge cases', () => {

0 commit comments

Comments
 (0)