mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-29 06:25:16 -07:00
feat(core): integrate SandboxManager to sandbox all process-spawning tools (#22231)
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
import type { GeminiClient } from '../core/client.js';
|
||||
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||
import type { ToolRegistry } from '../tools/tool-registry.js';
|
||||
import type { SandboxManager } from '../services/sandboxManager.js';
|
||||
import type { Config } from './config.js';
|
||||
|
||||
/**
|
||||
@@ -28,4 +29,7 @@ export interface AgentLoopContext {
|
||||
|
||||
/** The client used to communicate with the LLM in this context. */
|
||||
readonly geminiClient: GeminiClient;
|
||||
|
||||
/** The service used to prepare commands for sandboxed execution. */
|
||||
readonly sandboxManager: SandboxManager;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,10 @@ import { LocalLiteRtLmClient } from '../core/localLiteRtLmClient.js';
|
||||
import type { HookDefinition, HookEventName } from '../hooks/types.js';
|
||||
import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
|
||||
import { GitService } from '../services/gitService.js';
|
||||
import {
|
||||
createSandboxManager,
|
||||
type SandboxManager,
|
||||
} from '../services/sandboxManager.js';
|
||||
import {
|
||||
initializeTelemetry,
|
||||
DEFAULT_TELEMETRY_TARGET,
|
||||
@@ -510,6 +514,7 @@ export interface ConfigParameters {
|
||||
clientVersion?: string;
|
||||
embeddingModel?: string;
|
||||
sandbox?: SandboxConfig;
|
||||
toolSandboxing?: boolean;
|
||||
targetDir: string;
|
||||
debugMode: boolean;
|
||||
question?: string;
|
||||
@@ -686,6 +691,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
private readonly telemetrySettings: TelemetrySettings;
|
||||
private readonly usageStatisticsEnabled: boolean;
|
||||
private _geminiClient!: GeminiClient;
|
||||
private readonly _sandboxManager: SandboxManager;
|
||||
private baseLlmClient!: BaseLlmClient;
|
||||
private localLiteRtLmClient?: LocalLiteRtLmClient;
|
||||
private modelRouterService: ModelRouterService;
|
||||
@@ -855,7 +861,19 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
this.embeddingModel =
|
||||
params.embeddingModel ?? DEFAULT_GEMINI_EMBEDDING_MODEL;
|
||||
this.fileSystemService = new StandardFileSystemService();
|
||||
this.sandbox = params.sandbox;
|
||||
this.sandbox = params.sandbox
|
||||
? {
|
||||
enabled: params.sandbox.enabled ?? false,
|
||||
allowedPaths: params.sandbox.allowedPaths ?? [],
|
||||
networkAccess: params.sandbox.networkAccess ?? false,
|
||||
command: params.sandbox.command,
|
||||
image: params.sandbox.image,
|
||||
}
|
||||
: {
|
||||
enabled: false,
|
||||
allowedPaths: [],
|
||||
networkAccess: false,
|
||||
};
|
||||
this.targetDir = path.resolve(params.targetDir);
|
||||
this.folderTrust = params.folderTrust ?? false;
|
||||
this.workspaceContext = new WorkspaceContext(this.targetDir, []);
|
||||
@@ -985,6 +1003,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
showColor: params.shellExecutionConfig?.showColor ?? false,
|
||||
pager: params.shellExecutionConfig?.pager ?? 'cat',
|
||||
sanitizationConfig: this.sanitizationConfig,
|
||||
sandboxManager: this.sandboxManager,
|
||||
};
|
||||
this.truncateToolOutputThreshold =
|
||||
params.truncateToolOutputThreshold ??
|
||||
@@ -1102,6 +1121,8 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
}
|
||||
}
|
||||
this._geminiClient = new GeminiClient(this);
|
||||
this._sandboxManager = createSandboxManager(params.toolSandboxing ?? false);
|
||||
this.shellExecutionConfig.sandboxManager = this._sandboxManager;
|
||||
this.modelRouterService = new ModelRouterService(this);
|
||||
|
||||
// HACK: The settings loading logic doesn't currently merge the default
|
||||
@@ -1423,6 +1444,10 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
return this._geminiClient;
|
||||
}
|
||||
|
||||
get sandboxManager(): SandboxManager {
|
||||
return this._sandboxManager;
|
||||
}
|
||||
|
||||
getSessionId(): string {
|
||||
return this.promptId;
|
||||
}
|
||||
@@ -2810,6 +2835,8 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
sanitizationConfig:
|
||||
config.sanitizationConfig ??
|
||||
this.shellExecutionConfig.sanitizationConfig,
|
||||
sandboxManager:
|
||||
config.sandboxManager ?? this.shellExecutionConfig.sandboxManager,
|
||||
};
|
||||
}
|
||||
getScreenReader(): boolean {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { Config } from './config.js';
|
||||
import { NoopSandboxManager } from '../services/sandboxManager.js';
|
||||
|
||||
// Minimal mocks for Config dependencies to allow instantiation
|
||||
vi.mock('../core/client.js');
|
||||
vi.mock('../core/contentGenerator.js');
|
||||
vi.mock('../telemetry/index.js');
|
||||
vi.mock('../core/tokenLimits.js');
|
||||
vi.mock('../services/fileDiscoveryService.js');
|
||||
vi.mock('../services/gitService.js');
|
||||
vi.mock('../services/trackerService.js');
|
||||
vi.mock('../confirmation-bus/message-bus.js', () => ({
|
||||
MessageBus: vi.fn(),
|
||||
}));
|
||||
vi.mock('../policy/policy-engine.js', () => ({
|
||||
PolicyEngine: vi.fn().mockImplementation(() => ({
|
||||
getExcludedTools: vi.fn().mockReturnValue(new Set()),
|
||||
})),
|
||||
}));
|
||||
vi.mock('../skills/skillManager.js', () => ({
|
||||
SkillManager: vi.fn().mockImplementation(() => ({
|
||||
setAdminSettings: vi.fn(),
|
||||
})),
|
||||
}));
|
||||
vi.mock('../agents/registry.js', () => ({
|
||||
AgentRegistry: vi.fn().mockImplementation(() => ({
|
||||
initialize: vi.fn(),
|
||||
})),
|
||||
}));
|
||||
vi.mock('../agents/acknowledgedAgents.js', () => ({
|
||||
AcknowledgedAgentsService: vi.fn(),
|
||||
}));
|
||||
vi.mock('../services/modelConfigService.js', () => ({
|
||||
ModelConfigService: vi.fn(),
|
||||
}));
|
||||
vi.mock('./models.js', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('./models.js')>();
|
||||
return {
|
||||
...actual,
|
||||
isPreviewModel: vi.fn().mockReturnValue(false),
|
||||
resolveModel: vi.fn().mockReturnValue('test-model'),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Sandbox Integration', () => {
|
||||
it('should have a NoopSandboxManager by default in Config', () => {
|
||||
const config = new Config({
|
||||
sessionId: 'test-session',
|
||||
targetDir: '.',
|
||||
model: 'test-model',
|
||||
cwd: '.',
|
||||
debugMode: false,
|
||||
});
|
||||
|
||||
expect(config.sandboxManager).toBeDefined();
|
||||
expect(config.sandboxManager).toBeInstanceOf(NoopSandboxManager);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user