address comments

This commit is contained in:
Christine Betts
2026-02-24 14:25:05 -05:00
parent 29ab667755
commit 9851e0c024
8 changed files with 65 additions and 91 deletions
+8 -16
View File
@@ -136,8 +136,6 @@ The manifest file defines the extension's behavior and configuration.
also be an array of strings to load multiple context files.
- `excludeTools`: An array of tools to block from the model. You can restrict
specific arguments, such as `run_shell_command(rm -rf)`.
- `policies`: An optional path to a policy TOML file relative to the extension
root. See [Policy Engine](#policy-engine) for more information.
- `themes`: An optional list of themes provided by the extension. See
[Themes](../cli/themes.md) for more information.
@@ -209,22 +207,16 @@ agent definition files (`.md`) to an `agents/` directory in your extension root.
### <a id="policy-engine"></a>Policy Engine
Extensions can contribute policy rules and safety checkers to the Gemini CLI
[Policy Engine](../reference/policy-engine.md). These rules are defined in a
TOML file and take effect when the extension is activated.
[Policy Engine](../admin/policy-engine.md). These rules are defined in `.toml`
files and take effect when the extension is activated.
To add policies, specify the file path in your `gemini-extension.json`:
To add policies, create a `policies/` directory in your extension's root and
place your `.toml` policy files inside it. Gemini CLI automatically loads all
`.toml` files from this directory.
```json
{
"name": "my-secure-extension",
"version": "1.0.0",
"policies": "policies.toml"
}
```
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.
Rules contributed by extensions run in the **Workspace Tier** (Tier 2),
alongside workspace-defined policies. This tier has higher priority than the
default rules but lower priority than user or admin policies.
> **Warning:** For security, Gemini CLI ignores any `allow` decisions or `yolo`
> mode configurations in extension policies. This ensures that an extension
+1 -1
View File
@@ -728,7 +728,7 @@ export class Task {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const errorEvent = event as ServerGeminiErrorEvent; // Type assertion
const errorMessage =
errorEvent.value?.error.message ?? 'Unknown error from LLM stream';
errorEvent.value?.error?.message ?? 'Unknown error from LLM stream';
logger.error(
'[Task] Received error event from LLM stream:',
errorMessage,
@@ -5,7 +5,7 @@ to the Gemini CLI Policy Engine.
## Description
The extension uses a `policies.toml` file to define:
The extension uses a `policies/` directory containing `.toml` files to define:
- A rule that requires user confirmation for `rm -rf` commands.
- A rule that denies searching for sensitive files (like `.env`) using `grep`.
@@ -13,8 +13,8 @@ The extension uses a `policies.toml` file to define:
## Structure
- `gemini-extension.json`: The manifest file that points to the policy file.
- `policies.toml`: Contains the policy rules and safety checkers.
- `gemini-extension.json`: The manifest file.
- `policies/`: Contains the `.toml` policy files.
## How to use
@@ -1,6 +1,5 @@
{
"name": "policy-example",
"version": "1.0.0",
"description": "An example extension demonstrating Policy Engine support.",
"policies": "policies.toml"
"description": "An example extension demonstrating Policy Engine support."
}
+51 -64
View File
@@ -51,7 +51,7 @@ import {
applyAdminAllowlist,
getAdminBlockedMcpServersMessage,
CoreToolCallStatus,
EXTENSION_POLICY_TIER,
WORKSPACE_POLICY_TIER,
loadPoliciesFromToml,
PolicyDecision,
ApprovalMode,
@@ -761,74 +761,61 @@ Would you like to attempt to install via "git clone" instead?`,
let rules: PolicyRule[] | undefined;
let checkers: SafetyCheckerRule[] | undefined;
if (config.policies) {
const policyPath = path.join(effectiveExtensionPath, config.policies);
const resolvedPolicyPath = path.resolve(policyPath);
const resolvedExtensionPath = path.resolve(effectiveExtensionPath);
const policyDir = path.join(effectiveExtensionPath, 'policies');
if (fs.existsSync(policyDir)) {
const result = await loadPoliciesFromToml(
[policyDir],
() => WORKSPACE_POLICY_TIER,
);
rules = result.rules;
checkers = result.checkers;
if (!resolvedPolicyPath.startsWith(resolvedExtensionPath)) {
debugLogger.warn(
`[ExtensionManager] Extension "${config.name}" attempted to contribute a policy file outside its directory: "${config.policies}". Ignoring for security.`,
);
} else if (fs.existsSync(policyPath)) {
const result = await loadPoliciesFromToml(
[policyPath],
() => EXTENSION_POLICY_TIER,
);
rules = result.rules;
checkers = result.checkers;
// Prefix source with extension name to avoid collisions
if (rules) {
rules = rules.filter((rule) => {
// Security: Extensions are not allowed to automatically approve tool calls.
// We ignore any rule that is ALLOW.
if (rule.decision === PolicyDecision.ALLOW) {
debugLogger.warn(
`[ExtensionManager] Extension "${config.name}" attempted to contribute an ALLOW rule for tool "${rule.toolName}". Ignoring this rule for security.`,
);
return false;
}
// Security: Extensions are not allowed to contribute YOLO mode rules.
if (rule.modes?.includes(ApprovalMode.YOLO)) {
debugLogger.warn(
`[ExtensionManager] Extension "${config.name}" attempted to contribute a rule for YOLO mode. Ignoring this rule for security.`,
);
return false;
}
rule.source = `Extension (${config.name}): ${rule.source}`;
return true;
});
}
if (checkers) {
checkers = checkers.filter((checker) => {
// Security: Extensions are not allowed to contribute YOLO mode checkers.
if (checker.modes?.includes(ApprovalMode.YOLO)) {
debugLogger.warn(
`[ExtensionManager] Extension "${config.name}" attempted to contribute a safety checker for YOLO mode. Ignoring this checker for security.`,
);
return false;
}
checker.source = `Extension (${config.name}): ${checker.source}`;
return true;
});
}
if (result.errors.length > 0) {
for (const error of result.errors) {
// Prefix source with extension name to avoid collisions
if (rules) {
rules = rules.filter((rule) => {
// Security: Extensions are not allowed to automatically approve tool calls.
// We ignore any rule that is ALLOW.
if (rule.decision === PolicyDecision.ALLOW) {
debugLogger.warn(
`[ExtensionManager] Error loading policies from ${config.name}: ${error.message}`,
`[ExtensionManager] Extension "${config.name}" attempted to contribute an ALLOW rule for tool "${rule.toolName}". Ignoring this rule for security.`,
);
return false;
}
// Security: Extensions are not allowed to contribute YOLO mode rules.
if (rule.modes?.includes(ApprovalMode.YOLO)) {
debugLogger.warn(
`[ExtensionManager] Extension "${config.name}" attempted to contribute a rule for YOLO mode. Ignoring this rule for security.`,
);
return false;
}
rule.source = `Extension (${config.name}): ${rule.source}`;
return true;
});
}
if (checkers) {
checkers = checkers.filter((checker) => {
// Security: Extensions are not allowed to contribute YOLO mode checkers.
if (checker.modes?.includes(ApprovalMode.YOLO)) {
debugLogger.warn(
`[ExtensionManager] Extension "${config.name}" attempted to contribute a safety checker for YOLO mode. Ignoring this checker for security.`,
);
return false;
}
checker.source = `Extension (${config.name}): ${checker.source}`;
return true;
});
}
if (result.errors.length > 0) {
for (const error of result.errors) {
debugLogger.warn(
`[ExtensionManager] Error loading policies from ${config.name}: ${error.message}`,
);
}
} else {
debugLogger.warn(
`[ExtensionManager] Policy file not found for ${config.name}: ${policyPath}`,
);
}
}
-4
View File
@@ -33,10 +33,6 @@ export interface ExtensionConfig {
* These themes will be registered when the extension is activated.
*/
themes?: CustomTheme[];
/**
* Path to a policy TOML file relative to the extension root.
*/
policies?: string;
}
export interface ExtensionUpdateInfo {
+1 -1
View File
@@ -41,7 +41,7 @@ export const DEFAULT_CORE_POLICIES_DIR = path.join(__dirname, 'policies');
export const DEFAULT_POLICY_TIER = 1;
export const WORKSPACE_POLICY_TIER = 2;
export const USER_POLICY_TIER = 3;
export const EXTENSION_POLICY_TIER = 3;
export const EXTENSION_POLICY_TIER = WORKSPACE_POLICY_TIER;
export const ADMIN_POLICY_TIER = 4;
// Specific priority offsets and derived priorities for dynamic/settings rules.