From 1a973b9a3b691a66f63d4860732ab15f59cc8ec2 Mon Sep 17 00:00:00 2001 From: Christine Betts Date: Mon, 23 Feb 2026 16:46:46 -0500 Subject: [PATCH] Update extension tier --- docs/extensions/reference.md | 6 +-- packages/cli/src/config/extension-manager.ts | 5 -- .../config/policy-engine.integration.test.ts | 18 +++---- packages/core/src/policy/config.test.ts | 48 +++++++++---------- packages/core/src/policy/config.ts | 10 ++-- packages/core/src/policy/toml-loader.test.ts | 6 +-- packages/core/src/policy/toml-loader.ts | 13 ++--- .../core/src/policy/workspace-policy.test.ts | 18 +++---- 8 files changed, 58 insertions(+), 66 deletions(-) diff --git a/docs/extensions/reference.md b/docs/extensions/reference.md index 12b6bb7cbd..71969454bc 100644 --- a/docs/extensions/reference.md +++ b/docs/extensions/reference.md @@ -222,9 +222,9 @@ To add policies, specify the file path in your `gemini-extension.json`: } ``` -Rules contributed by extensions run in the **Extension Tier** (Tier 2). This -tier has higher priority than the default rules but lower priority than -workspace-specific or user-defined policies. +Rules contributed by extensions run in the **User Tier** (Tier 3), alongside +user-defined policies. This tier has higher priority than the default or +workspace-specific rules but lower priority than admin policies. > **Warning:** For security, Gemini CLI ignores any `allow` decisions or `yolo` > mode configurations in extension policies. This ensures that an extension diff --git a/packages/cli/src/config/extension-manager.ts b/packages/cli/src/config/extension-manager.ts index f189eacb68..c88058968a 100644 --- a/packages/cli/src/config/extension-manager.ts +++ b/packages/cli/src/config/extension-manager.ts @@ -810,11 +810,6 @@ Would you like to attempt to install via "git clone" instead?`, return true; }); } - if (checkers) { - for (const checker of checkers) { - checker.source = `Extension (${config.name}): ${checker.source}`; - } - } if (result.errors.length > 0) { for (const error of result.errors) { diff --git a/packages/cli/src/config/policy-engine.integration.test.ts b/packages/cli/src/config/policy-engine.integration.test.ts index 1aab0ceaa1..dbc7f6a415 100644 --- a/packages/cli/src/config/policy-engine.integration.test.ts +++ b/packages/cli/src/config/policy-engine.integration.test.ts @@ -412,25 +412,25 @@ describe('Policy Engine Integration Tests', () => { // Find rules and verify their priorities const blockedToolRule = rules.find((r) => r.toolName === 'blocked-tool'); - expect(blockedToolRule?.priority).toBe(4.4); // Command line exclude + expect(blockedToolRule?.priority).toBe(3.4); // Command line exclude const blockedServerRule = rules.find( (r) => r.toolName === 'blocked-server__*', ); - expect(blockedServerRule?.priority).toBe(4.9); // MCP server exclude + expect(blockedServerRule?.priority).toBe(3.9); // MCP server exclude const specificToolRule = rules.find( (r) => r.toolName === 'specific-tool', ); - expect(specificToolRule?.priority).toBe(4.3); // Command line allow + expect(specificToolRule?.priority).toBe(3.3); // Command line allow const trustedServerRule = rules.find( (r) => r.toolName === 'trusted-server__*', ); - expect(trustedServerRule?.priority).toBe(4.2); // MCP trusted server + expect(trustedServerRule?.priority).toBe(3.2); // MCP trusted server const mcpServerRule = rules.find((r) => r.toolName === 'mcp-server__*'); - expect(mcpServerRule?.priority).toBe(4.1); // MCP allowed server + expect(mcpServerRule?.priority).toBe(3.1); // MCP allowed server const readOnlyToolRule = rules.find((r) => r.toolName === 'glob'); // Priority 70 in default tier → 1.07 (Overriding Plan Mode Deny) @@ -577,16 +577,16 @@ describe('Policy Engine Integration Tests', () => { // Verify each rule has the expected priority const tool3Rule = rules.find((r) => r.toolName === 'tool3'); - expect(tool3Rule?.priority).toBe(4.4); // Excluded tools (user tier) + expect(tool3Rule?.priority).toBe(3.4); // Excluded tools (user tier) const server2Rule = rules.find((r) => r.toolName === 'server2__*'); - expect(server2Rule?.priority).toBe(4.9); // Excluded servers (user tier) + expect(server2Rule?.priority).toBe(3.9); // Excluded servers (user tier) const tool1Rule = rules.find((r) => r.toolName === 'tool1'); - expect(tool1Rule?.priority).toBe(4.3); // Allowed tools (user tier) + expect(tool1Rule?.priority).toBe(3.3); // Allowed tools (user tier) const server1Rule = rules.find((r) => r.toolName === 'server1__*'); - expect(server1Rule?.priority).toBe(4.1); // Allowed servers (user tier) + expect(server1Rule?.priority).toBe(3.1); // Allowed servers (user tier) const globRule = rules.find((r) => r.toolName === 'glob'); // Priority 70 in default tier → 1.07 diff --git a/packages/core/src/policy/config.test.ts b/packages/core/src/policy/config.test.ts index 61a8c9036c..a9fae7a1fa 100644 --- a/packages/core/src/policy/config.test.ts +++ b/packages/core/src/policy/config.test.ts @@ -169,7 +169,7 @@ describe('createPolicyEngineConfig', () => { r.decision === PolicyDecision.ALLOW, ); expect(rule).toBeDefined(); - expect(rule?.priority).toBeCloseTo(4.3, 5); // Command line allow + expect(rule?.priority).toBeCloseTo(3.3, 5); // Command line allow }); it('should deny tools in tools.exclude', async () => { @@ -188,7 +188,7 @@ describe('createPolicyEngineConfig', () => { r.decision === PolicyDecision.DENY, ); expect(rule).toBeDefined(); - expect(rule?.priority).toBeCloseTo(4.4, 5); // Command line exclude + expect(rule?.priority).toBeCloseTo(3.4, 5); // Command line exclude }); it('should allow tools from allowed MCP servers', async () => { @@ -206,7 +206,7 @@ describe('createPolicyEngineConfig', () => { r.toolName === 'my-server__*' && r.decision === PolicyDecision.ALLOW, ); expect(rule).toBeDefined(); - expect(rule?.priority).toBe(4.1); // MCP allowed server + expect(rule?.priority).toBe(3.1); // MCP allowed server }); it('should deny tools from excluded MCP servers', async () => { @@ -224,7 +224,7 @@ describe('createPolicyEngineConfig', () => { r.toolName === 'my-server__*' && r.decision === PolicyDecision.DENY, ); expect(rule).toBeDefined(); - expect(rule?.priority).toBe(4.9); // MCP excluded server + expect(rule?.priority).toBe(3.9); // MCP excluded server }); it('should allow tools from trusted MCP servers', async () => { @@ -251,7 +251,7 @@ describe('createPolicyEngineConfig', () => { r.decision === PolicyDecision.ALLOW, ); expect(trustedRule).toBeDefined(); - expect(trustedRule?.priority).toBe(4.2); // MCP trusted server + expect(trustedRule?.priority).toBe(3.2); // MCP trusted server // Untrusted server should not have an allow rule const untrustedRule = config.rules?.find( @@ -288,7 +288,7 @@ describe('createPolicyEngineConfig', () => { r.decision === PolicyDecision.ALLOW, ); expect(allowedRule).toBeDefined(); - expect(allowedRule?.priority).toBe(4.1); // MCP allowed server + expect(allowedRule?.priority).toBe(3.1); // MCP allowed server // Check trusted server const trustedRule = config.rules?.find( @@ -297,7 +297,7 @@ describe('createPolicyEngineConfig', () => { r.decision === PolicyDecision.ALLOW, ); expect(trustedRule).toBeDefined(); - expect(trustedRule?.priority).toBe(4.2); // MCP trusted server + expect(trustedRule?.priority).toBe(3.2); // MCP trusted server // Check excluded server const excludedRule = config.rules?.find( @@ -306,7 +306,7 @@ describe('createPolicyEngineConfig', () => { r.decision === PolicyDecision.DENY, ); expect(excludedRule).toBeDefined(); - expect(excludedRule?.priority).toBe(4.9); // MCP excluded server + expect(excludedRule?.priority).toBe(3.9); // MCP excluded server }); it('should allow all tools in YOLO mode', async () => { @@ -387,11 +387,11 @@ describe('createPolicyEngineConfig', () => { ); expect(serverDenyRule).toBeDefined(); - expect(serverDenyRule?.priority).toBe(4.9); // MCP excluded server + expect(serverDenyRule?.priority).toBe(3.9); // MCP excluded server expect(toolAllowRule).toBeDefined(); - expect(toolAllowRule?.priority).toBeCloseTo(4.3, 5); // Command line allow + expect(toolAllowRule?.priority).toBeCloseTo(3.3, 5); // Command line allow - // Server deny (4.9) has higher priority than tool allow (4.3), + // Server deny (3.9) has higher priority than tool allow (3.3), // so server deny wins (this is expected behavior - server-level blocks are security critical) }); @@ -424,7 +424,7 @@ describe('createPolicyEngineConfig', () => { expect(serverAllowRule).toBeDefined(); expect(toolDenyRule).toBeDefined(); - // Command line exclude (4.4) has higher priority than MCP server trust (4.2) + // Command line exclude (3.4) has higher priority than MCP server trust (3.2) // This is the correct behavior - specific exclusions should beat general server trust expect(toolDenyRule!.priority).toBeGreaterThan(serverAllowRule!.priority!); }); @@ -432,16 +432,16 @@ describe('createPolicyEngineConfig', () => { it('should handle complex priority scenarios correctly', async () => { const settings: PolicySettings = { tools: { - allowed: ['my-server__tool1', 'other-tool'], // Priority 4.3 - exclude: ['my-server__tool2', 'glob'], // Priority 4.4 + allowed: ['my-server__tool1', 'other-tool'], // Priority 3.3 + exclude: ['my-server__tool2', 'glob'], // Priority 3.4 }, mcp: { - allowed: ['allowed-server'], // Priority 4.1 - excluded: ['excluded-server'], // Priority 4.9 + allowed: ['allowed-server'], // Priority 3.1 + excluded: ['excluded-server'], // Priority 3.9 }, mcpServers: { 'trusted-server': { - trust: true, // Priority 90 -> 4.2 + trust: true, // Priority 90 -> 3.2 }, }, }; @@ -517,7 +517,7 @@ describe('createPolicyEngineConfig', () => { expect(globDenyRule).toBeDefined(); expect(globAllowRule).toBeDefined(); // Deny from settings (user tier) - expect(globDenyRule!.priority).toBeCloseTo(4.4, 5); // Command line exclude + expect(globDenyRule!.priority).toBeCloseTo(3.4, 5); // Command line exclude // Allow from default TOML: 1 + 50/1000 = 1.05 expect(globAllowRule!.priority).toBeCloseTo(1.05, 5); @@ -530,11 +530,11 @@ describe('createPolicyEngineConfig', () => { })) .sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)); - // Check that the highest priority items are the excludes (user tier: 4.4 and 4.9) + // Check that the highest priority items are the excludes (user tier: 3.4 and 3.9) const highestPriorityExcludes = priorities?.filter( (p) => - Math.abs(p.priority! - 4.4) < 0.01 || - Math.abs(p.priority! - 4.9) < 0.01, + Math.abs(p.priority! - 3.4) < 0.01 || + Math.abs(p.priority! - 3.9) < 0.01, ); expect( highestPriorityExcludes?.every((p) => p.decision === PolicyDecision.DENY), @@ -626,7 +626,7 @@ describe('createPolicyEngineConfig', () => { r.toolName === 'dangerous-tool' && r.decision === PolicyDecision.DENY, ); expect(excludeRule).toBeDefined(); - expect(excludeRule?.priority).toBeCloseTo(4.4, 5); // Command line exclude + expect(excludeRule?.priority).toBeCloseTo(3.4, 5); // Command line exclude }); it('should support argsPattern in policy rules', async () => { @@ -733,8 +733,8 @@ priority = 150 r.decision === PolicyDecision.ALLOW, ); expect(rule).toBeDefined(); - // Priority 150 in user tier → 4.150 - expect(rule?.priority).toBeCloseTo(4.15, 5); + // Priority 150 in user tier → 3.150 + expect(rule?.priority).toBeCloseTo(3.15, 5); expect(rule?.argsPattern).toBeInstanceOf(RegExp); expect(rule?.argsPattern?.test('{"command":"git status"}')).toBe(true); expect(rule?.argsPattern?.test('{"command":"git diff"}')).toBe(true); diff --git a/packages/core/src/policy/config.ts b/packages/core/src/policy/config.ts index 50219c154c..d81ceb0928 100644 --- a/packages/core/src/policy/config.ts +++ b/packages/core/src/policy/config.ts @@ -39,15 +39,15 @@ export const DEFAULT_CORE_POLICIES_DIR = path.join(__dirname, 'policies'); // Policy tier constants for priority calculation export const DEFAULT_POLICY_TIER = 1; -export const EXTENSION_POLICY_TIER = 2; -export const WORKSPACE_POLICY_TIER = 3; -export const USER_POLICY_TIER = 4; -export const ADMIN_POLICY_TIER = 5; +export const WORKSPACE_POLICY_TIER = 2; +export const USER_POLICY_TIER = 3; +export const EXTENSION_POLICY_TIER = 3; +export const ADMIN_POLICY_TIER = 4; // Specific priority offsets and derived priorities for dynamic/settings rules. // These are added to the tier base (e.g., USER_POLICY_TIER). -// Workspace tier (3) + high priority (950/1000) = ALWAYS_ALLOW_PRIORITY +// Workspace tier (2) + high priority (950/1000) = ALWAYS_ALLOW_PRIORITY // This ensures user "always allow" selections are high priority // within the workspace tier but still lose to user/admin policies. export const ALWAYS_ALLOW_PRIORITY = WORKSPACE_POLICY_TIER + 0.95; diff --git a/packages/core/src/policy/toml-loader.test.ts b/packages/core/src/policy/toml-loader.test.ts index 2263be9866..e6573426ee 100644 --- a/packages/core/src/policy/toml-loader.test.ts +++ b/packages/core/src/policy/toml-loader.test.ts @@ -234,11 +234,11 @@ modes = ["autoEdit"] expect(result3.rules).toHaveLength(1); expect(result3.rules[0].toolName).toBe('tier3-tool'); expect(result3.rules[0].modes).toEqual(['autoEdit']); - expect(result3.rules[0].source).toBe('Workspace: tier3.toml'); + expect(result3.rules[0].source).toBe('User: tier3.toml'); - const getPolicyTier4 = (_dir: string) => 4; // Tier 4 (User) + const getPolicyTier4 = (_dir: string) => 4; // Tier 4 (Admin) const result4 = await loadPoliciesFromToml([tempDir], getPolicyTier4); - expect(result4.rules[0].source).toBe('User: tier3.toml'); + expect(result4.rules[0].source).toBe('Admin: tier3.toml'); expect(result4.errors).toHaveLength(0); }); diff --git a/packages/core/src/policy/toml-loader.ts b/packages/core/src/policy/toml-loader.ts index 05f3b47147..7be3fe27dc 100644 --- a/packages/core/src/policy/toml-loader.ts +++ b/packages/core/src/policy/toml-loader.ts @@ -106,7 +106,7 @@ export type PolicyFileErrorType = export interface PolicyFileError { filePath: string; fileName: string; - tier: 'default' | 'extension' | 'user' | 'workspace' | 'admin'; + tier: 'default' | 'user' | 'workspace' | 'admin'; ruleIndex?: number; errorType: PolicyFileErrorType; message: string; @@ -171,14 +171,11 @@ export async function readPolicyFiles( /** * Converts a tier number to a human-readable tier name. */ -function getTierName( - tier: number, -): 'default' | 'extension' | 'user' | 'workspace' | 'admin' { +function getTierName(tier: number): 'default' | 'user' | 'workspace' | 'admin' { if (tier === 1) return 'default'; - if (tier === 2) return 'extension'; - if (tier === 3) return 'workspace'; - if (tier === 4) return 'user'; - if (tier === 5) return 'admin'; + if (tier === 2) return 'workspace'; + if (tier === 3) return 'user'; + if (tier === 4) return 'admin'; return 'default'; } diff --git a/packages/core/src/policy/workspace-policy.test.ts b/packages/core/src/policy/workspace-policy.test.ts index 1fc86f74ef..999dae6f0d 100644 --- a/packages/core/src/policy/workspace-policy.test.ts +++ b/packages/core/src/policy/workspace-policy.test.ts @@ -98,21 +98,21 @@ priority = 10 toolName = "test_tool" decision = "deny" priority = 10 -`; // Tier 4 -> 4.010 +`; // Tier 3 -> 3.010 } if (path.includes('workspace.toml')) { return `[[rule]] toolName = "test_tool" decision = "allow" priority = 10 -`; // Tier 3 -> 3.010 +`; // Tier 2 -> 2.010 } if (path.includes('admin.toml')) { return `[[rule]] toolName = "test_tool" decision = "deny" priority = 10 -`; // Tier 5 -> 5.010 +`; // Tier 4 -> 4.010 } return ''; }); @@ -144,9 +144,9 @@ priority = 10 // Check for all 4 rules const defaultRule = rules?.find((r) => r.priority === 1.01); - const workspaceRule = rules?.find((r) => r.priority === 3.01); - const userRule = rules?.find((r) => r.priority === 4.01); - const adminRule = rules?.find((r) => r.priority === 5.01); + const workspaceRule = rules?.find((r) => r.priority === 2.01); + const userRule = rules?.find((r) => r.priority === 3.01); + const adminRule = rules?.find((r) => r.priority === 4.01); expect(defaultRule).toBeDefined(); expect(userRule).toBeDefined(); @@ -224,7 +224,7 @@ priority=10`, expect(rules![0].priority).toBe(1.01); }); - it('should load workspace policies and correctly transform to Tier 3', async () => { + it('should load workspace policies and correctly transform to Tier 2', async () => { const workspacePoliciesDir = '/mock/workspace/policies'; // Mock FS @@ -284,7 +284,7 @@ priority=500`, const rule = config.rules?.find((r) => r.toolName === 'p_tool'); expect(rule).toBeDefined(); - // Workspace Tier (3) + 500/1000 = 3.5 - expect(rule?.priority).toBe(3.5); + // Workspace Tier (2) + 500/1000 = 2.5 + expect(rule?.priority).toBe(2.5); }); });