2025-09-18 13:44:23 -07:00
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright 2025 Google LLC
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
type PolicyEngineConfig,
|
|
|
|
|
PolicyDecision,
|
|
|
|
|
type PolicyRule,
|
|
|
|
|
ApprovalMode,
|
|
|
|
|
// Read-only tools
|
2025-10-19 19:21:47 -04:00
|
|
|
GREP_TOOL_NAME,
|
2025-10-19 20:53:53 -04:00
|
|
|
LS_TOOL_NAME,
|
2025-10-19 19:21:47 -04:00
|
|
|
READ_MANY_FILES_TOOL_NAME,
|
|
|
|
|
READ_FILE_TOOL_NAME,
|
2025-09-18 13:44:23 -07:00
|
|
|
// Write tools
|
2025-10-17 21:07:26 -04:00
|
|
|
SHELL_TOOL_NAME,
|
2025-10-09 00:33:31 -04:00
|
|
|
WRITE_FILE_TOOL_NAME,
|
2025-10-15 22:48:12 -04:00
|
|
|
WEB_FETCH_TOOL_NAME,
|
2025-10-19 20:53:53 -04:00
|
|
|
GLOB_TOOL_NAME,
|
|
|
|
|
EDIT_TOOL_NAME,
|
|
|
|
|
MEMORY_TOOL_NAME,
|
|
|
|
|
WEB_SEARCH_TOOL_NAME,
|
2025-10-21 11:45:33 -07:00
|
|
|
type PolicyEngine,
|
|
|
|
|
type MessageBus,
|
|
|
|
|
MessageBusType,
|
|
|
|
|
type UpdatePolicy,
|
2025-09-18 13:44:23 -07:00
|
|
|
} from '@google/gemini-cli-core';
|
2025-10-21 11:45:33 -07:00
|
|
|
import { type Settings } from './settings.js';
|
2025-09-18 13:44:23 -07:00
|
|
|
|
|
|
|
|
// READ_ONLY_TOOLS is a list of built-in tools that do not modify the user's
|
|
|
|
|
// files or system state.
|
|
|
|
|
const READ_ONLY_TOOLS = new Set([
|
2025-10-19 20:53:53 -04:00
|
|
|
GLOB_TOOL_NAME,
|
2025-10-19 19:21:47 -04:00
|
|
|
GREP_TOOL_NAME,
|
2025-10-19 20:53:53 -04:00
|
|
|
LS_TOOL_NAME,
|
2025-10-19 19:21:47 -04:00
|
|
|
READ_FILE_TOOL_NAME,
|
|
|
|
|
READ_MANY_FILES_TOOL_NAME,
|
2025-10-19 20:53:53 -04:00
|
|
|
WEB_SEARCH_TOOL_NAME,
|
2025-09-18 13:44:23 -07:00
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// WRITE_TOOLS is a list of built-in tools that can modify the user's files or
|
|
|
|
|
// system state. These tools have a shouldConfirmExecute method.
|
|
|
|
|
// We are keeping this here for visibility and to maintain backwards compatibility
|
|
|
|
|
// with the existing tool permissions system. Eventually we'll remove this and
|
|
|
|
|
// any tool that isn't read only will require a confirmation unless altered by
|
|
|
|
|
// config and policy.
|
|
|
|
|
const WRITE_TOOLS = new Set([
|
2025-10-19 20:53:53 -04:00
|
|
|
EDIT_TOOL_NAME,
|
|
|
|
|
MEMORY_TOOL_NAME,
|
2025-10-17 21:07:26 -04:00
|
|
|
SHELL_TOOL_NAME,
|
2025-10-09 00:33:31 -04:00
|
|
|
WRITE_FILE_TOOL_NAME,
|
2025-10-15 22:48:12 -04:00
|
|
|
WEB_FETCH_TOOL_NAME,
|
2025-09-18 13:44:23 -07:00
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
export function createPolicyEngineConfig(
|
|
|
|
|
settings: Settings,
|
|
|
|
|
approvalMode: ApprovalMode,
|
|
|
|
|
): PolicyEngineConfig {
|
|
|
|
|
const rules: PolicyRule[] = [];
|
|
|
|
|
|
|
|
|
|
// Priority system for policy rules:
|
|
|
|
|
// - Higher priority numbers win over lower priority numbers
|
|
|
|
|
// - When multiple rules match, the highest priority rule is applied
|
|
|
|
|
// - Rules are evaluated in order of priority (highest first)
|
|
|
|
|
//
|
|
|
|
|
// Priority levels used in this configuration:
|
|
|
|
|
// 0: Default allow-all (YOLO mode only)
|
|
|
|
|
// 10: Write tools default to ASK_USER
|
|
|
|
|
// 50: Auto-accept read-only tools
|
|
|
|
|
// 85: MCP servers allowed list
|
|
|
|
|
// 90: MCP servers with trust=true
|
|
|
|
|
// 100: Explicitly allowed individual tools
|
|
|
|
|
// 195: Explicitly excluded MCP servers
|
2025-10-21 11:45:33 -07:00
|
|
|
// 199: Tools that the user has selected as "Always Allow" in the interactive UI.
|
2025-09-18 13:44:23 -07:00
|
|
|
// 200: Explicitly excluded individual tools (highest priority)
|
|
|
|
|
|
|
|
|
|
// MCP servers that are explicitly allowed in settings.mcp.allowed
|
|
|
|
|
// Priority: 85 (lower than trusted servers)
|
|
|
|
|
if (settings.mcp?.allowed) {
|
|
|
|
|
for (const serverName of settings.mcp.allowed) {
|
|
|
|
|
rules.push({
|
|
|
|
|
toolName: `${serverName}__*`,
|
|
|
|
|
decision: PolicyDecision.ALLOW,
|
|
|
|
|
priority: 85,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MCP servers that are trusted in the settings.
|
|
|
|
|
// Priority: 90 (higher than general allowed servers but lower than explicit tool allows)
|
|
|
|
|
if (settings.mcpServers) {
|
|
|
|
|
for (const [serverName, serverConfig] of Object.entries(
|
|
|
|
|
settings.mcpServers,
|
|
|
|
|
)) {
|
|
|
|
|
if (serverConfig.trust) {
|
|
|
|
|
// Trust all tools from this MCP server
|
|
|
|
|
// Using pattern matching for MCP tool names which are formatted as "serverName__toolName"
|
|
|
|
|
rules.push({
|
|
|
|
|
toolName: `${serverName}__*`,
|
|
|
|
|
decision: PolicyDecision.ALLOW,
|
|
|
|
|
priority: 90,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Tools that are explicitly allowed in the settings.
|
|
|
|
|
// Priority: 100
|
|
|
|
|
if (settings.tools?.allowed) {
|
|
|
|
|
for (const tool of settings.tools.allowed) {
|
|
|
|
|
rules.push({
|
|
|
|
|
toolName: tool,
|
|
|
|
|
decision: PolicyDecision.ALLOW,
|
|
|
|
|
priority: 100,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Tools that are explicitly excluded in the settings.
|
|
|
|
|
// Priority: 200
|
|
|
|
|
if (settings.tools?.exclude) {
|
|
|
|
|
for (const tool of settings.tools.exclude) {
|
|
|
|
|
rules.push({
|
|
|
|
|
toolName: tool,
|
|
|
|
|
decision: PolicyDecision.DENY,
|
|
|
|
|
priority: 200,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MCP servers that are explicitly excluded in settings.mcp.excluded
|
|
|
|
|
// Priority: 195 (high priority to block servers)
|
|
|
|
|
if (settings.mcp?.excluded) {
|
|
|
|
|
for (const serverName of settings.mcp.excluded) {
|
|
|
|
|
rules.push({
|
|
|
|
|
toolName: `${serverName}__*`,
|
|
|
|
|
decision: PolicyDecision.DENY,
|
|
|
|
|
priority: 195,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-21 11:45:33 -07:00
|
|
|
// Allow all read-only tools.
|
2025-09-18 13:44:23 -07:00
|
|
|
// Priority: 50
|
2025-10-21 11:45:33 -07:00
|
|
|
for (const tool of READ_ONLY_TOOLS) {
|
|
|
|
|
rules.push({
|
|
|
|
|
toolName: tool,
|
|
|
|
|
decision: PolicyDecision.ALLOW,
|
|
|
|
|
priority: 50,
|
|
|
|
|
});
|
2025-09-18 13:44:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only add write tool rules if not in YOLO mode
|
|
|
|
|
// In YOLO mode, the wildcard ALLOW rule handles everything
|
|
|
|
|
if (approvalMode !== ApprovalMode.YOLO) {
|
|
|
|
|
for (const tool of WRITE_TOOLS) {
|
|
|
|
|
rules.push({
|
|
|
|
|
toolName: tool,
|
|
|
|
|
decision: PolicyDecision.ASK_USER,
|
|
|
|
|
priority: 10,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (approvalMode === ApprovalMode.YOLO) {
|
|
|
|
|
rules.push({
|
|
|
|
|
decision: PolicyDecision.ALLOW,
|
|
|
|
|
priority: 0, // Lowest priority - catches everything not explicitly configured
|
|
|
|
|
});
|
|
|
|
|
} else if (approvalMode === ApprovalMode.AUTO_EDIT) {
|
|
|
|
|
rules.push({
|
2025-10-19 20:53:53 -04:00
|
|
|
toolName: EDIT_TOOL_NAME,
|
2025-09-18 13:44:23 -07:00
|
|
|
decision: PolicyDecision.ALLOW,
|
|
|
|
|
priority: 15, // Higher than write tools (10) to override ASK_USER
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
rules,
|
|
|
|
|
defaultDecision: PolicyDecision.ASK_USER,
|
|
|
|
|
};
|
|
|
|
|
}
|
2025-10-21 11:45:33 -07:00
|
|
|
|
|
|
|
|
export function createPolicyUpdater(
|
|
|
|
|
policyEngine: PolicyEngine,
|
|
|
|
|
messageBus: MessageBus,
|
|
|
|
|
) {
|
|
|
|
|
messageBus.subscribe(
|
|
|
|
|
MessageBusType.UPDATE_POLICY,
|
|
|
|
|
(message: UpdatePolicy) => {
|
|
|
|
|
const toolName = message.toolName;
|
|
|
|
|
|
|
|
|
|
policyEngine.addRule({
|
|
|
|
|
toolName,
|
|
|
|
|
decision: PolicyDecision.ALLOW,
|
|
|
|
|
priority: 199, // High priority, but lower than explicit DENY (200)
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|