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
@@ -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 {