mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-21 10:34:35 -07:00
Implemented unified secrets sanitization and env. redaction options (#15348)
This commit is contained in:
committed by
GitHub
parent
2ac9fe08f7
commit
3b1dbcd42d
@@ -35,6 +35,13 @@ import * as fs from 'node:fs';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import { coreEvents } from '../utils/events.js';
|
||||
import type { EnvironmentSanitizationConfig } from '../services/environmentSanitization.js';
|
||||
|
||||
const EMPTY_CONFIG: EnvironmentSanitizationConfig = {
|
||||
enableEnvironmentVariableRedaction: true,
|
||||
allowedEnvironmentVariables: [],
|
||||
blockedEnvironmentVariables: [],
|
||||
};
|
||||
|
||||
vi.mock('@modelcontextprotocol/sdk/client/stdio.js');
|
||||
vi.mock('@modelcontextprotocol/sdk/client/index.js');
|
||||
@@ -124,7 +131,7 @@ describe('mcp-client', () => {
|
||||
promptRegistry,
|
||||
resourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
);
|
||||
await client.connect();
|
||||
@@ -204,7 +211,7 @@ describe('mcp-client', () => {
|
||||
promptRegistry,
|
||||
resourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
);
|
||||
await client.connect();
|
||||
@@ -255,7 +262,7 @@ describe('mcp-client', () => {
|
||||
promptRegistry,
|
||||
resourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
);
|
||||
await client.connect();
|
||||
@@ -310,7 +317,7 @@ describe('mcp-client', () => {
|
||||
promptRegistry,
|
||||
resourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
);
|
||||
await client.connect();
|
||||
@@ -369,7 +376,7 @@ describe('mcp-client', () => {
|
||||
promptRegistry,
|
||||
resourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
);
|
||||
await client.connect();
|
||||
@@ -442,7 +449,7 @@ describe('mcp-client', () => {
|
||||
promptRegistry,
|
||||
resourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
);
|
||||
await client.connect();
|
||||
@@ -518,7 +525,7 @@ describe('mcp-client', () => {
|
||||
promptRegistry,
|
||||
resourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
);
|
||||
await client.connect();
|
||||
@@ -601,7 +608,7 @@ describe('mcp-client', () => {
|
||||
promptRegistry,
|
||||
resourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
);
|
||||
await client.connect();
|
||||
@@ -681,7 +688,7 @@ describe('mcp-client', () => {
|
||||
mockedPromptRegistry,
|
||||
resourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
);
|
||||
await client.connect();
|
||||
@@ -730,7 +737,7 @@ describe('mcp-client', () => {
|
||||
{} as PromptRegistry,
|
||||
{} as ResourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
);
|
||||
|
||||
@@ -766,7 +773,7 @@ describe('mcp-client', () => {
|
||||
{} as PromptRegistry,
|
||||
{} as ResourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
);
|
||||
|
||||
@@ -821,7 +828,7 @@ describe('mcp-client', () => {
|
||||
{} as PromptRegistry,
|
||||
{} as ResourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
onToolsUpdatedSpy,
|
||||
);
|
||||
@@ -891,7 +898,7 @@ describe('mcp-client', () => {
|
||||
{} as PromptRegistry,
|
||||
{} as ResourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
);
|
||||
|
||||
@@ -961,7 +968,7 @@ describe('mcp-client', () => {
|
||||
{} as PromptRegistry,
|
||||
{} as ResourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
onToolsUpdatedSpy,
|
||||
);
|
||||
@@ -973,7 +980,7 @@ describe('mcp-client', () => {
|
||||
{} as PromptRegistry,
|
||||
{} as ResourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
onToolsUpdatedSpy,
|
||||
);
|
||||
@@ -1055,7 +1062,7 @@ describe('mcp-client', () => {
|
||||
{} as PromptRegistry,
|
||||
{} as ResourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
);
|
||||
|
||||
@@ -1119,7 +1126,7 @@ describe('mcp-client', () => {
|
||||
{} as PromptRegistry,
|
||||
{} as ResourceRegistry,
|
||||
workspaceContext,
|
||||
{} as Config,
|
||||
{ sanitizationConfig: EMPTY_CONFIG } as Config,
|
||||
false,
|
||||
onToolsUpdatedSpy,
|
||||
);
|
||||
@@ -1170,6 +1177,7 @@ describe('mcp-client', () => {
|
||||
httpUrl: 'http://test-server',
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
||||
@@ -1187,6 +1195,7 @@ describe('mcp-client', () => {
|
||||
headers: { Authorization: 'derp' },
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
||||
@@ -1207,6 +1216,7 @@ describe('mcp-client', () => {
|
||||
url: 'http://test-server',
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
||||
expect(transport).toMatchObject({
|
||||
@@ -1223,6 +1233,7 @@ describe('mcp-client', () => {
|
||||
headers: { Authorization: 'derp' },
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
||||
@@ -1242,6 +1253,7 @@ describe('mcp-client', () => {
|
||||
type: 'http',
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
||||
@@ -1259,6 +1271,7 @@ describe('mcp-client', () => {
|
||||
type: 'sse',
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(transport).toBeInstanceOf(SSEClientTransport);
|
||||
@@ -1275,6 +1288,7 @@ describe('mcp-client', () => {
|
||||
url: 'http://test-server',
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
||||
@@ -1293,6 +1307,7 @@ describe('mcp-client', () => {
|
||||
headers: { Authorization: 'Bearer token' },
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
||||
@@ -1313,6 +1328,7 @@ describe('mcp-client', () => {
|
||||
headers: { 'X-API-Key': 'key123' },
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(transport).toBeInstanceOf(SSEClientTransport);
|
||||
@@ -1332,6 +1348,7 @@ describe('mcp-client', () => {
|
||||
url: 'http://test-server-url',
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
// httpUrl should take priority and create HTTP transport
|
||||
@@ -1357,13 +1374,14 @@ describe('mcp-client', () => {
|
||||
cwd: 'test/cwd',
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(mockedTransport).toHaveBeenCalledWith({
|
||||
command: 'test-command',
|
||||
args: ['--foo', 'bar'],
|
||||
cwd: 'test/cwd',
|
||||
env: { ...process.env, FOO: 'bar' },
|
||||
env: expect.objectContaining({ FOO: 'bar' }),
|
||||
stderr: 'pipe',
|
||||
});
|
||||
});
|
||||
@@ -1393,6 +1411,7 @@ describe('mcp-client', () => {
|
||||
},
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
||||
@@ -1425,6 +1444,7 @@ describe('mcp-client', () => {
|
||||
},
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
||||
@@ -1456,6 +1476,7 @@ describe('mcp-client', () => {
|
||||
},
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
||||
@@ -1476,6 +1497,7 @@ describe('mcp-client', () => {
|
||||
},
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(transport).toBeInstanceOf(SSEClientTransport);
|
||||
@@ -1495,6 +1517,7 @@ describe('mcp-client', () => {
|
||||
},
|
||||
},
|
||||
false,
|
||||
EMPTY_CONFIG,
|
||||
),
|
||||
).rejects.toThrow(
|
||||
'URL must be provided in the config for Google Credentials provider',
|
||||
@@ -1656,6 +1679,7 @@ describe('connectToMcpServer with OAuth', () => {
|
||||
{ httpUrl: serverUrl, oauth: { enabled: true } },
|
||||
false,
|
||||
workspaceContext,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(client).toBe(mockedClient);
|
||||
@@ -1700,6 +1724,7 @@ describe('connectToMcpServer with OAuth', () => {
|
||||
{ httpUrl: serverUrl, oauth: { enabled: true } },
|
||||
false,
|
||||
workspaceContext,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(client).toBe(mockedClient);
|
||||
@@ -1754,6 +1779,7 @@ describe('connectToMcpServer - HTTP→SSE fallback', () => {
|
||||
{ url: 'http://test-server', type: 'http' },
|
||||
false,
|
||||
workspaceContext,
|
||||
EMPTY_CONFIG,
|
||||
),
|
||||
).rejects.toThrow('Connection failed');
|
||||
|
||||
@@ -1772,6 +1798,7 @@ describe('connectToMcpServer - HTTP→SSE fallback', () => {
|
||||
{ url: 'http://test-server', type: 'sse' },
|
||||
false,
|
||||
workspaceContext,
|
||||
EMPTY_CONFIG,
|
||||
),
|
||||
).rejects.toThrow('Connection failed');
|
||||
|
||||
@@ -1789,6 +1816,7 @@ describe('connectToMcpServer - HTTP→SSE fallback', () => {
|
||||
{ url: 'http://test-server' },
|
||||
false,
|
||||
workspaceContext,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(client).toBe(mockedClient);
|
||||
@@ -1810,6 +1838,7 @@ describe('connectToMcpServer - HTTP→SSE fallback', () => {
|
||||
{ url: 'http://test-server' },
|
||||
false,
|
||||
workspaceContext,
|
||||
EMPTY_CONFIG,
|
||||
),
|
||||
).rejects.toThrow('Server error');
|
||||
|
||||
@@ -1826,6 +1855,7 @@ describe('connectToMcpServer - HTTP→SSE fallback', () => {
|
||||
{ url: 'http://test-server' },
|
||||
false,
|
||||
workspaceContext,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(client).toBe(mockedClient);
|
||||
@@ -1895,6 +1925,7 @@ describe('connectToMcpServer - OAuth with transport fallback', () => {
|
||||
{ url: 'http://test-server', oauth: { enabled: true } },
|
||||
false,
|
||||
workspaceContext,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(client).toBe(mockedClient);
|
||||
|
||||
@@ -60,6 +60,10 @@ import { debugLogger } from '../utils/debugLogger.js';
|
||||
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||
import { coreEvents } from '../utils/events.js';
|
||||
import type { ResourceRegistry } from '../resources/resource-registry.js';
|
||||
import {
|
||||
sanitizeEnvironment,
|
||||
type EnvironmentSanitizationConfig,
|
||||
} from '../services/environmentSanitization.js';
|
||||
|
||||
export const MCP_DEFAULT_TIMEOUT_MSEC = 10 * 60 * 1000; // default to 10 minutes
|
||||
|
||||
@@ -137,6 +141,7 @@ export class McpClient {
|
||||
this.serverConfig,
|
||||
this.debugMode,
|
||||
this.workspaceContext,
|
||||
this.cliConfig.sanitizationConfig,
|
||||
);
|
||||
|
||||
this.registerNotificationHandlers();
|
||||
@@ -820,6 +825,7 @@ export async function connectAndDiscover(
|
||||
mcpServerConfig,
|
||||
debugMode,
|
||||
workspaceContext,
|
||||
cliConfig.sanitizationConfig,
|
||||
);
|
||||
|
||||
mcpClient.onerror = (error) => {
|
||||
@@ -1329,6 +1335,7 @@ export async function connectToMcpServer(
|
||||
mcpServerConfig: MCPServerConfig,
|
||||
debugMode: boolean,
|
||||
workspaceContext: WorkspaceContext,
|
||||
sanitizationConfig: EnvironmentSanitizationConfig,
|
||||
): Promise<Client> {
|
||||
const mcpClient = new Client(
|
||||
{
|
||||
@@ -1395,6 +1402,7 @@ export async function connectToMcpServer(
|
||||
mcpServerName,
|
||||
mcpServerConfig,
|
||||
debugMode,
|
||||
sanitizationConfig,
|
||||
);
|
||||
try {
|
||||
await mcpClient.connect(transport, {
|
||||
@@ -1711,6 +1719,7 @@ export async function createTransport(
|
||||
mcpServerName: string,
|
||||
mcpServerConfig: MCPServerConfig,
|
||||
debugMode: boolean,
|
||||
sanitizationConfig: EnvironmentSanitizationConfig,
|
||||
): Promise<Transport> {
|
||||
const noUrl = !mcpServerConfig.url && !mcpServerConfig.httpUrl;
|
||||
if (noUrl) {
|
||||
@@ -1782,7 +1791,7 @@ export async function createTransport(
|
||||
command: mcpServerConfig.command,
|
||||
args: mcpServerConfig.args || [],
|
||||
env: {
|
||||
...process.env,
|
||||
...sanitizeEnvironment(process.env, sanitizationConfig),
|
||||
...(mcpServerConfig.env || {}),
|
||||
} as Record<string, string>,
|
||||
cwd: mcpServerConfig.cwd,
|
||||
|
||||
@@ -254,7 +254,13 @@ export class ShellToolInvocation extends BaseToolInvocation<
|
||||
},
|
||||
combinedController.signal,
|
||||
this.config.getEnableInteractiveShell(),
|
||||
{ ...shellExecutionConfig, pager: 'cat' },
|
||||
{
|
||||
...shellExecutionConfig,
|
||||
pager: 'cat',
|
||||
sanitizationConfig:
|
||||
shellExecutionConfig?.sanitizationConfig ??
|
||||
this.config.sanitizationConfig,
|
||||
},
|
||||
);
|
||||
|
||||
if (pid && setPidCallback) {
|
||||
|
||||
Reference in New Issue
Block a user