Skip to content

Commit 1a1074e

Browse files
author
weikiyue
committed
feat_fix command injection mode, bash mode -4508 issue
1 parent e28ef33 commit 1a1074e

2 files changed

Lines changed: 65 additions & 9 deletions

File tree

source/hooks/input/useBashMode.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,10 @@ export function useBashMode() {
341341
if (outputFlushTimer) {
342342
clearTimeout(outputFlushTimer);
343343
}
344-
outputFlushTimer = setTimeout(flushOutputBuffer, OUTPUT_FLUSH_DELAY);
344+
outputFlushTimer = setTimeout(
345+
flushOutputBuffer,
346+
OUTPUT_FLUSH_DELAY,
347+
);
345348
};
346349

347350
child.stdout?.on('data', (data: Buffer) => {
@@ -388,13 +391,14 @@ export function useBashMode() {
388391
);
389392

390393
child.on('error', (error: any) => {
391-
// 常见:Windows 没安装 pwsh 时会触发 ENOENT,这里自动回退。
392394
if (
393395
isWindows &&
394396
error &&
395397
(error.code === 'ENOENT' ||
396398
String(error.message || '').includes('ENOENT'))
397399
) {
400+
settled = true;
401+
safeCleanup();
398402
spawnWithFallback(index + 1);
399403
return;
400404
}

source/mcp/bash.ts

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {exec, spawn} from 'child_process';
1+
import {exec, spawn, spawnSync} from 'child_process';
22
// Type definitions
33
import type {CommandExecutionResult} from './types/bash.types.js';
44
// Utility functions
@@ -151,6 +151,58 @@ export class TerminalCommandService {
151151
}
152152
}
153153

154+
/**
155+
* Select an available local shell on Windows.
156+
* Tries preferred shell first, then falls back to alternatives.
157+
*/
158+
private selectAvailableWindowsShell(
159+
preferred: 'pwsh' | 'powershell' | null,
160+
): {
161+
shell: 'pwsh' | 'powershell' | 'cmd';
162+
isPowerShell: boolean;
163+
} {
164+
const candidates: Array<'pwsh' | 'powershell' | 'cmd'> = [];
165+
if (preferred === 'pwsh') {
166+
candidates.push('pwsh', 'powershell', 'cmd');
167+
} else if (preferred === 'powershell') {
168+
candidates.push('powershell', 'pwsh', 'cmd');
169+
} else {
170+
candidates.push('powershell', 'pwsh', 'cmd');
171+
}
172+
173+
for (const candidate of candidates) {
174+
try {
175+
if (candidate === 'cmd') {
176+
const probe = spawnSync('cmd', ['/c', 'echo'], {
177+
windowsHide: true,
178+
stdio: 'ignore',
179+
});
180+
if (!probe.error) {
181+
return {shell: 'cmd', isPowerShell: false};
182+
}
183+
continue;
184+
}
185+
186+
const probe = spawnSync(
187+
candidate,
188+
['-NoProfile', '-Command', '$PSVersionTable.PSVersion.ToString()'],
189+
{
190+
windowsHide: true,
191+
stdio: 'ignore',
192+
},
193+
);
194+
195+
if (!probe.error) {
196+
return {shell: candidate, isPowerShell: true};
197+
}
198+
} catch {
199+
// Ignore probe errors and continue fallback.
200+
}
201+
}
202+
203+
return {shell: 'cmd', isPowerShell: false};
204+
}
205+
154206
/**
155207
* Execute a terminal command in the working directory
156208
* Supports both local and remote SSH directories
@@ -227,15 +279,15 @@ export class TerminalCommandService {
227279
let shellArgs: string[];
228280

229281
if (isWindows) {
230-
const psType = detectWindowsPowerShell();
231-
if (psType) {
232-
// Use PowerShell (pwsh for 7.x, powershell for 5.x)
233-
shell = psType === 'pwsh' ? 'pwsh' : 'powershell';
282+
const preferredPowerShell = detectWindowsPowerShell();
283+
const selectedShell =
284+
this.selectAvailableWindowsShell(preferredPowerShell);
285+
shell = selectedShell.shell;
286+
287+
if (selectedShell.isPowerShell) {
234288
const utf8WrappedCommand = `& { $OutputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new(); ${command} }`;
235289
shellArgs = ['-NoProfile', '-Command', utf8WrappedCommand];
236290
} else {
237-
// Fallback to cmd if not in PowerShell environment
238-
shell = 'cmd';
239291
const utf8Command = `chcp 65001>nul && ${command}`;
240292
shellArgs = ['/c', utf8Command];
241293
}

0 commit comments

Comments
 (0)