fix(core): enable global session and persistent approval for web_fetch (#23295)

Co-authored-by: Spencer <spencertang@google.com>
This commit is contained in:
N. Taylor Mullen
2026-03-21 10:32:07 -07:00
committed by GitHub
parent 0df9498674
commit 4a3d9414ef
5 changed files with 35 additions and 19 deletions

View File

@@ -30,7 +30,10 @@ import { type MessageBus } from '../confirmation-bus/message-bus.js';
import { coreEvents } from '../utils/events.js';
import { debugLogger } from '../utils/debugLogger.js';
import { SHELL_TOOL_NAMES } from '../utils/shell-utils.js';
import { SHELL_TOOL_NAME, SENSITIVE_TOOLS } from '../tools/tool-names.js';
import {
SHELL_TOOL_NAME,
TOOLS_REQUIRING_NARROWING,
} from '../tools/tool-names.js';
import { isNodeError } from '../utils/errors.js';
import { MCP_TOOL_PREFIX } from '../tools/mcp-tool.js';
@@ -560,7 +563,7 @@ export function createPolicyUpdater(
: WORKSPACE_POLICY_TIER;
const priority = tier + getAlwaysAllowPriorityFraction() / 1000;
if (SENSITIVE_TOOLS.has(toolName) && !message.commandPrefix) {
if (TOOLS_REQUIRING_NARROWING.has(toolName) && !message.commandPrefix) {
debugLogger.warn(
`Attempted to update policy for sensitive tool '${toolName}' without a commandPrefix. Skipping.`,
);
@@ -600,7 +603,7 @@ export function createPolicyUpdater(
: WORKSPACE_POLICY_TIER;
const priority = tier + getAlwaysAllowPriorityFraction() / 1000;
if (SENSITIVE_TOOLS.has(toolName) && !message.argsPattern) {
if (TOOLS_REQUIRING_NARROWING.has(toolName) && !message.argsPattern) {
debugLogger.warn(
`Attempted to update policy for sensitive tool '${toolName}' without an argsPattern. Skipping.`,
);

View File

@@ -74,6 +74,12 @@ type = "in-process"
name = "allowed-path"
required_context = ["environment"]
[[rule]]
toolName = "web_fetch"
decision = "allow"
priority = 15
modes = ["autoEdit"]
[[rule]]
toolName = "web_fetch"
decision = "ask_user"

View File

@@ -155,14 +155,13 @@ export const LS_TOOL_NAME_LEGACY = 'list_directory'; // Just to be safe if anyth
export const EDIT_TOOL_NAMES = new Set([EDIT_TOOL_NAME, WRITE_FILE_TOOL_NAME]);
/**
* Tools that can access local files or remote resources and should be
* treated with extra caution when updating policies.
* Tools that require mandatory argument narrowing (e.g., file paths, command prefixes)
* when granting persistent or session-wide approval.
*/
export const SENSITIVE_TOOLS = new Set([
export const TOOLS_REQUIRING_NARROWING = new Set([
GLOB_TOOL_NAME,
GREP_TOOL_NAME,
READ_MANY_FILES_TOOL_NAME,
WEB_FETCH_TOOL_NAME,
READ_FILE_TOOL_NAME,
LS_TOOL_NAME,
WRITE_FILE_TOOL_NAME,

View File

@@ -752,6 +752,24 @@ describe('WebFetchTool', () => {
});
});
describe('getPolicyUpdateOptions', () => {
it('should return empty object for any outcome to allow global approval', () => {
const tool = new WebFetchTool(mockConfig, bus);
const invocation = tool.build({ prompt: 'fetch https://example.com' });
expect(
invocation.getPolicyUpdateOptions!(
ToolConfirmationOutcome.ProceedAlways,
),
).toEqual({});
expect(
invocation.getPolicyUpdateOptions!(
ToolConfirmationOutcome.ProceedAlwaysAndSave,
),
).toEqual({});
});
});
describe('Message Bus Integration', () => {
let policyEngine: PolicyEngine;
let messageBus: MessageBus;

View File

@@ -5,16 +5,15 @@
*/
import {
type ToolConfirmationOutcome,
BaseDeclarativeTool,
BaseToolInvocation,
Kind,
type ToolCallConfirmationDetails,
type ToolInvocation,
type ToolResult,
type ToolConfirmationOutcome,
type PolicyUpdateOptions,
} from './tools.js';
import { buildParamArgsPattern } from '../policy/utils.js';
import type { MessageBus } from '../confirmation-bus/message-bus.js';
import { ToolErrorType } from './tool-error.js';
import { getErrorMessage } from '../utils/errors.js';
@@ -509,16 +508,7 @@ ${aggregatedContent}
override getPolicyUpdateOptions(
_outcome: ToolConfirmationOutcome,
): PolicyUpdateOptions | undefined {
if (this.params.url) {
return {
argsPattern: buildParamArgsPattern('url', this.params.url),
};
} else if (this.params.prompt) {
return {
argsPattern: buildParamArgsPattern('prompt', this.params.prompt),
};
}
return undefined;
return {};
}
protected override async getConfirmationDetails(