From 691ce222370835d07b5e2b1c4d89db2deb085801 Mon Sep 17 00:00:00 2001 From: Peter Schuster Date: Wed, 22 Jul 2026 20:40:37 +0200 Subject: [PATCH] fix(amazonq): make cmd.exe builtin detection locale-independent resolveWindowsCommand probed 'cmd.exe /c help ' and looked for the English 'not supported by the help utility' marker. That string is localized, so on non-English Windows (e.g. German) every unknown command was treated as a valid builtin and passed validation. Replace the probe with a static set of cmd.exe internal commands and check it before falling back to 'where' for external commands. --- .../agenticChat/tools/executeBash.test.ts | 11 +++ .../agenticChat/tools/executeBash.ts | 74 ++++++++++++++----- 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/executeBash.test.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/executeBash.test.ts index 279c87603e..966bb8372e 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/executeBash.test.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/executeBash.test.ts @@ -62,6 +62,17 @@ describe('ExecuteBash Tool', () => { ) }) + it('pass validation for a cmd.exe builtin without an executable on PATH', async function () { + // Builtins like 'assoc' have no .exe that 'where' can resolve, so they must be + // recognized as internal commands. This is only meaningful on Windows. + if (process.platform !== 'win32') { + this.skip() + return + } + const execBash = new ExecuteBash(features) + await execBash.validate({ command: 'assoc' }) + }) + it('validate and invokes the command', async () => { const execBash = new ExecuteBash(features) diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/executeBash.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/executeBash.ts index 6533cf2cd4..f346ceb211 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/executeBash.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/executeBash.ts @@ -751,8 +751,64 @@ export class ExecuteBash { } } + // cmd.exe internal commands that have no executable on PATH, so 'where' cannot find them. + // Kept as a static set because probing 'cmd.exe /c help ' is locale-dependent: the + // "not supported" marker is localized (e.g. German Windows), which made every unknown + // command pass validation on non-English systems. + private static readonly CMD_BUILTINS = new Set([ + 'assoc', + 'break', + 'call', + 'cd', + 'chdir', + 'cls', + 'color', + 'copy', + 'date', + 'del', + 'dir', + 'dpath', + 'echo', + 'endlocal', + 'erase', + 'exit', + 'for', + 'ftype', + 'goto', + 'if', + 'md', + 'mkdir', + 'mklink', + 'move', + 'path', + 'pause', + 'popd', + 'prompt', + 'pushd', + 'rd', + 'rem', + 'ren', + 'rename', + 'rmdir', + 'set', + 'setlocal', + 'shift', + 'start', + 'time', + 'title', + 'type', + 'ver', + 'verify', + 'vol', + ]) + private static async resolveWindowsCommand(logger: Logging, cmd: string): Promise { - // 1. Check for external command or alias + // 1. Check for built-in command + if (ExecuteBash.CMD_BUILTINS.has(cmd.toLowerCase())) { + return + } + + // 2. Check for external command or alias try { const whereProc = new processUtils.ChildProcess(logger, 'where', [cmd], { collect: true, @@ -768,22 +824,6 @@ export class ExecuteBash { logger.debug(`'where ${cmd}' failed: ${(err as Error).message}`) } - // 2. Check for built-in command - try { - const helpProc = new processUtils.ChildProcess(logger, 'cmd.exe', ['/c', 'help', cmd], { - collect: true, - waitForStreams: true, - }) - const result = await helpProc.run() - const output = result.stdout.trim() - - if (output && !output.includes('This command is not supported by the help utility')) { - return - } - } catch (err) { - logger.debug(`'help ${cmd}' failed: ${(err as Error).message}`) - } - throw new Error(`Command '${cmd}' not found as executable or Windows built-in command`) }