diff --git a/packages/core/src/codewhispererChat/tools/executeBash.ts b/packages/core/src/codewhispererChat/tools/executeBash.ts index 77390ebdb7d..8701b4b99dc 100644 --- a/packages/core/src/codewhispererChat/tools/executeBash.ts +++ b/packages/core/src/codewhispererChat/tools/executeBash.ts @@ -190,41 +190,48 @@ export class ExecuteBash { return { requiresAcceptance: true } } - // For each command, validate arguments for path safety within workspace + const command = cmdArgs[0] + const category = commandCategories.get(command) + let hasOutsideWorkspacePath = false + for (const arg of cmdArgs) { if (this.looksLikePath(arg)) { - // If not absolute, resolve using workingDirectory if available. let fullPath = arg if (!path.isAbsolute(arg) && this.workingDirectory) { fullPath = path.join(this.workingDirectory, arg) } + const workspaceFolders = vscode.workspace.workspaceFolders if (!workspaceFolders || workspaceFolders.length === 0) { - return { requiresAcceptance: true, warning: destructiveCommandWarningMessage } + hasOutsideWorkspacePath = true + break } + const isInWorkspace = workspaceFolders.some((folder) => isInDirectory(folder.uri.fsPath, fullPath) ) if (!isInWorkspace) { - return { requiresAcceptance: true, warning: destructiveCommandWarningMessage } + hasOutsideWorkspacePath = true + break } } } - const command = cmdArgs[0] - const category = commandCategories.get(command) - switch (category) { case CommandCategory.Destructive: return { requiresAcceptance: true, warning: destructiveCommandWarningMessage } case CommandCategory.Mutate: return { requiresAcceptance: true, warning: mutateCommandWarningMessage } case CommandCategory.ReadOnly: + if (hasOutsideWorkspacePath) { + return { requiresAcceptance: true } + } continue default: return { requiresAcceptance: true } } } + return { requiresAcceptance: false } } catch (error) { this.logger.warn(`Error while checking acceptance: ${(error as Error).message}`) diff --git a/packages/core/src/test/codewhispererChat/tools/executeBash.test.ts b/packages/core/src/test/codewhispererChat/tools/executeBash.test.ts index 54dfb3f02a0..fade87bcbfc 100644 --- a/packages/core/src/test/codewhispererChat/tools/executeBash.test.ts +++ b/packages/core/src/test/codewhispererChat/tools/executeBash.test.ts @@ -54,6 +54,16 @@ describe('ExecuteBash Tool', () => { ) }) + it('requiresAcceptance=true without destructive warning for read-only command outside workspace', () => { + const execBash = new ExecuteBash({ + command: 'ls /', + cwd: '/do/not/exist/dir', + }) + const result = execBash.requiresAcceptance() + assert.equal(result.requiresAcceptance, true, 'Should require acceptance due to path outside workspace') + assert.equal(result.warning, undefined, 'Should not show destructive warning for read-only command') + }) + it('set requiresAcceptance=false if it is a read-only command', () => { const execBash = new ExecuteBash({ command: 'cat file.txt' }) const needsAcceptance = execBash.requiresAcceptance().requiresAcceptance