Skip to content

Commit 4fdbedd

Browse files
CopilotTyriar
andcommitted
Implement path-aware terminal auto approval
Co-authored-by: Tyriar <[email protected]>
1 parent 791c661 commit 4fdbedd

File tree

2 files changed

+138
-1
lines changed

2 files changed

+138
-1
lines changed

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,26 @@ export class CommandLineAutoApprover extends Disposable {
207207
return neverMatchRegex;
208208
}
209209

210-
// Escape regex special characters
210+
// Check if this looks like a path (contains path separators)
211+
if (value.includes('/') || value.includes('\\')) {
212+
// Handle path-like strings with flexible separator and optional ./ prefix matching
213+
214+
// Replace path separators with placeholders first, before escaping
215+
let pathPattern = value.replace(/[/\\]/g, '§PATH_SEP§');
216+
217+
// Now escape all regex special characters
218+
pathPattern = pathPattern.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&');
219+
220+
// Replace placeholders with character class that matches both / and \
221+
pathPattern = pathPattern.replace(/§PATH_SEP§/g, '[/\\\\]');
222+
223+
// Create pattern that optionally matches ./ or .\ at the start
224+
const finalPattern = `^(?:\\.[/\\\\])?${pathPattern}\\b`;
225+
226+
return new RegExp(finalPattern);
227+
}
228+
229+
// Escape regex special characters for non-path strings
211230
const sanitizedValue = value.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&');
212231

213232
// Regular strings should match the start of the command line and be a word boundary

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

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,124 @@ suite('CommandLineAutoApprover', () => {
379379
});
380380
});
381381

382+
suite('path-aware auto approval', () => {
383+
test('should handle path variations with forward slashes', () => {
384+
setAutoApprove({
385+
"bin/foo": true
386+
});
387+
388+
// Should approve the exact match
389+
ok(isAutoApproved('bin/foo'));
390+
ok(isAutoApproved('bin/foo --arg'));
391+
392+
// Should approve with Windows backslashes
393+
ok(isAutoApproved('bin\\foo'));
394+
ok(isAutoApproved('bin\\foo --arg'));
395+
396+
// Should approve with current directory prefixes
397+
ok(isAutoApproved('./bin/foo'));
398+
ok(isAutoApproved('.\\bin/foo'));
399+
ok(isAutoApproved('./bin\\foo'));
400+
ok(isAutoApproved('.\\bin\\foo'));
401+
402+
// Should not approve partial matches
403+
ok(!isAutoApproved('bin/foobar'));
404+
ok(!isAutoApproved('notbin/foo'));
405+
});
406+
407+
test('should handle path variations with backslashes', () => {
408+
setAutoApprove({
409+
"bin\\script.bat": true
410+
});
411+
412+
// Should approve the exact match
413+
ok(isAutoApproved('bin\\script.bat'));
414+
ok(isAutoApproved('bin\\script.bat --help'));
415+
416+
// Should approve with forward slashes
417+
ok(isAutoApproved('bin/script.bat'));
418+
ok(isAutoApproved('bin/script.bat --help'));
419+
420+
// Should approve with current directory prefixes
421+
ok(isAutoApproved('./bin\\script.bat'));
422+
ok(isAutoApproved('.\\bin\\script.bat'));
423+
ok(isAutoApproved('./bin/script.bat'));
424+
ok(isAutoApproved('.\\bin/script.bat'));
425+
});
426+
427+
test('should handle deep paths', () => {
428+
setAutoApprove({
429+
"src/utils/helper.js": true
430+
});
431+
432+
ok(isAutoApproved('src/utils/helper.js'));
433+
ok(isAutoApproved('src\\utils\\helper.js'));
434+
ok(isAutoApproved('src/utils\\helper.js'));
435+
ok(isAutoApproved('src\\utils/helper.js'));
436+
ok(isAutoApproved('./src/utils/helper.js'));
437+
ok(isAutoApproved('.\\src\\utils\\helper.js'));
438+
});
439+
440+
test('should not treat non-paths as paths', () => {
441+
setAutoApprove({
442+
"echo": true, // Not a path
443+
"ls": true, // Not a path
444+
"git": true // Not a path
445+
});
446+
447+
// These should work as normal command matching, not path matching
448+
ok(isAutoApproved('echo'));
449+
ok(isAutoApproved('ls'));
450+
ok(isAutoApproved('git'));
451+
452+
// Should not be treated as paths, so these prefixes shouldn't work
453+
ok(!isAutoApproved('./echo'));
454+
ok(!isAutoApproved('.\\ls'));
455+
});
456+
457+
test('should handle paths with mixed separators in config', () => {
458+
setAutoApprove({
459+
"bin/foo\\bar": true // Mixed separators in config
460+
});
461+
462+
ok(isAutoApproved('bin/foo\\bar'));
463+
ok(isAutoApproved('bin\\foo/bar'));
464+
ok(isAutoApproved('bin/foo/bar'));
465+
ok(isAutoApproved('bin\\foo\\bar'));
466+
ok(isAutoApproved('./bin/foo\\bar'));
467+
ok(isAutoApproved('.\\bin\\foo\\bar'));
468+
});
469+
470+
test('should work with command line auto approval for paths', () => {
471+
setAutoApproveWithCommandLine({
472+
"bin/deploy": { approve: true, matchCommandLine: true }
473+
});
474+
475+
ok(isCommandLineAutoApproved('bin/deploy --prod'));
476+
ok(isCommandLineAutoApproved('bin\\deploy --prod'));
477+
ok(isCommandLineAutoApproved('./bin/deploy --prod'));
478+
ok(isCommandLineAutoApproved('.\\bin\\deploy --prod'));
479+
});
480+
481+
test('should handle special characters in paths', () => {
482+
setAutoApprove({
483+
"bin/my-script.sh": true,
484+
"scripts/build_all.py": true,
485+
"tools/run (debug).exe": true
486+
});
487+
488+
ok(isAutoApproved('bin/my-script.sh'));
489+
ok(isAutoApproved('bin\\my-script.sh'));
490+
ok(isAutoApproved('./bin/my-script.sh'));
491+
492+
ok(isAutoApproved('scripts/build_all.py'));
493+
ok(isAutoApproved('scripts\\build_all.py'));
494+
495+
ok(isAutoApproved('tools/run (debug).exe'));
496+
ok(isAutoApproved('tools\\run (debug).exe'));
497+
});
498+
});
499+
382500
suite('PowerShell-specific commands', () => {
383501
setup(() => {
384502
shell = 'pwsh';

0 commit comments

Comments
 (0)