Sandboxed Tool Authorization
Clawdbot Contributors· validated-in-production
问题
工具授权既要兼顾灵活性,又要保障安全性。静态允许列表无法适配以下各类场景:
- 多环境差异:开发环境(权限宽松)与生产环境(权限严格)
- 智能体角色分化:代码智能体需要文件系统访问权限,消息智能体则不应拥有该权限
- 层级委托机制:子智能体需继承父智能体的权限限制,同时遵循额外约束
- 插件生态体系:外部工具需支持动态接入,无需手动更新允许列表
智能体需要一套具备模式匹配、默认拒绝语义及层级继承能力的策略系统。
方案
基于匹配模式的权限策略(默认拒绝+支持继承)
工具授权通过与编译后的匹配模式(精确匹配、正则表达式、通配符)比对实现,拒绝列表优先级高于允许列表。子Agent继承父Agent的策略并可添加额外限制;基于配置文件的分层策略为常见Agent类型提供预设配置。
核心概念
- 模式匹配:支持精确匹配(如
exec)、通配符匹配(如fs:*)及类正则匹配模式(如*test*)。 - 默认拒绝:允许列表为空时,所有工具均被拒绝;仅显式添加至允许列表的工具会获得授权。
- 拒绝优先:先校验拒绝列表,只要工具匹配拒绝规则,无论允许列表配置如何,都会被阻止。
- 关联工具权限继承:部分工具会隐式授予关联权限(例如,拥有
exec权限则自动允许apply_patch)。 - 层级策略继承:子Agent的策略继承自父Agent,且可叠加额外拒绝规则。
- 配置文件分层策略:预定义配置文件(
minimal、coding、messaging、full)支持快速完成权限配置。
实现示例
type CompiledPattern =
| { kind: "all" } // "*" 匹配所有工具
| { kind: "exact"; value: string } // 精确匹配模式
| { kind: "regex"; value: RegExp }; // 正则匹配模式
function compilePattern(pattern: string): CompiledPattern {
const normalized = normalizeToolName(pattern);
if (!normalized) return { kind: "exact", value: "" };
if (normalized === "*") return { kind: "all" };
if (!normalized.includes("*")) return { kind: "exact", value: normalized };
// 将 "fs:*" 转换为 /^fs:.*$/ 正则表达式
const escaped = normalized.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
return {
kind: "regex",
value: new RegExp(`^${escaped.replaceAll("\\*", ".*")}$`),
};
}
// 判断工具名是否匹配任意给定的编译模式
function matchesAny(name: string, patterns: CompiledPattern[]): boolean {
const normalized = normalizeToolName(name);
for (const pattern of patterns) {
if (pattern.kind === "all") return true;
if (pattern.kind === "exact" && name === pattern.value) return true;
if (pattern.kind === "regex" && pattern.value.test(name)) return true;
}
return false;
}
// 生成工具策略匹配器,校验工具是否符合权限规则
function makeToolPolicyMatcher(policy: ToolPolicy) {
const deny = compilePatterns(policy.deny);
const allow = compilePatterns(policy.allow);
return (name: string) => {
const normalized = normalizeToolName(name);
// 拒绝规则优先级更高
if (matchesAny(normalized, deny)) return false;
// 允许列表为空则默认允许所有工具(默认拒绝逻辑由调用方处理)
if (allow.length === 0) return true;
// 需要显式匹配允许规则
if (matchesAny(normalized, allow)) return true;
// 关联工具权限继承:若允许exec,则自动允许apply_patch
if (normalized === "apply_patch" && matchesAny("exec", allow)) return true;
return false;
};
}
基于配置文件的分层策略
const TOOL_PROFILES: Record<ToolProfileId, ToolProfilePolicy> = {
minimal: {
allow: ["session_status"], // 最小权限集
},
coding: {
allow: [
"group:fs", // 文件系统组:读取、写入、编辑、应用补丁
"group:runtime", // 运行时组:执行命令、进程管理
"group:sessions", // 会话组:会话列表、创建会话等
"group:memory", // 记忆组:记忆检索、获取记忆
"image",
],
},
messaging: {
allow: [
"group:messaging", // 消息工具组
"sessions_list",
"sessions_history",
"sessions_send",
"session_status",
],
},
full: {}, // 空策略表示允许所有工具
};
批量配置工具组
const TOOL_GROUPS: Record<string, string[]> = {
"group:memory": ["memory_search", "memory_get"],
"group:web": ["web_search", "web_fetch"],
"group:fs": ["read", "write", "edit", "apply_patch"],
"group:runtime": ["exec", "process"],
"group:sessions": ["sessions_list", "sessions_history", "sessions_send", "sessions_spawn"],
"group:clawdbot": ["browser", "canvas", "nodes", "cron", "message", "gateway", /* ... */],
};
子Agent策略继承
const DEFAULT_SUBAGENT_TOOL_DENY = [
// 会话管理:由主Agent统一调度
"sessions_list",
"sessions_history",
"sessions_send",
"sessions_spawn",
// 系统管理:子Agent使用存在风险
"gateway",
"agents_list",
// 状态与调度:由主Agent协调处理
"session_status",
"cron",
];
function resolveSubagentToolPolicy(config?: Config): ToolPolicy {
const configured = config?.tools?.subagents?.tools;
const deny = [
...DEFAULT_SUBAGENT_TOOL_DENY, // 基础限制规则
...(configured?.deny ?? []), // 额外添加的限制规则
];
const allow = configured?.allow; // 可选允许列表
return { allow, deny };
}
跨多层级的策略解析
function resolveEffectiveToolPolicy(params: {
config?: Config;
sessionKey?: string;
modelProvider?: string;
modelId?: string;
}) {
const agentId = resolveAgentIdFromSessionKey(params.sessionKey);
const agentConfig = resolveAgentConfig(params.config, agentId);
const globalTools = params.config?.tools;
const agentTools = agentConfig?.tools;
// 获取基于配置文件的分层策略
const profile = agentTools?.profile ?? globalTools?.profile;
// 获取模型供应商专属的覆盖规则
const providerPolicy = resolveProviderToolPolicy({
byProvider: globalTools?.byProvider,
modelProvider: params.modelProvider,
modelId: params.modelId,
});
return {
globalPolicy: pickToolPolicy(globalTools),
agentPolicy: pickToolPolicy(agentTools),
providerPolicy: pickToolPolicy(providerPolicy),
profile,
};
}
如何使用
- 定义工具组:将相关工具(
group:fs、group:runtime)分组,用于批量配置策略规则。 - 选择预设配置模板(Profile):选择一个预定义的配置模板(
minimal、coding、messaging、full)作为基准。 - 添加显式规则:在配置模板基础上叠加允许/拒绝规则,以满足特定需求。
- 配置子Agent限制:为衍生生成的Agent定义额外的拒绝规则。
- 运行时过滤工具:使用策略匹配器在将工具传递给Agent之前,过滤可用工具。
需要规避的陷阱:
- 过于宽泛的匹配模式:诸如
*的通配符模式可能会意外授予过度权限,建议优先使用具体匹配模式。 - 忽视拒绝规则优先级:必须先评估拒绝规则,再处理允许规则;否则允许规则可能会违背安全设计初衷。
- 遗漏关联工具:如果允许使用
exec工具,别忘了apply_patch也应被许可(它属于文件操作类工具)。 - 继承关系混淆:子Agent的策略是在父策略基础上追加限制,而非完全替代父策略。
权衡
优势:
- 灵活的匹配模式:通配符与分组机制可为大规模工具集构建简洁的策略。
- 默认安全机制:默认拒绝语义可防止意外授予权限。
- 分层管控:无需修改父级策略,即可进一步对子Agent进行权限限制。
- 配置文件预设:针对编码、消息类等常见Agent类型提供预配置策略。
- 插件支持:工具组可通过动态发现机制纳入插件类工具。
劣势/注意事项:
- 模式复杂度:类正则表达式模式易造成混淆,模式语法错误可能导致意外授予权限。
- 策略膨胀:拥有不同策略的大量Agent会增加管理与审计的难度。
- 评估顺序风险:必须严格执行“先拒绝后允许”的优先级规则,否则漏洞可能引发安全问题。
- 关联工具歧义:判断哪些工具属于“关联工具”(如
exec→apply_patch)具有主观性,且可能无法覆盖所有场景。
参考文献
关键词:
涵盖Clawdbot项目中的工具策略、PI工具策略、沙箱专用策略三类核心策略文件,以及关联的“出站锁定(无数据泄露通道)”安全模式。
直译:
- Clawdbot tool-policy.ts - 策略决议
- Clawdbot pi-tools.policy.ts - 策略执行
- Clawdbot 沙箱策略 - 沙箱专用策略
- 相关:出站锁定(无数据泄露通道) - 安全模式范畴
来源摘要
正在获取来源并生成中文摘要…
来源: https://github.com/clawdbot/clawdbot/blob/main/src/agents/tool-policy.ts