mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-11 02:20:48 -07:00
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
This commit is contained in:
@@ -213,7 +213,7 @@ describe('ShellExecutionService', () => {
|
||||
mockSerializeTerminalToObject.mockReturnValue([]);
|
||||
mockIsBinary.mockReturnValue(false);
|
||||
mockPlatform.mockReturnValue('linux');
|
||||
mockResolveExecutable.mockImplementation(async (exe: string) => exe);
|
||||
mockResolveExecutable.mockImplementation((exe: string) => exe);
|
||||
process.env['PATH'] = '/test/path';
|
||||
mockGetPty.mockResolvedValue({
|
||||
module: { spawn: mockPtySpawn },
|
||||
@@ -2064,7 +2064,7 @@ describe('ShellExecutionService environment variables', () => {
|
||||
sandboxManager: mockSandboxManager,
|
||||
};
|
||||
|
||||
mockResolveExecutable.mockResolvedValue('/bin/bash/resolved');
|
||||
mockResolveExecutable.mockReturnValue('/bin/bash/resolved');
|
||||
const mockChild = new EventEmitter() as unknown as ChildProcess;
|
||||
mockChild.stdout = new EventEmitter() as unknown as Readable;
|
||||
mockChild.stderr = new EventEmitter() as unknown as Readable;
|
||||
|
||||
@@ -414,8 +414,7 @@ export class ShellExecutionService {
|
||||
executable = 'cmd.exe';
|
||||
}
|
||||
|
||||
const resolvedExecutable =
|
||||
(await resolveExecutable(executable)) ?? executable;
|
||||
const resolvedExecutable = resolveExecutable(executable) ?? executable;
|
||||
|
||||
const guardedCommand = ensurePromptvarsDisabled(commandToExecute, shell);
|
||||
const spawnArgs = [...argsPrefix, guardedCommand];
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import os from 'node:os';
|
||||
import { ShellExecutionService } from './shellExecutionService.js';
|
||||
import { NoopSandboxManager } from './sandboxManager.js';
|
||||
|
||||
const isWindows = os.platform() === 'win32';
|
||||
|
||||
/**
|
||||
* Real-shell integration tests that reproduce the regression class from
|
||||
* issue #25859: commands with inline double quotes executed on Windows
|
||||
* lose their quotes when they reach the native executable, because
|
||||
* Windows PowerShell 5.1 mangles embedded " during native-command
|
||||
* argument passing. PowerShell 7 (pwsh.exe) passes arguments correctly.
|
||||
*
|
||||
* These tests exercise the full pipeline end-to-end. They pass when
|
||||
* gemini-cli selects pwsh.exe from PATH; they fail when the pipeline
|
||||
* routes through Windows PowerShell 5.1.
|
||||
*/
|
||||
describe.skipIf(!isWindows)(
|
||||
'ShellExecutionService Windows quoting (real shell)',
|
||||
() => {
|
||||
const baseConfig = {
|
||||
sanitizationConfig: {
|
||||
allowedEnvironmentVariables: [],
|
||||
blockedEnvironmentVariables: [],
|
||||
enableEnvironmentVariableRedaction: false,
|
||||
},
|
||||
sandboxManager: new NoopSandboxManager(),
|
||||
};
|
||||
|
||||
async function runReal(command: string) {
|
||||
const controller = new AbortController();
|
||||
const handle = await ShellExecutionService.execute(
|
||||
command,
|
||||
process.cwd(),
|
||||
() => {},
|
||||
controller.signal,
|
||||
false,
|
||||
baseConfig,
|
||||
);
|
||||
const result = await handle.result;
|
||||
return { result, output: result.output };
|
||||
}
|
||||
|
||||
it('should preserve inline double quotes through node -e', async () => {
|
||||
const { result, output } = await runReal(
|
||||
`node -e 'console.log("preserved")'`,
|
||||
);
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(output).toBe('preserved');
|
||||
});
|
||||
|
||||
it('should preserve double quotes inside JSON output', async () => {
|
||||
const { result, output } = await runReal(
|
||||
`node -e 'console.log(JSON.stringify({ok:"yes"}))'`,
|
||||
);
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(output).toBe('{"ok":"yes"}');
|
||||
});
|
||||
|
||||
it('should handle quoted argument containing a space', async () => {
|
||||
const { result, output } = await runReal(
|
||||
`node -e "console.log('hello world')"`,
|
||||
);
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(output).toBe('hello world');
|
||||
});
|
||||
|
||||
it('should handle a mixed-quote regex literal', async () => {
|
||||
const { result, output } = await runReal(
|
||||
`node -e 'console.log(String("a").match(/"/))'`,
|
||||
);
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(output).toBe('null');
|
||||
});
|
||||
|
||||
it('should pass a literal double-quote byte through to stdout', async () => {
|
||||
const { result, output } = await runReal(`node -e 'console.log("\\"")'`);
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(output).toBe('"');
|
||||
});
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user