in server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/executeBash.ts [180:289]
public async requiresAcceptance(
params: ExecuteBashParams,
approvedPaths?: Set<string>
): Promise<CommandValidation> {
try {
const args = split(params.command)
if (!args || args.length === 0) {
return { requiresAcceptance: true }
}
// Split commands by operators and process each segment
let currentCmd: string[] = []
const allCommands: string[][] = []
for (const arg of args) {
if (splitOperators.has(arg)) {
if (currentCmd.length > 0) {
allCommands.push(currentCmd)
}
currentCmd = []
} else if (splitOperatorsArray.some(op => arg.includes(op))) {
return { requiresAcceptance: true }
} else {
currentCmd.push(arg)
}
}
if (currentCmd.length > 0) {
allCommands.push(currentCmd)
}
for (const cmdArgs of allCommands) {
if (cmdArgs.length === 0) {
return { requiresAcceptance: true }
}
// For each command, validate arguments for path safety within workspace
for (const arg of cmdArgs) {
if (this.looksLikePath(arg)) {
// Special handling for tilde paths in Unix-like systems
let fullPath: string
if (!IS_WINDOWS_PLATFORM && arg.startsWith('~')) {
// Treat tilde paths as absolute paths (they will be expanded by the shell)
return { requiresAcceptance: true, warning: destructiveCommandWarningMessage }
} else if (!isAbsolute(arg) && params.cwd) {
// If not absolute, resolve using workingDirectory if available
fullPath = join(params.cwd, arg)
} else {
fullPath = arg
}
// Check if the path is already approved
if (approvedPaths && isPathApproved(fullPath, approvedPaths)) {
continue
}
const isInWorkspace = workspaceUtils.isInWorkspace(getWorkspaceFolderPaths(this.lsp), fullPath)
if (!isInWorkspace) {
return { requiresAcceptance: true, warning: outOfWorkspaceWarningmessage }
}
}
}
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:
continue
default:
return { requiresAcceptance: true }
}
}
// Finally, check if the cwd is outside the workspace
if (params.cwd) {
// Check if the cwd is already approved
if (!(approvedPaths && isPathApproved(params.cwd, approvedPaths))) {
const workspaceFolders = getWorkspaceFolderPaths(this.lsp)
// If there are no workspace folders, we can't validate the path
if (!workspaceFolders || workspaceFolders.length === 0) {
return { requiresAcceptance: true, warning: outOfWorkspaceWarningmessage }
}
// Normalize paths for consistent comparison
const normalizedCwd = params.cwd.replace(/\\/g, '/')
const normalizedWorkspaceFolders = workspaceFolders.map(folder => folder.replace(/\\/g, '/'))
// Check if the normalized cwd is in any of the normalized workspace folders
const isInWorkspace = normalizedWorkspaceFolders.some(
folder => normalizedCwd === folder || normalizedCwd.startsWith(folder + '/')
)
if (!isInWorkspace) {
return { requiresAcceptance: true, warning: outOfWorkspaceWarningmessage }
}
}
}
// If we've checked all commands and none required acceptance, we're good
return { requiresAcceptance: false }
} catch (error) {
this.logging.warn(`Error while checking acceptance: ${(error as Error).message}`)
return { requiresAcceptance: true }
}
}