@@ -12,6 +12,7 @@ import { isPowerShell } from './runInTerminalHelpers.js';
12
12
13
13
interface IAutoApproveRule {
14
14
regex : RegExp ;
15
+ regexCaseInsensitive : RegExp ;
15
16
sourceText : string ;
16
17
}
17
18
@@ -65,14 +66,14 @@ export class CommandLineAutoApprover extends Disposable {
65
66
isCommandAutoApproved ( command : string , shell : string , os : OperatingSystem ) : { result : ICommandApprovalResult ; reason : string } {
66
67
// Check the deny list to see if this command requires explicit approval
67
68
for ( const rule of this . _denyListRules ) {
68
- if ( this . _commandMatchesRegex ( rule . regex , command , shell , os ) ) {
69
+ if ( this . _commandMatchesRule ( rule , command , shell , os ) ) {
69
70
return { result : 'denied' , reason : `Command '${ command } ' is denied by deny list rule: ${ rule . sourceText } ` } ;
70
71
}
71
72
}
72
73
73
74
// Check the allow list to see if the command is allowed to run without explicit approval
74
75
for ( const rule of this . _allowListRules ) {
75
- if ( this . _commandMatchesRegex ( rule . regex , command , shell , os ) ) {
76
+ if ( this . _commandMatchesRule ( rule , command , shell , os ) ) {
76
77
return { result : 'approved' , reason : `Command '${ command } ' is approved by allow list rule: ${ rule . sourceText } ` } ;
77
78
}
78
79
}
@@ -123,15 +124,17 @@ export class CommandLineAutoApprover extends Disposable {
123
124
return trimmedCommand ;
124
125
}
125
126
126
- private _commandMatchesRegex ( regex : RegExp , command : string , shell : string , os : OperatingSystem ) : boolean {
127
+ private _commandMatchesRule ( rule : IAutoApproveRule , command : string , shell : string , os : OperatingSystem ) : boolean {
127
128
const actualCommand = this . _removeEnvAssignments ( command , shell , os ) ;
129
+ const isPwsh = isPowerShell ( shell , os ) ;
128
130
129
- if ( regex . test ( actualCommand ) ) {
131
+ // PowerShell is case insensitive regardless of platform
132
+ if ( ( isPwsh ? rule . regexCaseInsensitive : rule . regex ) . test ( actualCommand ) ) {
130
133
return true ;
131
- } else if ( isPowerShell ( shell , os ) && actualCommand . startsWith ( '(' ) ) {
134
+ } else if ( isPwsh && actualCommand . startsWith ( '(' ) ) {
132
135
// Allow ignoring of the leading ( for PowerShell commands as it's a command pattern to
133
136
// operate on the output of a command. For example `(Get-Content README.md) ...`
134
- if ( regex . test ( actualCommand . slice ( 1 ) ) ) {
137
+ if ( rule . regexCaseInsensitive . test ( actualCommand . slice ( 1 ) ) ) {
135
138
return true ;
136
139
}
137
140
}
@@ -160,29 +163,29 @@ export class CommandLineAutoApprover extends Disposable {
160
163
161
164
Object . entries ( config ) . forEach ( ( [ key , value ] ) => {
162
165
if ( typeof value === 'boolean' ) {
163
- const regex = this . _convertAutoApproveEntryToRegex ( key ) ;
166
+ const { regex, regexCaseInsensitive } = this . _convertAutoApproveEntryToRegex ( key ) ;
164
167
// IMPORTANT: Only true and false are used, null entries need to be ignored
165
168
if ( value === true ) {
166
- allowListRules . push ( { regex, sourceText : key } ) ;
169
+ allowListRules . push ( { regex, regexCaseInsensitive , sourceText : key } ) ;
167
170
} else if ( value === false ) {
168
- denyListRules . push ( { regex, sourceText : key } ) ;
171
+ denyListRules . push ( { regex, regexCaseInsensitive , sourceText : key } ) ;
169
172
}
170
173
} else if ( typeof value === 'object' && value !== null ) {
171
174
// Handle object format like { approve: true/false, matchCommandLine: true/false }
172
175
const objectValue = value as { approve ?: boolean ; matchCommandLine ?: boolean } ;
173
176
if ( typeof objectValue . approve === 'boolean' ) {
174
- const regex = this . _convertAutoApproveEntryToRegex ( key ) ;
177
+ const { regex, regexCaseInsensitive } = this . _convertAutoApproveEntryToRegex ( key ) ;
175
178
if ( objectValue . approve === true ) {
176
179
if ( objectValue . matchCommandLine === true ) {
177
- allowListCommandLineRules . push ( { regex, sourceText : key } ) ;
180
+ allowListCommandLineRules . push ( { regex, regexCaseInsensitive , sourceText : key } ) ;
178
181
} else {
179
- allowListRules . push ( { regex, sourceText : key } ) ;
182
+ allowListRules . push ( { regex, regexCaseInsensitive , sourceText : key } ) ;
180
183
}
181
184
} else if ( objectValue . approve === false ) {
182
185
if ( objectValue . matchCommandLine === true ) {
183
- denyListCommandLineRules . push ( { regex, sourceText : key } ) ;
186
+ denyListCommandLineRules . push ( { regex, regexCaseInsensitive , sourceText : key } ) ;
184
187
} else {
185
- denyListRules . push ( { regex, sourceText : key } ) ;
188
+ denyListRules . push ( { regex, regexCaseInsensitive , sourceText : key } ) ;
186
189
}
187
190
}
188
191
}
@@ -197,7 +200,15 @@ export class CommandLineAutoApprover extends Disposable {
197
200
} ;
198
201
}
199
202
200
- private _convertAutoApproveEntryToRegex ( value : string ) : RegExp {
203
+ private _convertAutoApproveEntryToRegex ( value : string ) : { regex : RegExp ; regexCaseInsensitive : RegExp } {
204
+ const regex = this . _doConvertAutoApproveEntryToRegex ( value ) ;
205
+ if ( regex . flags . includes ( 'i' ) ) {
206
+ return { regex, regexCaseInsensitive : regex } ;
207
+ }
208
+ return { regex, regexCaseInsensitive : new RegExp ( regex . source , regex . flags + 'i' ) } ;
209
+ }
210
+
211
+ private _doConvertAutoApproveEntryToRegex ( value : string ) : RegExp {
201
212
// If it's wrapped in `/`, it's in regex format and should be converted directly
202
213
// Support all standard JavaScript regex flags: d, g, i, m, s, u, v, y
203
214
const regexMatch = value . match ( / ^ \/ (?< pattern > .+ ) \/ (?< flags > [ d g i m s u v y ] * ) $ / ) ;
@@ -213,6 +224,7 @@ export class CommandLineAutoApprover extends Disposable {
213
224
// Allow .* as users expect this would match everything
214
225
if ( regexPattern === '.*' ) {
215
226
return new RegExp ( regexPattern ) ;
227
+
216
228
}
217
229
218
230
try {
0 commit comments