Skip to content

Commit 7887c99

Browse files
committed
Always use case insensitivity on pwsh
Fixes microsoft#259765
1 parent 64576c7 commit 7887c99

File tree

2 files changed

+37
-22
lines changed

2 files changed

+37
-22
lines changed

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

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { isPowerShell } from './runInTerminalHelpers.js';
1212

1313
interface IAutoApproveRule {
1414
regex: RegExp;
15+
regexCaseInsensitive: RegExp;
1516
sourceText: string;
1617
}
1718

@@ -65,14 +66,14 @@ export class CommandLineAutoApprover extends Disposable {
6566
isCommandAutoApproved(command: string, shell: string, os: OperatingSystem): { result: ICommandApprovalResult; reason: string } {
6667
// Check the deny list to see if this command requires explicit approval
6768
for (const rule of this._denyListRules) {
68-
if (this._commandMatchesRegex(rule.regex, command, shell, os)) {
69+
if (this._commandMatchesRule(rule, command, shell, os)) {
6970
return { result: 'denied', reason: `Command '${command}' is denied by deny list rule: ${rule.sourceText}` };
7071
}
7172
}
7273

7374
// Check the allow list to see if the command is allowed to run without explicit approval
7475
for (const rule of this._allowListRules) {
75-
if (this._commandMatchesRegex(rule.regex, command, shell, os)) {
76+
if (this._commandMatchesRule(rule, command, shell, os)) {
7677
return { result: 'approved', reason: `Command '${command}' is approved by allow list rule: ${rule.sourceText}` };
7778
}
7879
}
@@ -123,15 +124,16 @@ export class CommandLineAutoApprover extends Disposable {
123124
return trimmedCommand;
124125
}
125126

126-
private _commandMatchesRegex(regex: RegExp, command: string, shell: string, os: OperatingSystem): boolean {
127+
private _commandMatchesRule(rule: IAutoApproveRule, command: string, shell: string, os: OperatingSystem): boolean {
127128
const actualCommand = this._removeEnvAssignments(command, shell, os);
129+
const isPwsh = isPowerShell(shell, os);
128130

129-
if (regex.test(actualCommand)) {
131+
if ((isPwsh ? rule.regexCaseInsensitive : rule.regex).test(actualCommand)) {
130132
return true;
131-
} else if (isPowerShell(shell, os) && actualCommand.startsWith('(')) {
133+
} else if (isPwsh && actualCommand.startsWith('(')) {
132134
// Allow ignoring of the leading ( for PowerShell commands as it's a command pattern to
133135
// operate on the output of a command. For example `(Get-Content README.md) ...`
134-
if (regex.test(actualCommand.slice(1))) {
136+
if (rule.regexCaseInsensitive.test(actualCommand.slice(1))) {
135137
return true;
136138
}
137139
}
@@ -160,29 +162,29 @@ export class CommandLineAutoApprover extends Disposable {
160162

161163
Object.entries(config).forEach(([key, value]) => {
162164
if (typeof value === 'boolean') {
163-
const regex = this._convertAutoApproveEntryToRegex(key);
165+
const { regex, regexCaseInsensitive } = this._convertAutoApproveEntryToRegex(key);
164166
// IMPORTANT: Only true and false are used, null entries need to be ignored
165167
if (value === true) {
166-
allowListRules.push({ regex, sourceText: key });
168+
allowListRules.push({ regex, regexCaseInsensitive, sourceText: key });
167169
} else if (value === false) {
168-
denyListRules.push({ regex, sourceText: key });
170+
denyListRules.push({ regex, regexCaseInsensitive, sourceText: key });
169171
}
170172
} else if (typeof value === 'object' && value !== null) {
171173
// Handle object format like { approve: true/false, matchCommandLine: true/false }
172174
const objectValue = value as { approve?: boolean; matchCommandLine?: boolean };
173175
if (typeof objectValue.approve === 'boolean') {
174-
const regex = this._convertAutoApproveEntryToRegex(key);
176+
const { regex, regexCaseInsensitive } = this._convertAutoApproveEntryToRegex(key);
175177
if (objectValue.approve === true) {
176178
if (objectValue.matchCommandLine === true) {
177-
allowListCommandLineRules.push({ regex, sourceText: key });
179+
allowListCommandLineRules.push({ regex, regexCaseInsensitive, sourceText: key });
178180
} else {
179-
allowListRules.push({ regex, sourceText: key });
181+
allowListRules.push({ regex, regexCaseInsensitive, sourceText: key });
180182
}
181183
} else if (objectValue.approve === false) {
182184
if (objectValue.matchCommandLine === true) {
183-
denyListCommandLineRules.push({ regex, sourceText: key });
185+
denyListCommandLineRules.push({ regex, regexCaseInsensitive, sourceText: key });
184186
} else {
185-
denyListRules.push({ regex, sourceText: key });
187+
denyListRules.push({ regex, regexCaseInsensitive, sourceText: key });
186188
}
187189
}
188190
}
@@ -197,7 +199,15 @@ export class CommandLineAutoApprover extends Disposable {
197199
};
198200
}
199201

200-
private _convertAutoApproveEntryToRegex(value: string): RegExp {
202+
private _convertAutoApproveEntryToRegex(value: string): { regex: RegExp; regexCaseInsensitive: RegExp } {
203+
const regex = this._doConvertAutoApproveEntryToRegex(value);
204+
if (regex.flags.includes('i')) {
205+
return { regex, regexCaseInsensitive: regex };
206+
}
207+
return { regex, regexCaseInsensitive: new RegExp(regex.source, regex.flags + 'i') };
208+
}
209+
210+
private _doConvertAutoApproveEntryToRegex(value: string): RegExp {
201211
// If it's wrapped in `/`, it's in regex format and should be converted directly
202212
// Support all standard JavaScript regex flags: d, g, i, m, s, u, v, y
203213
const regexMatch = value.match(/^\/(?<pattern>.+)\/(?<flags>[dgimsuvy]*)$/);
@@ -213,6 +223,7 @@ export class CommandLineAutoApprover extends Disposable {
213223
// Allow .* as users expect this would match everything
214224
if (regexPattern === '.*') {
215225
return new RegExp(regexPattern);
226+
216227
}
217228

218229
try {

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,28 +96,32 @@ export const terminalChatAgentToolsConfiguration: IStringDictionary<IConfigurati
9696
rm: false,
9797
rmdir: false,
9898
del: false,
99-
'/^Remove-Item\\b/i': false,
99+
'Remove-Item': false,
100100
ri: false,
101101
rd: false,
102102
erase: false,
103103
// Killing processes, dangerous thing to do generally
104104
kill: false,
105-
'/^Stop-Process\\b/i': false,
105+
'Stop-Process': false,
106106
spps: false,
107-
'/^taskkill(\\.exe)?\\b/i': false,
107+
taskkill: false,
108+
'taskkill.exe': false,
108109
// Web requests, prompt injection concerns
109110
curl: false,
110111
wget: false,
111-
'/^(Invoke-(RestMethod|WebRequest)|irm|iwr)\\b/i': false,
112+
'Invoke-RestMethod': false,
113+
'Invoke-WebRequest': false,
114+
'irm': false,
115+
'iwr': false,
112116
// File permissions and ownership, messing with these can cause hard to diagnose issues
113117
chmod: false,
114118
chown: false,
115-
'/^Set-ItemProperty\\b/i': false,
119+
'Set-ItemProperty': false,
116120
'sp': false,
117-
'/^Set-Acl\\b/i': false,
121+
'Set-Acl': false,
118122
// Eval string, can lead to anything else running
119123
eval: false,
120-
'/^Invoke-Expression\\b/i': false,
124+
'Invoke-Expression': false,
121125
iex: false,
122126
},
123127
}

0 commit comments

Comments
 (0)