feat(core): populate sandbox forbidden paths with project ignore file contents (#24038)

This commit is contained in:
Emily Hedlund
2026-04-01 12:27:55 -04:00
committed by GitHub
parent 066da2a1d1
commit 6a8a0d4faa
13 changed files with 309 additions and 79 deletions
+29
View File
@@ -253,6 +253,10 @@ vi.mock('../core/tokenLimits.js', () => ({
vi.mock('../code_assist/codeAssist.js');
vi.mock('../code_assist/experiments/experiments.js');
afterEach(() => {
vi.clearAllMocks();
});
describe('Server Config (config.ts)', () => {
const MODEL = DEFAULT_GEMINI_MODEL;
const SANDBOX: SandboxConfig = createMockSandboxConfig({
@@ -1613,6 +1617,31 @@ describe('Server Config (config.ts)', () => {
expect(config.getSandboxAllowedPaths()).toEqual(['/only/this']);
expect(config.getSandboxNetworkAccess()).toBe(false);
});
it('lazily resolves forbidden paths when first accessed', async () => {
const config = new Config({
...baseParams,
sandbox: { enabled: true, command: 'docker' },
});
const fileService = config.getFileService();
vi.spyOn(fileService, 'getIgnoredPaths').mockResolvedValue([
'/tmp/forbidden',
]);
await config.initialize();
expect(fileService.getIgnoredPaths).not.toHaveBeenCalled();
// Access resolved paths via the internal resolver
const resolved = await (
config as unknown as {
getSandboxForbiddenPaths: () => Promise<string[]>;
}
).getSandboxForbiddenPaths();
expect(fileService.getIgnoredPaths).toHaveBeenCalled();
expect(resolved).toEqual(['/tmp/forbidden']);
});
});
it('should have independent TopicState across instances', () => {
+16
View File
@@ -758,6 +758,7 @@ export class Config implements McpContext, AgentLoopContext {
readonly modelConfigService: ModelConfigService;
private readonly embeddingModel: string;
private readonly sandbox: SandboxConfig | undefined;
private _sandboxForbiddenPaths: string[] | undefined;
private readonly targetDir: string;
private workspaceContext: WorkspaceContext;
private readonly debugMode: boolean;
@@ -997,6 +998,7 @@ export class Config implements McpContext, AgentLoopContext {
this.sandbox,
{
workspace: this.targetDir,
forbiddenPaths: this.getSandboxForbiddenPaths.bind(this),
includeDirectories: this.pendingIncludeDirectories,
policyManager: this._sandboxPolicyManager,
},
@@ -1678,11 +1680,25 @@ export class Config implements McpContext, AgentLoopContext {
return this._geminiClient;
}
private async getSandboxForbiddenPaths(): Promise<string[]> {
if (this._sandboxForbiddenPaths) {
return this._sandboxForbiddenPaths;
}
this._sandboxForbiddenPaths = await this.getFileService().getIgnoredPaths({
respectGitIgnore: false,
respectGeminiIgnore: true,
});
return this._sandboxForbiddenPaths;
}
private refreshSandboxManager(): void {
this._sandboxManager = createSandboxManager(
this.sandbox,
{
workspace: this.targetDir,
forbiddenPaths: this.getSandboxForbiddenPaths.bind(this),
policyManager: this._sandboxPolicyManager,
},
this.getApprovalMode(),