From 7396628a5bf5f222040c67f64870e2b07902a991 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 3 Jan 2026 10:04:13 -0800 Subject: [PATCH 1/2] Approve -C and --no-pager git args Part of #285744 --- .../terminalChatAgentToolsConfiguration.ts | 15 +++--- .../browser/commandLineAutoApprover.test.ts | 49 +++++++++++++++++++ 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts index cfa6fc94b1e..6cb7a7d196b 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts @@ -197,22 +197,23 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary` and `--no-pager` immediately after `git` + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+status\\b/': true, + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+log\\b/': true, + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+show\\b/': true, + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+diff\\b/': true, // git grep // - `--open-files-in-pager`: This is the configured pager, so no risk of code execution // - See notes on `grep` - 'git grep': true, + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+grep\\b/': true, // git branch // - `-d`, `-D`, `--delete`: Prevent branch deletion // - `-m`, `-M`: Prevent branch renaming // - `--force`: Generally dangerous - 'git branch': true, - '/^git branch\\b.*-(d|D|m|M|-delete|-force)\\b/': false, + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+branch\\b/': true, + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+branch\\b.*-(d|D|m|M|-delete|-force)\\b/': false, // #endregion diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/test/browser/commandLineAutoApprover.test.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/test/browser/commandLineAutoApprover.test.ts index f9d03d797f6..2b5c0883f5e 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/test/browser/commandLineAutoApprover.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/test/browser/commandLineAutoApprover.test.ts @@ -202,6 +202,55 @@ suite('CommandLineAutoApprover', () => { ok(!await isAutoApproved('kill process')); }); + test('should handle git patterns with -C and --no-pager', async () => { + setAutoApprove({ + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+status\\b/': true, + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+log\\b/': true, + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+show\\b/': true, + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+diff\\b/': true, + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+grep\\b/': true, + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+branch\\b/': true, + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+branch\\b.*-(d|D|m|M|-delete|-force)\\b/': false, + }); + + // Basic commands + ok(await isAutoApproved('git status')); + ok(await isAutoApproved('git log')); + ok(await isAutoApproved('git show HEAD')); + ok(await isAutoApproved('git diff')); + ok(await isAutoApproved('git grep pattern')); + ok(await isAutoApproved('git branch')); + + // With -C path + ok(await isAutoApproved('git -C /some/path status')); + ok(await isAutoApproved('git -C ../relative log')); + ok(await isAutoApproved('git -C . diff')); + + // With --no-pager + ok(await isAutoApproved('git --no-pager status')); + ok(await isAutoApproved('git --no-pager log')); + ok(await isAutoApproved('git --no-pager diff HEAD~1')); + + // With both -C and --no-pager + ok(await isAutoApproved('git -C /path --no-pager status')); + ok(await isAutoApproved('git --no-pager -C /path log')); + ok(await isAutoApproved('git -C /path1 -C /path2 status')); + ok(await isAutoApproved('git --no-pager --no-pager log')); + + // Branch deletion should be denied + ok(!await isAutoApproved('git branch -d feature')); + ok(!await isAutoApproved('git branch -D feature')); + ok(!await isAutoApproved('git branch --delete feature')); + ok(!await isAutoApproved('git -C /path branch -d feature')); + ok(!await isAutoApproved('git --no-pager branch -D feature')); + ok(!await isAutoApproved('git -C /path --no-pager branch --force')); + + // Branch rename should be denied + ok(!await isAutoApproved('git branch -m old new')); + ok(!await isAutoApproved('git branch -M old new')); + ok(!await isAutoApproved('git -C /path branch -m old new')); + }); + suite('flags', () => { test('should handle case-insensitive regex patterns with i flag', async () => { setAutoApprove({ From 5d4b6d4d0d7091874fa6ce8feda3b48f03d91f24 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 3 Jan 2026 10:06:16 -0800 Subject: [PATCH 2/2] Allow git ls-files Fixes #285744 --- .../common/terminalChatAgentToolsConfiguration.ts | 1 + .../test/browser/commandLineAutoApprover.test.ts | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts index 6cb7a7d196b..65bc74ad18d 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/common/terminalChatAgentToolsConfiguration.ts @@ -202,6 +202,7 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary { '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+log\\b/': true, '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+show\\b/': true, '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+diff\\b/': true, + '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+ls-files\\b/': true, '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+grep\\b/': true, '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+branch\\b/': true, '/^git(\\s+(-C\\s+\\S+|--no-pager))*\\s+branch\\b.*-(d|D|m|M|-delete|-force)\\b/': false, @@ -218,9 +219,15 @@ suite('CommandLineAutoApprover', () => { ok(await isAutoApproved('git log')); ok(await isAutoApproved('git show HEAD')); ok(await isAutoApproved('git diff')); + ok(await isAutoApproved('git ls-files')); ok(await isAutoApproved('git grep pattern')); ok(await isAutoApproved('git branch')); + // ls-files with options + ok(await isAutoApproved('git ls-files --cached')); + ok(await isAutoApproved('git -C /path ls-files')); + ok(await isAutoApproved('git --no-pager ls-files')); + // With -C path ok(await isAutoApproved('git -C /some/path status')); ok(await isAutoApproved('git -C ../relative log'));