mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-12 23:21:27 -07:00
wip maybe remove
This commit is contained in:
@@ -1 +1 @@
|
||||
../CONTRIBUTING.md
|
||||
../CONTRIBUTING.md
|
||||
|
||||
@@ -216,7 +216,7 @@ export async function start_sandbox(
|
||||
// process.argv is [node, script, ...args]
|
||||
// We want to skip the first element (node) when calling spawn(process.execPath, ...)
|
||||
const finalArgv = cliArgs.slice(1);
|
||||
|
||||
|
||||
const child = spawn(process.execPath, finalArgv, {
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
|
||||
@@ -22,9 +22,18 @@ function compileWindowsSandbox() {
|
||||
return;
|
||||
}
|
||||
|
||||
const srcHelperPath = path.resolve(__dirname, '../src/services/scripts/GeminiSandbox.exe');
|
||||
const distHelperPath = path.resolve(__dirname, '../dist/src/services/scripts/GeminiSandbox.exe');
|
||||
const sourcePath = path.resolve(__dirname, '../src/services/scripts/GeminiSandbox.cs');
|
||||
const srcHelperPath = path.resolve(
|
||||
__dirname,
|
||||
'../src/services/scripts/GeminiSandbox.exe',
|
||||
);
|
||||
const distHelperPath = path.resolve(
|
||||
__dirname,
|
||||
'../dist/src/services/scripts/GeminiSandbox.exe',
|
||||
);
|
||||
const sourcePath = path.resolve(
|
||||
__dirname,
|
||||
'../src/services/scripts/GeminiSandbox.cs',
|
||||
);
|
||||
|
||||
if (!fs.existsSync(sourcePath)) {
|
||||
console.error(`Sandbox source not found at ${sourcePath}`);
|
||||
@@ -32,7 +41,7 @@ function compileWindowsSandbox() {
|
||||
}
|
||||
|
||||
// Ensure directories exist
|
||||
[srcHelperPath, distHelperPath].forEach(p => {
|
||||
[srcHelperPath, distHelperPath].forEach((p) => {
|
||||
const dir = path.dirname(p);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
@@ -42,34 +51,52 @@ function compileWindowsSandbox() {
|
||||
// Find csc.exe (C# Compiler) which is built into Windows .NET Framework
|
||||
const systemRoot = process.env['SystemRoot'] || 'C:\\Windows';
|
||||
const cscPaths = [
|
||||
path.join(systemRoot, 'Microsoft.NET', 'Framework64', 'v4.0.30319', 'csc.exe'),
|
||||
path.join(systemRoot, 'Microsoft.NET', 'Framework', 'v4.0.30319', 'csc.exe'),
|
||||
path.join(
|
||||
systemRoot,
|
||||
'Microsoft.NET',
|
||||
'Framework64',
|
||||
'v4.0.30319',
|
||||
'csc.exe',
|
||||
),
|
||||
path.join(
|
||||
systemRoot,
|
||||
'Microsoft.NET',
|
||||
'Framework',
|
||||
'v4.0.30319',
|
||||
'csc.exe',
|
||||
),
|
||||
];
|
||||
|
||||
const csc = cscPaths.find(p => fs.existsSync(p));
|
||||
const csc = cscPaths.find((p) => fs.existsSync(p));
|
||||
|
||||
if (!csc) {
|
||||
console.warn('Windows C# compiler (csc.exe) not found. Native sandboxing will attempt to compile on first run.');
|
||||
console.warn(
|
||||
'Windows C# compiler (csc.exe) not found. Native sandboxing will attempt to compile on first run.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Compiling native Windows sandbox helper...`);
|
||||
// Compile to src
|
||||
let result = spawnSync(csc, [`/out:${srcHelperPath}`, '/optimize', sourcePath], {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
let result = spawnSync(
|
||||
csc,
|
||||
[`/out:${srcHelperPath}`, '/optimize', sourcePath],
|
||||
{
|
||||
stdio: 'inherit',
|
||||
},
|
||||
);
|
||||
|
||||
if (result.status === 0) {
|
||||
console.log('Successfully compiled GeminiSandbox.exe to src');
|
||||
// Copy to dist if dist exists
|
||||
const distDir = path.resolve(__dirname, '../dist');
|
||||
if (fs.existsSync(distDir)) {
|
||||
const distScriptsDir = path.dirname(distHelperPath);
|
||||
if (!fs.existsSync(distScriptsDir)) {
|
||||
fs.mkdirSync(distScriptsDir, { recursive: true });
|
||||
}
|
||||
fs.copyFileSync(srcHelperPath, distHelperPath);
|
||||
console.log('Successfully copied GeminiSandbox.exe to dist');
|
||||
const distScriptsDir = path.dirname(distHelperPath);
|
||||
if (!fs.existsSync(distScriptsDir)) {
|
||||
fs.mkdirSync(distScriptsDir, { recursive: true });
|
||||
}
|
||||
fs.copyFileSync(srcHelperPath, distHelperPath);
|
||||
console.log('Successfully copied GeminiSandbox.exe to dist');
|
||||
}
|
||||
} else {
|
||||
console.error('Failed to compile Windows sandbox helper.');
|
||||
|
||||
@@ -461,7 +461,13 @@ export interface SandboxConfig {
|
||||
enabled: boolean;
|
||||
allowedPaths: string[];
|
||||
networkAccess: boolean;
|
||||
command?: 'docker' | 'podman' | 'sandbox-exec' | 'runsc' | 'lxc' | 'windows-native';
|
||||
command?:
|
||||
| 'docker'
|
||||
| 'podman'
|
||||
| 'sandbox-exec'
|
||||
| 'runsc'
|
||||
| 'lxc'
|
||||
| 'windows-native';
|
||||
image?: string;
|
||||
}
|
||||
|
||||
@@ -472,7 +478,14 @@ export const ConfigSchema = z.object({
|
||||
allowedPaths: z.array(z.string()).default([]),
|
||||
networkAccess: z.boolean().default(false),
|
||||
command: z
|
||||
.enum(['docker', 'podman', 'sandbox-exec', 'runsc', 'lxc', 'windows-native'])
|
||||
.enum([
|
||||
'docker',
|
||||
'podman',
|
||||
'sandbox-exec',
|
||||
'runsc',
|
||||
'lxc',
|
||||
'windows-native',
|
||||
])
|
||||
.optional(),
|
||||
image: z.string().optional(),
|
||||
})
|
||||
|
||||
@@ -46,7 +46,11 @@ export class SandboxedFileSystemService implements FileSystemService {
|
||||
if (code === 0) {
|
||||
resolve(output);
|
||||
} else {
|
||||
reject(new Error(`Sandbox Error: Command failed with exit code ${code}. ${error ? 'Details: ' + error : ''}`));
|
||||
reject(
|
||||
new Error(
|
||||
`Sandbox Error: Command failed with exit code ${code}. ${error ? 'Details: ' + error : ''}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -78,7 +82,11 @@ export class SandboxedFileSystemService implements FileSystemService {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`Sandbox Error: Command failed with exit code ${code}. ${error ? 'Details: ' + error : ''}`));
|
||||
reject(
|
||||
new Error(
|
||||
`Sandbox Error: Command failed with exit code ${code}. ${error ? 'Details: ' + error : ''}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -337,14 +337,17 @@ export class ShellExecutionService {
|
||||
const finalShell = isStrictSandbox ? 'cmd' : shell;
|
||||
let finalArgsPrefix: string[] = [];
|
||||
if (finalShell === 'cmd') {
|
||||
finalArgsPrefix = ['/c'];
|
||||
finalArgsPrefix = ['/c'];
|
||||
} else {
|
||||
finalArgsPrefix = argsPrefix;
|
||||
finalArgsPrefix = argsPrefix;
|
||||
}
|
||||
|
||||
// We still use the original executable logic (e.g. searching for git)
|
||||
// but the guard command formatting is based on the final shell.
|
||||
const guardedCommand = ensurePromptvarsDisabled(commandToExecute, finalShell as ShellType);
|
||||
const guardedCommand = ensurePromptvarsDisabled(
|
||||
commandToExecute,
|
||||
finalShell as ShellType,
|
||||
);
|
||||
const spawnArgs = [...finalArgsPrefix, guardedCommand];
|
||||
const env = {
|
||||
...process.env,
|
||||
@@ -475,8 +478,8 @@ export class ShellExecutionService {
|
||||
|
||||
const isSandboxError = code === 3221225781 || code === -1073741515; // 0xC0000135
|
||||
if (isSandboxError && shellExecutionConfig.sandboxConfig?.enabled) {
|
||||
const sandboxMessage = `\\n[GEMINI_CLI_SANDBOX_ERROR: Command execution was blocked by the native Windows sandbox. This typically means the command attempted an unauthorized network request or file access.]`;
|
||||
combinedOutput += sandboxMessage;
|
||||
const sandboxMessage = `\\n[GEMINI_CLI_SANDBOX_ERROR: Command execution was blocked by the native Windows sandbox. This typically means the command attempted an unauthorized network request or file access.]`;
|
||||
combinedOutput += sandboxMessage;
|
||||
}
|
||||
|
||||
if (state.truncated) {
|
||||
@@ -628,12 +631,15 @@ export class ShellExecutionService {
|
||||
const finalShell = isStrictSandbox ? 'cmd' : shell;
|
||||
let finalArgsPrefix: string[] = [];
|
||||
if (finalShell === 'cmd') {
|
||||
finalArgsPrefix = ['/c'];
|
||||
finalArgsPrefix = ['/c'];
|
||||
} else {
|
||||
finalArgsPrefix = argsPrefix;
|
||||
finalArgsPrefix = argsPrefix;
|
||||
}
|
||||
|
||||
const guardedCommand = ensurePromptvarsDisabled(commandToExecute, finalShell as ShellType);
|
||||
const guardedCommand = ensurePromptvarsDisabled(
|
||||
commandToExecute,
|
||||
finalShell as ShellType,
|
||||
);
|
||||
const args = [...finalArgsPrefix, guardedCommand];
|
||||
|
||||
const env = {
|
||||
@@ -887,10 +893,14 @@ export class ShellExecutionService {
|
||||
// Store exit info for late subscribers (e.g. backgrounding race condition)
|
||||
this.exitedPtyInfo.set(ptyProcess.pid, { exitCode, signal });
|
||||
|
||||
const isSandboxError = exitCode === 3221225781 || exitCode === -1073741515; // 0xC0000135
|
||||
const isSandboxError =
|
||||
exitCode === 3221225781 || exitCode === -1073741515; // 0xC0000135
|
||||
let finalOutput = getFullBufferText(headlessTerminal);
|
||||
if (isSandboxError && shellExecutionConfig.sandboxConfig?.enabled) {
|
||||
finalOutput += `\n[GEMINI_CLI_SANDBOX_ERROR: Command execution was blocked by the native Windows sandbox. This typically means the command attempted an unauthorized network request or file access.]`;
|
||||
if (
|
||||
isSandboxError &&
|
||||
shellExecutionConfig.sandboxConfig?.enabled
|
||||
) {
|
||||
finalOutput += `\n[GEMINI_CLI_SANDBOX_ERROR: Command execution was blocked by the native Windows sandbox. This typically means the command attempted an unauthorized network request or file access.]`;
|
||||
}
|
||||
|
||||
setTimeout(
|
||||
|
||||
@@ -12,35 +12,43 @@ import * as os from 'node:os';
|
||||
describe('WindowsSandboxManager', () => {
|
||||
const manager = new WindowsSandboxManager();
|
||||
|
||||
it.skipIf(os.platform() !== 'win32')('should prepare a GeminiSandbox.exe command', async () => {
|
||||
const req: SandboxRequest = {
|
||||
command: 'whoami',
|
||||
args: ['/groups'],
|
||||
cwd: process.cwd(),
|
||||
env: { TEST_VAR: 'test_value' },
|
||||
config: {
|
||||
networkAccess: false
|
||||
}
|
||||
};
|
||||
it.skipIf(os.platform() !== 'win32')(
|
||||
'should prepare a GeminiSandbox.exe command',
|
||||
async () => {
|
||||
const req: SandboxRequest = {
|
||||
command: 'whoami',
|
||||
args: ['/groups'],
|
||||
cwd: process.cwd(),
|
||||
env: { TEST_VAR: 'test_value' },
|
||||
config: {
|
||||
networkAccess: false,
|
||||
},
|
||||
};
|
||||
|
||||
const result = await manager.prepareCommand(req);
|
||||
const result = await manager.prepareCommand(req);
|
||||
|
||||
expect(result.program).toContain('GeminiSandbox.exe');
|
||||
expect(result.args).toEqual(expect.arrayContaining(['0', process.cwd(), 'whoami', '/groups']));
|
||||
});
|
||||
expect(result.program).toContain('GeminiSandbox.exe');
|
||||
expect(result.args).toEqual(
|
||||
expect.arrayContaining(['0', process.cwd(), 'whoami', '/groups']),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it.skipIf(os.platform() !== 'win32')('should handle networkAccess from config', async () => {
|
||||
const req: SandboxRequest = {
|
||||
command: 'whoami',
|
||||
args: [],
|
||||
cwd: process.cwd(),
|
||||
env: {},
|
||||
config: {
|
||||
networkAccess: true
|
||||
}
|
||||
};
|
||||
it.skipIf(os.platform() !== 'win32')(
|
||||
'should handle networkAccess from config',
|
||||
async () => {
|
||||
const req: SandboxRequest = {
|
||||
command: 'whoami',
|
||||
args: [],
|
||||
cwd: process.cwd(),
|
||||
env: {},
|
||||
config: {
|
||||
networkAccess: true,
|
||||
},
|
||||
};
|
||||
|
||||
const result = await manager.prepareCommand(req);
|
||||
expect(result.args[0]).toBe('1');
|
||||
});
|
||||
const result = await manager.prepareCommand(req);
|
||||
expect(result.args[0]).toBe('1');
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -31,11 +31,7 @@ export class WindowsSandboxManager implements SandboxManager {
|
||||
private initialized = false;
|
||||
|
||||
constructor() {
|
||||
this.helperPath = path.resolve(
|
||||
__dirname,
|
||||
'scripts',
|
||||
'GeminiSandbox.exe',
|
||||
);
|
||||
this.helperPath = path.resolve(__dirname, 'scripts', 'GeminiSandbox.exe');
|
||||
}
|
||||
|
||||
private ensureInitialized(): void {
|
||||
@@ -53,7 +49,9 @@ export class WindowsSandboxManager implements SandboxManager {
|
||||
'csc.exe',
|
||||
);
|
||||
if (fs.existsSync(csc)) {
|
||||
spawnSync(csc, ['/out:' + this.helperPath, sourcePath], { stdio: 'ignore' });
|
||||
spawnSync(csc, ['/out:' + this.helperPath, sourcePath], {
|
||||
stdio: 'ignore',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,7 +105,7 @@ export class WindowsSandboxManager implements SandboxManager {
|
||||
// 2. Construct the helper command
|
||||
// GeminiSandbox.exe <network:0|1> <cwd> <command> [args...]
|
||||
const program = this.helperPath;
|
||||
|
||||
|
||||
// If the command starts with __, it's an internal command for the sandbox helper itself.
|
||||
const args = [
|
||||
req.config?.networkAccess ? '1' : '0',
|
||||
|
||||
Reference in New Issue
Block a user